github.com/jordwest/imap-server@v0.0.0-20200627020849-1cf758ba359f/conn/commands.go (about) 1 package conn 2 3 import ( 4 "fmt" 5 "regexp" 6 7 "github.com/jordwest/imap-server/mailstore" 8 ) 9 10 type command struct { 11 match *regexp.Regexp 12 handler func(commandArgs, *Conn) 13 } 14 15 type commandArgs []string 16 17 func (a commandArgs) FullCommand() string { 18 return a[0] 19 } 20 21 func (a commandArgs) ID() string { 22 return a[1] 23 } 24 25 func (a commandArgs) Arg(i int) string { 26 return a[i+2] 27 } 28 29 func (a commandArgs) DebugPrint(prompt string) { 30 fmt.Printf("%s\n", prompt) 31 fmt.Printf("\tFull Command: %s\n", a.FullCommand()) 32 fmt.Printf("\t.ID(): %s\n", a.ID()) 33 for index, arg := range a { 34 if index < 2 { 35 continue 36 } 37 fmt.Printf("\t.Arg(%d): \"%s\"\n", index-2, arg) 38 } 39 } 40 41 var commands []command 42 43 // Register all supported client command handlers 44 // with the server. This function is run on server startup and 45 // panics if a command regex is invalid. 46 func init() { 47 commands = make([]command, 0) 48 49 // A sequence set consists only of digits, colons, stars and commas. 50 // eg: 5,9,10:15,256:*,566 51 sequenceSet := "[\\d\\:\\*\\,]+" 52 53 registerCommand("(?i:CAPABILITY)", cmdCapability) 54 registerCommand("(?i:LOGIN) \"([A-z0-9]+)\" \"([A-z0-9]+)\"", cmdLogin) 55 registerCommand("(?i:AUTHENTICATE PLAIN)", cmdAuthPlain) 56 registerCommand("(?i:LIST) \"?([A-z0-9]+)?\"? \"?([A-z0-9*]+)?\"?", cmdList) 57 registerCommand("(?i:LSUB)", cmdLSub) 58 registerCommand("(?i:LOGOUT)", cmdLogout) 59 registerCommand("(?i:NOOP)", cmdNoop) 60 registerCommand("(?i:CLOSE)", cmdClose) 61 registerCommand("(?i:EXPUNGE)", cmdExpunge) 62 registerCommand("(?i:SELECT) \"?([A-z0-9]+)?\"?", cmdSelect) 63 registerCommand("(?i:EXAMINE) \"?([A-z0-9]+)\"?", cmdExamine) 64 registerCommand("(?i:STATUS) \"?([A-z0-9/]+)\"? \\(([A-z\\s]+)\\)", cmdStatus) 65 registerCommand("((?i)UID )?(?i:FETCH) ("+sequenceSet+") \\(([A-z0-9\\s\\(\\)\\[\\]\\.-]+)\\)", cmdFetch) 66 67 // APPEND "INBOX" (\Seen) {310} 68 // APPEND "INBOX" (\Seen) "21-Jun-2015 01:00:25 +0900" {310} 69 // APPEND "INBOX" {310} 70 registerCommand("(?i:APPEND) \"?([A-z0-9/]+)\"?(?: \\(([\\\\A-z\\s]+)\\))?(?: \"([A-Za-z0-9\\-\\:\\+ ]+)\")? {([0-9]+)}", cmdAppend) 71 72 // STORE 2:4 +FLAGS (\Deleted) Mark messages as deleted 73 // STORE 2:4 -FLAGS (\Seen) Mark messages as unseen 74 // STORE 2:4 FLAGS (\Seen \Deleted) Replace flags 75 registerCommand("((?i)UID )?(?i:STORE) ("+sequenceSet+") ([\\+\\-])?(?i:FLAGS(\\.SILENT)?) \\(?([\\\\A-z0-9\\s]+)\\)?", cmdStoreFlags) 76 77 registerCommand("((?i)UID )?(?i:COPY) ("+sequenceSet+") \"?([A-z0-9]+)?\"?", cmdCopy) 78 79 registerCommand("", cmdNA) 80 } 81 82 func registerCommand(matchExpr string, handleFunc func(commandArgs, *Conn)) error { 83 // Add command identifier to beginning of command 84 matchExpr = "([A-z0-9\\.]+) " + matchExpr 85 86 newRE := regexp.MustCompile(matchExpr) 87 c := command{match: newRE, handler: handleFunc} 88 commands = append(commands, c) 89 return nil 90 } 91 92 // Write out the info for a mailbox (used in both SELECT and EXAMINE) 93 func writeMailboxInfo(c *Conn, m mailstore.Mailbox) { 94 fmt.Fprintf(c, "* %d EXISTS\r\n", m.Messages()) 95 fmt.Fprintf(c, "* %d RECENT\r\n", m.Recent()) 96 fmt.Fprintf(c, "* OK [UNSEEN %d]\r\n", m.Unseen()) 97 fmt.Fprintf(c, "* OK [UIDNEXT %d]\r\n", m.NextUID()) 98 fmt.Fprintf(c, "* OK [UIDVALIDITY %d]\r\n", 250) 99 fmt.Fprintf(c, "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n") 100 } 101 102 func cmdNA(args commandArgs, c *Conn) { 103 c.writeResponse(args.ID(), "BAD Not implemented") 104 }