Initial commit
This commit is contained in:
commit
9757016561
5 changed files with 223 additions and 0 deletions
27
server/main.go
Normal file
27
server/main.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"log"
|
||||||
|
"github.com/metonimie/simpleFTP/server/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener, err := net.Listen("tcp", "localhost:8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Hello world!")
|
||||||
|
log.Println("Running on:", "localhost", "port", "8080")
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go server.HandleConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
73
server/server/commands.go
Normal file
73
server/server/commands.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"os"
|
||||||
|
"log"
|
||||||
|
"io/ioutil"
|
||||||
|
"bytes"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PATH is the constant which should contain the fixed path where the simpleFTP server will run
|
||||||
|
// This will act like a root cage.
|
||||||
|
const PATH = "/Users/denis/GoglandProjects/golangBook/GoRoutines/"
|
||||||
|
|
||||||
|
// SendFile sends the file to the client and returns true if it succeeds and false otherwise.
|
||||||
|
func SendFile(c net.Conn, path string) (int, error) {
|
||||||
|
var fileName string
|
||||||
|
|
||||||
|
// Make sure the user can't request any files on the system.
|
||||||
|
lastForwardSlash := strings.LastIndex(path, "/")
|
||||||
|
if lastForwardSlash != -1 {
|
||||||
|
// Eliminate the last forward slash i.e ../../asdas will become asdas
|
||||||
|
fileName = path[lastForwardSlash + 1:]
|
||||||
|
} else {
|
||||||
|
fileName = path
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(PATH + fileName)
|
||||||
|
if err != nil {
|
||||||
|
// Open file failed.
|
||||||
|
log.Println(err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer file.Close() // Closing the fd when the function has exited.
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
n, err := c.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// How is this even possible?
|
||||||
|
if n == 0 {
|
||||||
|
log.Println("0 bits written for:", path)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ListFiles list the files from path and sends them to the connection
|
||||||
|
func ListFiles(c net.Conn) error {
|
||||||
|
files, err := ioutil.ReadDir(PATH)
|
||||||
|
if err != nil {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := bytes.NewBufferString("Directory Mode Size LastModified Name\n")
|
||||||
|
for _, f := range files {
|
||||||
|
buffer.WriteString(strconv.FormatBool(f.IsDir()) + " " + string(f.Mode().String()) + " " +
|
||||||
|
strconv.FormatInt(f.Size(), 10) + " " + f.ModTime().String() + " " + string(f.Name()) + " " + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.Write(buffer.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
30
server/server/connection.go
Normal file
30
server/server/connection.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"bufio"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleConnection(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
io.WriteString(c, "Hello and welcome to simple ftp\n")
|
||||||
|
|
||||||
|
log.Println(c.RemoteAddr(), "has connected.")
|
||||||
|
|
||||||
|
// Process input
|
||||||
|
input := bufio.NewScanner(c)
|
||||||
|
for input.Scan() {
|
||||||
|
log.Println(c.RemoteAddr(), ":", input.Text())
|
||||||
|
|
||||||
|
err := ProcessInput(c, input.Text())
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
io.WriteString(c, err.Error()+"\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client has left.
|
||||||
|
log.Println(c.RemoteAddr(), "has disconnected.")
|
||||||
|
}
|
19
server/server/errors.go
Normal file
19
server/server/errors.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// InputError will be raised when the input is not right.
|
||||||
|
type InputError struct {
|
||||||
|
// The operation that caused the error.
|
||||||
|
Op string
|
||||||
|
// The error that occurred during the operation.
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InputError) Error() string { return "Error: " + e.Op + ": " + e.Err.Error() }
|
||||||
|
|
||||||
|
var (
|
||||||
|
InvalidCommand = errors.New("Invalid command.")
|
||||||
|
TooManyArguments = errors.New("Too many arguments.")
|
||||||
|
TooFewArguments = errors.New("Too few arguments.")
|
||||||
|
)
|
74
server/server/parser.go
Normal file
74
server/server/parser.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkArgumentsLength returns an error if length is not equal to expected.
|
||||||
|
func checkArgumentsLength(length int, expected int) error {
|
||||||
|
if length > expected {
|
||||||
|
return TooManyArguments
|
||||||
|
} else if length < expected {
|
||||||
|
return TooFewArguments
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessInput(c net.Conn, text string) error {
|
||||||
|
commands := strings.Fields(text)
|
||||||
|
commandsLen := len(commands)
|
||||||
|
|
||||||
|
// Possibly empty input, just go on.
|
||||||
|
if commandsLen == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch commands[0] {
|
||||||
|
case "get":
|
||||||
|
// Check arguments
|
||||||
|
err := checkArgumentsLength(commandsLen, 2)
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{commands[0], err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the file
|
||||||
|
_, err = SendFile(c, commands[1])
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{"SendFile", err}
|
||||||
|
}
|
||||||
|
case "ls":
|
||||||
|
// Check arguments
|
||||||
|
err := checkArgumentsLength(commandsLen, 1)
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{commands[0], err}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ListFiles(c)
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{commands[0], err}
|
||||||
|
}
|
||||||
|
case "clear":
|
||||||
|
// Check arguments
|
||||||
|
err := checkArgumentsLength(commandsLen, 1)
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{commands[0], err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ansi clear: 1b 5b 48 1b 5b 4a
|
||||||
|
// clear | hexdump -C
|
||||||
|
var b []byte = []byte{0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x4a}
|
||||||
|
c.Write(b)
|
||||||
|
case "exit":
|
||||||
|
err := checkArgumentsLength(commandsLen, 1)
|
||||||
|
if err != nil {
|
||||||
|
return &InputError{commands[0], err}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Close()
|
||||||
|
default:
|
||||||
|
return &InputError{commands[0], InvalidCommand}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue