Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix quoted, space separated, HASH parameters #459

Merged
merged 1 commit into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading