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