diff --git a/README.md b/README.md index 8b4b54cb..83efe305 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,7 @@ sftpgo initprovider --help The `initprovider` command is enough for new installations. From now on, the database structure will be automatically checked and updated, if required, at startup. -If you are upgrading from version 0.9.5 or before you have to manually execute the SQL scripts to create the required database structure.Theese script can be found inside the source tree [sql](./sql "sql") directory. The SQL scripts filename is, by convention, the date as `YYYYMMDD` and the suffix `.sql`. You need to apply all the SQL scripts for your database ordered by name, for example `20190828.sql` must be applied before `20191112.sql` and so on. +If you are upgrading from version 0.9.5 or before you have to manually execute the SQL scripts to create the required database structure.These script can be found inside the source tree [sql](./sql "sql") directory. The SQL scripts filename is, by convention, the date as `YYYYMMDD` and the suffix `.sql`. You need to apply all the SQL scripts for your database ordered by name, for example `20190828.sql` must be applied before `20191112.sql` and so on. Example for SQLite: `find sql/sqlite/ -type f -iname '*.sql' -print | sort -n | xargs cat | sqlite3 sftpgo.db`. After applying these scripts your database structure is the same as the one obtained using `initprovider` for new installations, so from now on you don't have to manually upgrade your database anymore. diff --git a/sftpd/internal_test.go b/sftpd/internal_test.go index b079bd22..b883cf02 100644 --- a/sftpd/internal_test.go +++ b/sftpd/internal_test.go @@ -628,6 +628,25 @@ func TestSSHCommandPath(t *testing.T) { if path != "/" { t.Errorf("unexpected path: %v", path) } + sshCommand.args = []string{"-f", "/a space.txt"} + path = sshCommand.getDestPath() + if path != "/a space.txt" { + t.Errorf("unexpected path: %v", path) + } +} + +func TestSSHParseCommandPayload(t *testing.T) { + cmd := "command -a -f some\\ spaces\\ \\ .txt" + name, args, _ := parseCommandPayload(cmd) + if name != "command" { + t.Errorf("unexpected command: %v", name) + } + if len(args) != 3 { + t.Errorf("unexpected number of arguments %v/3", len(args)) + } + if !utils.IsStringInSlice("some spaces .txt", args) { + t.Errorf("command parsing error, expected arguments not found: %v", args) + } } func TestSSHCommandErrors(t *testing.T) { diff --git a/sftpd/scp.go b/sftpd/scp.go index a8ae7f06..4050f4a1 100644 --- a/sftpd/scp.go +++ b/sftpd/scp.go @@ -56,6 +56,7 @@ func (c *scpCommand) handle() error { } } else { err = fmt.Errorf("scp command not supported, args: %v", c.args) + c.connection.Log(logger.LevelDebug, logSenderSCP, "unsupported scp command, args: %v", c.args) } c.sendExitStatus(err) return err diff --git a/sftpd/ssh_cmd.go b/sftpd/ssh_cmd.go index 1cdc05a1..036ed905 100644 --- a/sftpd/ssh_cmd.go +++ b/sftpd/ssh_cmd.go @@ -45,7 +45,7 @@ type systemCommand struct { func processSSHCommand(payload []byte, connection *Connection, channel ssh.Channel, enabledSSHCommands []string) bool { var msg sshSubsystemExecMsg if err := ssh.Unmarshal(payload, &msg); err == nil { - name, args, err := parseCommandPayload(msg.Command) + name, args, err := parseCommandPayload(strings.TrimSpace(msg.Command)) connection.Log(logger.LevelDebug, logSenderSSH, "new ssh command: %#v args: %v user: %v, error: %v", name, args, connection.User.Username, err) if err == nil && utils.IsStringInSlice(name, enabledSSHCommands) { @@ -421,9 +421,17 @@ func computeHashForFile(hasher hash.Hash, path string) (string, error) { } func parseCommandPayload(command string) (string, []string, error) { - parts := strings.Split(command, " ") + parts := strings.Split(strings.ReplaceAll(command, "\\ ", "\\"), " ") if len(parts) < 2 { return parts[0], []string{}, nil } - return parts[0], parts[1:], nil + args := []string{} + for _, arg := range parts[1:] { + parsed := strings.TrimSpace(strings.ReplaceAll(arg, "\\", " ")) + if len(parsed) == 0 { + continue + } + args = append(args, parsed) + } + return parts[0], args, nil }