Skip to content

Commit

Permalink
fix quoted, space separated, HASH parameters (#459) by @drakkan as su…
Browse files Browse the repository at this point in the history
…ggested by @syncplify
  • Loading branch information
drakkan committed Jun 2, 2024
1 parent 1586bde commit bfb6dd6
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 20 deletions.
55 changes: 35 additions & 20 deletions handle_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,14 @@ func (c *clientHandler) handleGenericHash(param string, algo HASHAlgo, isCustomM
return nil
}

args := strings.SplitN(param, " ", 3)
info, err := c.driver.Stat(args[0])
args, err := unquoteSpaceSeparatedParams(param)
if err != nil || len(args) == 0 {
c.writeMessage(StatusSyntaxErrorParameters, fmt.Sprintf("invalid HASH parameters: %v", param))

return nil //nolint:nilerr
}

info, err := c.driver.Stat(args[0])
if err != nil {
c.writeMessage(StatusActionNotTaken, fmt.Sprintf("%v: %v", param, err))

Expand All @@ -627,25 +632,11 @@ func (c *clientHandler) handleGenericHash(param string, algo HASHAlgo, isCustomM
// to support partial hash also for the HASH command, we should implement RANG,
// but it applies also to uploads/downloads and so it complicates their handling,
// we'll add this support in future improvements
if isCustomMode { //nolint:nestif // too much effort to change for now
// for custom command the range can be specified in this way:
// XSHA1 <file> <start> <end>
if len(args) > 1 {
start, err = strconv.ParseInt(args[1], 10, 64)
if err != nil {
c.writeMessage(StatusSyntaxErrorParameters, fmt.Sprintf("invalid start offset %v: %v", args[1], err))

return nil
}
}

if len(args) > 2 {
end, err = strconv.ParseInt(args[2], 10, 64)
if err != nil {
c.writeMessage(StatusSyntaxErrorParameters, fmt.Sprintf("invalid end offset %v: %v", args[2], err))
if isCustomMode {
if err = getPartialHASHRange(args, &start, &end); err != nil {
c.writeMessage(StatusSyntaxErrorParameters, err.Error())

return nil
}
return nil
}
}

Expand Down Expand Up @@ -747,6 +738,30 @@ func (c *clientHandler) closeUnchecked(file io.Closer) {
}
}

func getPartialHASHRange(args []string, start *int64, end *int64) error {
// for custom HASH commands the range can be specified in this way:
// XSHA1 <file> <start> <end>
if len(args) > 1 {
val, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
return fmt.Errorf("invalid start offset %v: %w", args[1], err)
}

*start = val
}

if len(args) > 2 {
val, err := strconv.ParseInt(args[2], 10, 64)
if err != nil {
return fmt.Errorf("invalid end offset %v: %w", args[1], err)
}

*end = val
}

return nil
}

// This method split params by spaces, except when the space is inside quotes.
// It was introduced to support COMB command. Supported COMB examples:
//
Expand Down
37 changes: 37 additions & 0 deletions handle_files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ func TestHASHCommand(t *testing.T) {
sha256Hash := "ceee704dd96e2b8c2ceca59c4c697bc01123fb9e66a1a3ac34dbdd2d6da9659b"

ftpUpload(t, client, tempFile, "file.txt")
ftpUpload(t, client, tempFile, "file with space.txt")

raw, err := client.OpenRawConn()
require.NoError(t, err, "Couldn't open raw connection")
Expand All @@ -562,6 +563,11 @@ func TestHASHCommand(t *testing.T) {
require.NoError(t, err)
require.Equal(t, StatusFileStatus, returnCode)
require.True(t, strings.HasSuffix(message, fmt.Sprintf("SHA-256 0-36 %v file.txt", sha256Hash)))
// test the same quoting the file name
returnCode, message, err = raw.SendCommand(`HASH "file with space.txt"`)
require.NoError(t, err)
require.Equal(t, StatusFileStatus, returnCode)
require.True(t, strings.HasSuffix(message, fmt.Sprintf("SHA-256 0-36 %v file with space.txt", sha256Hash)))

// change algo and request the hash again
returnCode, message, err = raw.SendCommand("OPTS HASH CRC32")
Expand All @@ -575,6 +581,37 @@ func TestHASHCommand(t *testing.T) {
require.True(t, strings.HasSuffix(message, fmt.Sprintf("CRC32 0-36 %v file.txt", crc32Sum)))
}

func TestHashWithoutParams(t *testing.T) {
server := NewTestServerWithTestDriver(
t,
&TestServerDriver{
Debug: false,
Settings: &Settings{
EnableHASH: true,
},
},
)
conf := goftp.Config{
User: authUser,
Password: authPass,
}

client, err := goftp.DialConfig(conf, server.Addr())
require.NoError(t, err, "Couldn't connect")

defer func() { panicOnError(client.Close()) }()

raw, err := client.OpenRawConn()
require.NoError(t, err, "Couldn't open raw connection")

defer func() { require.NoError(t, raw.Close()) }()

returnCode, message, err := raw.SendCommand("HASH")
require.NoError(t, err)
require.Equal(t, StatusSyntaxErrorParameters, returnCode)
require.Contains(t, message, "invalid HASH parameters")
}

func TestCustomHASHCommands(t *testing.T) {
server := NewTestServer(t, false)
server.settings.EnableHASH = true
Expand Down

0 comments on commit bfb6dd6

Please sign in to comment.