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  }