diff --git a/server/server/commands.go b/server/server/commands.go index 74a10d0..66d6eda 100644 --- a/server/server/commands.go +++ b/server/server/commands.go @@ -11,7 +11,10 @@ import ( // GetFile sends the file to the client and returns true if it succeeds and false otherwise. func GetFile(c Client, path string) (int, error) { - fileName := sanitizeFilePath(path) + fileName, sanitized := sanitizeFilePath(path) + if sanitized { + return 0, ErrSlashNotAllowed + } stack, ok := c.Stack().(*StringStack) if !ok { @@ -51,17 +54,20 @@ func readFileData(file *os.File) ([]byte, error) { return data, nil } -func sanitizeFilePath(path string) string { +func sanitizeFilePath(path string) (string, bool) { var fileName string + var sanitized bool // 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:] + sanitized = true } else { fileName = path + sanitized = false } - return fileName + return fileName, sanitized } // ListFiles list the files from path and sends them to the connection @@ -90,13 +96,46 @@ func ListFiles(c Client) error { return nil } +// ClearScreen cleans the client's screen by sending clear to the terminal. +func ClearScreen(c Client) error { + // Ansi clear: 1b 5b 48 1b 5b 4a + // clear | hexdump -C + var b = []byte{0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x4a} + _, err := c.Connection().Write(b) + + return err +} + +// ChangeDirectoryCommand changes the directory to the given directory +func ChangeDirectoryCommand(c Client, directory string) error { + var err error + path, sanitized := sanitizeFilePath(directory) + if sanitized { + return ErrSlashNotAllowed + } + + stack, ok := c.Stack().(*StringStack) + if !ok { + return ErrStackCast + } + + if path == ".." { + err = ChangeDirectoryToPrevious(stack) + } else { + err = ChangeDirectory(stack, path) + } + + return err +} + func ShowHelp(c Client) error { var helpText = ` The available commands are: get - Download the requested filename. -ls - List the files in the current directory. -clear - Clear the screen. -exit - Close the connection with the server. +ls - List the files in the current directory. +cd - Changes the directory. +clear - Clear the screen. +exit - Close the connection with the server.c ` _, err := c.Connection().Write([]byte(helpText)) diff --git a/server/server/errors.go b/server/server/errors.go index 74b7473..208173d 100644 --- a/server/server/errors.go +++ b/server/server/errors.go @@ -44,4 +44,5 @@ var ( ErrInvalidDirectoryName = errors.New("names should not contain / character") ErrNotADirectory = errors.New("file name is not a valid directory") ErrAlreadyAtBaseDirectory = errors.New("can't go past beyond root directory") + ErrSlashNotAllowed = errors.New("slash is not allowed in file names") ) diff --git a/server/server/parser.go b/server/server/parser.go index 8420eec..a90c98f 100644 --- a/server/server/parser.go +++ b/server/server/parser.go @@ -25,6 +25,16 @@ func ProcessInput(c Client, text string) error { thisCommand := commands[0] switch thisCommand { + case "cd": + err := checkArgumentsLength(commandsLen, 2) + if err != nil { + return &InputError{thisCommand, err} + } + + err = ChangeDirectoryCommand(c, commands[1]) + if err != nil { + return err + } case "get": err := checkArgumentsLength(commandsLen, 2) if err != nil { @@ -52,11 +62,10 @@ func ProcessInput(c Client, text string) error { return &InputError{thisCommand, err} } - // Ansi clear: 1b 5b 48 1b 5b 4a - // clear | hexdump -C - var b = []byte{0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x4a} - - c.Connection().Write(b) + err = ClearScreen(c) + if err != nil { + return err + } case "help": // Check arguments err := checkArgumentsLength(commandsLen, 1) diff --git a/server/server/path.go b/server/server/path.go index d0ba0f2..ef2ec1d 100644 --- a/server/server/path.go +++ b/server/server/path.go @@ -18,17 +18,17 @@ func containsSlash(dir string) bool { } // PATH is the constant which should contain the fixed path where the simpleFTP server will run -// This will act like a root cage. +// This will act like a root cage. It must end with a forward slash! var BasePath = "/Users/denis/GoglandProjects/golangBook/" // GetPath will return the complete path -func GetPath(stack *StringStack) string { - if stack.IsEmpty() { - return BasePath - } - - return BasePath -} +//func GetPath(stack *StringStack) string { +// if stack.IsEmpty() { +// return BasePath +// } +// +// return BasePath +//} // MakePathFromStringStack gets a StringStack and makes a path. func MakePathFromStringStack(stack *StringStack) string { @@ -56,10 +56,12 @@ func ChangeDirectory(stack *StringStack, directory string) error { path := MakePathFromStringStack(stack) fileInfo, err := os.Stat(path) if err != nil { + stack.Pop() return err } if fileInfo.IsDir() == false { + stack.Pop() return ErrNotADirectory } diff --git a/server/server/path_test.go b/server/server/path_test.go index efa4244..78cc0f1 100644 --- a/server/server/path_test.go +++ b/server/server/path_test.go @@ -7,16 +7,6 @@ import ( "testing" ) -func TestGetPath_StackIsEmpty(t *testing.T) { - st := MakeStringStack(1) - - path := GetPath(st) - - if path != BasePath { - t.Errorf("TestGetPath: Base path is not returned when stack is empty.") - } -} - func TestMakePathFromStringStack(t *testing.T) { st := MakeStringStack(5) @@ -25,7 +15,7 @@ func TestMakePathFromStringStack(t *testing.T) { st.Push("trinity") path := MakePathFromStringStack(st) - expected := fmt.Sprintf("%s/%s/%s/%s/", BasePath, "first", "folder two", "trinity") + expected := fmt.Sprintf("%s%s/%s/%s/", BasePath, "first", "folder two", "trinity") if path != expected { t.Errorf("TestMakePathFromStringStack: Returned an invalid path! Want %s Got %s", expected, path) @@ -77,6 +67,23 @@ func TestChangeDirectory(t *testing.T) { } } +func TestChangeDirectory_InvalidDirectoryIsNotInStack(t *testing.T) { + st := MakeStringStack(1) + err := os.Chdir(BasePath) + if err != nil { + t.Errorf("TestChangeDirectory_InvalidDirectoryIsNotInStack: Step1: %s", err.Error()) + } + + dirName := string(rand.Intn(10000)) + + // ignore no such directory error + ChangeDirectory(st, dirName) + if !st.IsEmpty() && st.Top() == dirName { + t.Errorf("TestChangeDirectory: Stack is corrupted because invalid directory remained in stack") + } + +} + func TestChangeDirectory_InvalidDirectoryName(t *testing.T) { st := MakeStringStack(1) err := ChangeDirectory(st, "some/not/cool/directory/")