Implementing change directory command

This commit is contained in:
Denis-Cosmin Nutiu 2017-11-09 22:39:58 +02:00
parent 8158edf88c
commit da7f4cbfd0
5 changed files with 88 additions and 30 deletions

View file

@ -11,7 +11,10 @@ import (
// GetFile sends the file to the client and returns true if it succeeds and false otherwise. // GetFile sends the file to the client and returns true if it succeeds and false otherwise.
func GetFile(c Client, path string) (int, error) { 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) stack, ok := c.Stack().(*StringStack)
if !ok { if !ok {
@ -51,17 +54,20 @@ func readFileData(file *os.File) ([]byte, error) {
return data, nil return data, nil
} }
func sanitizeFilePath(path string) string { func sanitizeFilePath(path string) (string, bool) {
var fileName string var fileName string
var sanitized bool
// Make sure the user can't request any files on the system. // Make sure the user can't request any files on the system.
lastForwardSlash := strings.LastIndex(path, "/") lastForwardSlash := strings.LastIndex(path, "/")
if lastForwardSlash != -1 { if lastForwardSlash != -1 {
// Eliminate the last forward slash i.e ../../asdas will become asdas // Eliminate the last forward slash i.e ../../asdas will become asdas
fileName = path[lastForwardSlash+1:] fileName = path[lastForwardSlash+1:]
sanitized = true
} else { } else {
fileName = path fileName = path
sanitized = false
} }
return fileName return fileName, sanitized
} }
// ListFiles list the files from path and sends them to the connection // ListFiles list the files from path and sends them to the connection
@ -90,13 +96,46 @@ func ListFiles(c Client) error {
return nil 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 { func ShowHelp(c Client) error {
var helpText = ` var helpText = `
The available commands are: The available commands are:
get <filename> - Download the requested filename. get <filename> - Download the requested filename.
ls - List the files in the current directory. ls - List the files in the current directory.
cd - Changes the directory.
clear - Clear the screen. clear - Clear the screen.
exit - Close the connection with the server. exit - Close the connection with the server.c
` `
_, err := c.Connection().Write([]byte(helpText)) _, err := c.Connection().Write([]byte(helpText))

View file

@ -44,4 +44,5 @@ var (
ErrInvalidDirectoryName = errors.New("names should not contain / character") ErrInvalidDirectoryName = errors.New("names should not contain / character")
ErrNotADirectory = errors.New("file name is not a valid directory") ErrNotADirectory = errors.New("file name is not a valid directory")
ErrAlreadyAtBaseDirectory = errors.New("can't go past beyond root directory") ErrAlreadyAtBaseDirectory = errors.New("can't go past beyond root directory")
ErrSlashNotAllowed = errors.New("slash is not allowed in file names")
) )

View file

@ -25,6 +25,16 @@ func ProcessInput(c Client, text string) error {
thisCommand := commands[0] thisCommand := commands[0]
switch thisCommand { 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": case "get":
err := checkArgumentsLength(commandsLen, 2) err := checkArgumentsLength(commandsLen, 2)
if err != nil { if err != nil {
@ -52,11 +62,10 @@ func ProcessInput(c Client, text string) error {
return &InputError{thisCommand, err} return &InputError{thisCommand, err}
} }
// Ansi clear: 1b 5b 48 1b 5b 4a err = ClearScreen(c)
// clear | hexdump -C if err != nil {
var b = []byte{0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x4a} return err
}
c.Connection().Write(b)
case "help": case "help":
// Check arguments // Check arguments
err := checkArgumentsLength(commandsLen, 1) err := checkArgumentsLength(commandsLen, 1)

View file

@ -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 // 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/" var BasePath = "/Users/denis/GoglandProjects/golangBook/"
// GetPath will return the complete path // GetPath will return the complete path
func GetPath(stack *StringStack) string { //func GetPath(stack *StringStack) string {
if stack.IsEmpty() { // if stack.IsEmpty() {
return BasePath // return BasePath
} // }
//
return BasePath // return BasePath
} //}
// MakePathFromStringStack gets a StringStack and makes a path. // MakePathFromStringStack gets a StringStack and makes a path.
func MakePathFromStringStack(stack *StringStack) string { func MakePathFromStringStack(stack *StringStack) string {
@ -56,10 +56,12 @@ func ChangeDirectory(stack *StringStack, directory string) error {
path := MakePathFromStringStack(stack) path := MakePathFromStringStack(stack)
fileInfo, err := os.Stat(path) fileInfo, err := os.Stat(path)
if err != nil { if err != nil {
stack.Pop()
return err return err
} }
if fileInfo.IsDir() == false { if fileInfo.IsDir() == false {
stack.Pop()
return ErrNotADirectory return ErrNotADirectory
} }

View file

@ -7,16 +7,6 @@ import (
"testing" "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) { func TestMakePathFromStringStack(t *testing.T) {
st := MakeStringStack(5) st := MakeStringStack(5)
@ -25,7 +15,7 @@ func TestMakePathFromStringStack(t *testing.T) {
st.Push("trinity") st.Push("trinity")
path := MakePathFromStringStack(st) 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 { if path != expected {
t.Errorf("TestMakePathFromStringStack: Returned an invalid path! Want %s Got %s", expected, path) 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) { func TestChangeDirectory_InvalidDirectoryName(t *testing.T) {
st := MakeStringStack(1) st := MakeStringStack(1)
err := ChangeDirectory(st, "some/not/cool/directory/") err := ChangeDirectory(st, "some/not/cool/directory/")