From a87e30dc1b7779d39613a85345ec19e449901dd4 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 19 Dec 2017 13:36:40 +0100 Subject: pkg/email: improve parsing of splitted lines Allow: full-commit-title-on-next-line This allows commit titles between 70 and 80 cols with gmail. Also be more permissive wrt spaces and tabs. --- pkg/email/parser.go | 61 ++++++++++++++++++++++++++++++++++-------------- pkg/email/parser_test.go | 48 +++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 29 deletions(-) (limited to 'pkg') diff --git a/pkg/email/parser.go b/pkg/email/parser.go index 67c0159cd..c59736a1b 100644 --- a/pkg/email/parser.go +++ b/pkg/email/parser.go @@ -172,42 +172,69 @@ func extractCommand(body []byte) (cmd, args string) { return } cmdPos += len(commandPrefix) + for cmdPos < len(body) && body[cmdPos] == ' ' { + cmdPos++ + } cmdEnd := bytes.IndexByte(body[cmdPos:], '\n') if cmdEnd == -1 { cmdEnd = len(body) - cmdPos } - cmdLine := strings.TrimSpace(string(body[cmdPos : cmdPos+cmdEnd])) - if cmdLine == "" { - return + if cmdEnd1 := bytes.IndexByte(body[cmdPos:], '\r'); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { + cmdEnd = cmdEnd1 } - split := strings.Split(cmdLine, " ") - cmd = split[0] - var aargs []string - if len(split) > 1 { - aargs = split[1:] + if cmdEnd1 := bytes.IndexByte(body[cmdPos:], ' '); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { + cmdEnd = cmdEnd1 } - // Text emails are split at 80 columns are the transformation is irrevesible. - // Try to restore args for some commands that don't have spaces in args. - want := 0 + cmd = string(body[cmdPos : cmdPos+cmdEnd]) + // Some email clients split text emails at 80 columns are the transformation is irrevesible. + // We try hard to restore what was there before. + // For "test:" command we know that there must be 2 tokens without spaces. + // For "fix:"/"dup:" we need a whole non-empty line of text. switch cmd { case "test:": - want = 2 + args = extractArgsTokens(body[cmdPos+cmdEnd:], 2) case "test_5_arg_cmd": - want = 5 + args = extractArgsTokens(body[cmdPos+cmdEnd:], 5) + case "fix:", "dup:": + args = extractArgsLine(body[cmdPos+cmdEnd:]) } - for pos := cmdPos + cmdEnd + 1; len(aargs) < want && pos < len(body); { + return +} + +func extractArgsTokens(body []byte, num int) string { + var args []string + for pos := 0; len(args) < num && pos < len(body); { lineEnd := bytes.IndexByte(body[pos:], '\n') if lineEnd == -1 { lineEnd = len(body) - pos } line := strings.TrimSpace(string(body[pos : pos+lineEnd])) + for { + line1 := strings.Replace(line, " ", " ", -1) + if line == line1 { + break + } + line = line1 + } if line != "" { - aargs = append(aargs, strings.Split(line, " ")...) + args = append(args, strings.Split(line, " ")...) } pos += lineEnd + 1 } - args = strings.TrimSpace(strings.Join(aargs, " ")) - return + return strings.TrimSpace(strings.Join(args, " ")) +} + +func extractArgsLine(body []byte) string { + pos := 0 + for pos < len(body) && (body[pos] == ' ' || body[pos] == '\t' || + body[pos] == '\n' || body[pos] == '\r') { + pos++ + } + lineEnd := bytes.IndexByte(body[pos:], '\n') + if lineEnd == -1 { + lineEnd = len(body) - pos + } + return strings.TrimSpace(string(body[pos : pos+lineEnd])) } func parseBody(r io.Reader, headers mail.Header) (body []byte, attachments [][]byte, err error) { diff --git a/pkg/email/parser_test.go b/pkg/email/parser_test.go index e4e79e253..727dafc02 100644 --- a/pkg/email/parser_test.go +++ b/pkg/email/parser_test.go @@ -19,6 +19,12 @@ func TestExtractCommand(t *testing.T) { t.Logf("got : %q %q", cmd, args) t.Fail() } + cmd, args = extractCommand([]byte(strings.Replace(test.body, "\n", "\r\n", -1))) + if cmd != test.cmd || !reflect.DeepEqual(args, test.args) { + t.Logf("expect: %q %q", test.cmd, test.args) + t.Logf("got : %q %q", cmd, args) + t.Fail() + } }) } } @@ -118,24 +124,24 @@ var extractCommandTests = []struct { body: `Hello, line1 -#syz foo bar baz `, - cmd: "foo", +#syz fix: bar baz `, + cmd: "fix:", args: "bar baz", }, { body: `Hello, line1 -#syz foo bar baz +#syz fix: bar baz line 2 `, - cmd: "foo", + cmd: "fix:", args: "bar baz", }, { body: ` line1 -> #syz foo bar baz +> #syz fix: bar baz line 2 `, cmd: "", @@ -173,7 +179,7 @@ locking/core body: ` #syz test_5_arg_cmd arg1 - arg2 arg3 + arg2 arg3 arg4 arg5 @@ -206,6 +212,24 @@ arg2 cmd: "test_5_arg_cmd", args: "arg1 arg2", }, + { + body: ` +#syz fix: +arg1 arg2 arg3 +arg4 arg5 + +`, + cmd: "fix:", + args: "arg1 arg2 arg3", + }, + { + body: ` +#syz fix: arg1 arg2 arg3 +arg4 arg5 +`, + cmd: "fix:", + args: "arg1 arg2 arg3", + }, } type ParseTest struct { @@ -223,7 +247,7 @@ Content-Type: text/plain; charset="UTF-8" text body second line -#syz command arg1 arg2 arg3 +#syz fix: arg1 arg2 arg3 last line -- You received this message because you are subscribed to the Google Groups "syzkaller" group. @@ -240,7 +264,7 @@ For more options, visit https://groups.google.com/d/optout.`, Cc: []string{"bob@example.com"}, Body: `text body second line -#syz command arg1 arg2 arg3 +#syz fix: arg1 arg2 arg3 last line -- You received this message because you are subscribed to the Google Groups "syzkaller" group. @@ -249,7 +273,7 @@ To post to this group, send email to syzkaller@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/abcdef@google.com. For more options, visit https://groups.google.com/d/optout.`, Patch: "", - Command: "command", + Command: "fix:", CommandArgs: "arg1 arg2 arg3", }}, @@ -280,7 +304,7 @@ From: Bob To: syzbot , Alice Content-Type: text/plain -#syz command +#syz invalid text body second line last line`, @@ -289,12 +313,12 @@ last line`, Subject: "test subject", From: "\"Bob\" ", Cc: []string{"alice@example.com", "bob@example.com", "bot@example.com"}, - Body: `#syz command + Body: `#syz invalid text body second line last line`, Patch: "", - Command: "command", + Command: "invalid", CommandArgs: "", }}, -- cgit mrf-deployment