Files
gomail/internal/imap/parser.go
T

78 lines
1.7 KiB
Go
Raw Normal View History

2026-08-09 18:03:09 +01:00
package imap
import "strings"
// tokenize splits an IMAP command line into space-separated tokens, treating
// "quoted strings" and (parenthesized lists) as single tokens (lists keep
// their outer parens so command handlers can recognize and further split
// them). Literal syntax ({n}\r\n<bytes>) is not handled here — see session.go's
// readCommand, which handles literals as a pre-pass before tokenizing since
// they require reading raw bytes off the connection, not just string scanning.
func tokenize(line string) []string {
var tokens []string
i, n := 0, len(line)
for i < n {
for i < n && (line[i] == ' ' || line[i] == '\t') {
i++
}
if i >= n {
break
}
switch line[i] {
case '"':
j := i + 1
var sb strings.Builder
for j < n && line[j] != '"' {
if line[j] == '\\' && j+1 < n {
j++
}
sb.WriteByte(line[j])
j++
}
tokens = append(tokens, sb.String())
i = j + 1
case '(':
depth := 1
j := i + 1
for j < n && depth > 0 {
switch line[j] {
case '(':
depth++
case ')':
depth--
}
j++
}
tokens = append(tokens, line[i:j])
i = j
default:
j := i
for j < n && line[j] != ' ' && line[j] != '\t' {
j++
}
tokens = append(tokens, line[i:j])
i = j
}
}
return tokens
}
// splitList takes a token like "(FLAGS UID)" and returns its inner
// space-separated items — used by FETCH/STORE argument parsing.
func splitList(token string) []string {
inner := strings.TrimPrefix(token, "(")
inner = strings.TrimSuffix(inner, ")")
if inner == "" {
return nil
}
return tokenize(inner)
}
func isList(token string) bool {
return strings.HasPrefix(token, "(") && strings.HasSuffix(token, ")")
}