IMAP in OCaml
at main 127 lines 5.6 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** IMAP Commands 7 8 Client-to-server commands as specified in RFC 9051. *) 9 10(** ESEARCH return options (RFC 4731) *) 11type search_return_opt = 12 | Return_min (** Return minimum matching sequence number/UID *) 13 | Return_max (** Return maximum matching sequence number/UID *) 14 | Return_all (** Return all matching sequence numbers/UIDs as a sequence set *) 15 | Return_count (** Return count of matching messages *) 16 17type t = 18 | Capability 19 | Noop 20 | Logout 21 | Starttls 22 | Login of { username : string; password : string } 23 | Authenticate of { mechanism : string; initial_response : string option } 24 | Enable of string list 25 | Select of Mailbox.t 26 | Examine of Mailbox.t 27 | Create of Mailbox.t 28 | Delete of Mailbox.t 29 | Rename of { old_name : Mailbox.t; new_name : Mailbox.t } 30 | Subscribe of Mailbox.t 31 | Unsubscribe of Mailbox.t 32 | List of { reference : string; pattern : string } 33 | Namespace 34 | Status of { mailbox : Mailbox.t; items : Status.item list } 35 | Append of { 36 mailbox : Mailbox.t; 37 flags : Flag.t list; 38 date : string option; 39 message : string; 40 } 41 | Idle 42 | Close 43 | Unselect 44 | Expunge 45 | Search of { charset : string option; criteria : Search.t; return_opts : search_return_opt list option } 46 | Sort of { charset : string; criteria : Sort.t; search : Search.t } 47 | Thread of { algorithm : Thread.algorithm; charset : string; search : Search.t } 48 | Fetch of { sequence : Seq.t; items : Fetch.request list; changedsince : int64 option } 49 | Store of { 50 sequence : Seq.t; 51 silent : bool; 52 action : Store.t; 53 flags : Flag.t list; 54 unchangedsince : int64 option; 55 } 56 | Copy of { sequence : Seq.t; mailbox : Mailbox.t } 57 | Move of { sequence : Seq.t; mailbox : Mailbox.t } 58 | Uid of uid_command 59 | Id of (string * string) list option 60 61and uid_command = 62 | Uid_fetch of { sequence : Seq.t; items : Fetch.request list; changedsince : int64 option } 63 | Uid_store of { 64 sequence : Seq.t; 65 silent : bool; 66 action : Store.t; 67 flags : Flag.t list; 68 unchangedsince : int64 option; 69 } 70 | Uid_copy of { sequence : Seq.t; mailbox : Mailbox.t } 71 | Uid_move of { sequence : Seq.t; mailbox : Mailbox.t } 72 | Uid_search of { charset : string option; criteria : Search.t; return_opts : search_return_opt list option } 73 | Uid_sort of { charset : string; criteria : Sort.t; search : Search.t } 74 | Uid_thread of { algorithm : Thread.algorithm; charset : string; search : Search.t } 75 | Uid_expunge of Seq.t 76 77type tagged = { tag : string; command : t } 78 79(** Pretty-printer for commands (passwords redacted for security) *) 80let rec pp ppf = function 81 | Capability -> Fmt.string ppf "CAPABILITY" 82 | Noop -> Fmt.string ppf "NOOP" 83 | Logout -> Fmt.string ppf "LOGOUT" 84 | Starttls -> Fmt.string ppf "STARTTLS" 85 | Login { username; _ } -> Fmt.pf ppf "LOGIN %s ***" username 86 | Authenticate { mechanism; initial_response } -> 87 Fmt.pf ppf "AUTHENTICATE %s%s" mechanism 88 (if Option.is_some initial_response then " <initial-response>" else "") 89 | Enable caps -> Fmt.pf ppf "ENABLE %s" (String.concat " " caps) 90 | Select mailbox -> Fmt.pf ppf "SELECT %s" mailbox 91 | Examine mailbox -> Fmt.pf ppf "EXAMINE %s" mailbox 92 | Create mailbox -> Fmt.pf ppf "CREATE %s" mailbox 93 | Delete mailbox -> Fmt.pf ppf "DELETE %s" mailbox 94 | Rename { old_name; new_name } -> Fmt.pf ppf "RENAME %s %s" old_name new_name 95 | Subscribe mailbox -> Fmt.pf ppf "SUBSCRIBE %s" mailbox 96 | Unsubscribe mailbox -> Fmt.pf ppf "UNSUBSCRIBE %s" mailbox 97 | List { reference; pattern } -> Fmt.pf ppf "LIST %S %S" reference pattern 98 | Namespace -> Fmt.string ppf "NAMESPACE" 99 | Status { mailbox; _ } -> Fmt.pf ppf "STATUS %s (...)" mailbox 100 | Append { mailbox; _ } -> Fmt.pf ppf "APPEND %s (...)" mailbox 101 | Idle -> Fmt.string ppf "IDLE" 102 | Close -> Fmt.string ppf "CLOSE" 103 | Unselect -> Fmt.string ppf "UNSELECT" 104 | Expunge -> Fmt.string ppf "EXPUNGE" 105 | Search _ -> Fmt.string ppf "SEARCH (...)" 106 | Sort _ -> Fmt.string ppf "SORT (...)" 107 | Thread _ -> Fmt.string ppf "THREAD (...)" 108 | Fetch { sequence; _ } -> Fmt.pf ppf "FETCH %a (...)" Seq.pp sequence 109 | Store { sequence; action; _ } -> 110 let action_str = match action with Store.Set -> "FLAGS" | Store.Add -> "+FLAGS" | Store.Remove -> "-FLAGS" in 111 Fmt.pf ppf "STORE %a %s (...)" Seq.pp sequence action_str 112 | Copy { sequence; mailbox } -> Fmt.pf ppf "COPY %a %s" Seq.pp sequence mailbox 113 | Move { sequence; mailbox } -> Fmt.pf ppf "MOVE %a %s" Seq.pp sequence mailbox 114 | Uid cmd -> Fmt.pf ppf "UID %a" pp_uid cmd 115 | Id _ -> Fmt.string ppf "ID (...)" 116 117and pp_uid ppf = function 118 | Uid_fetch { sequence; _ } -> Fmt.pf ppf "FETCH %a (...)" Seq.pp sequence 119 | Uid_store { sequence; action; _ } -> 120 let action_str = match action with Store.Set -> "FLAGS" | Store.Add -> "+FLAGS" | Store.Remove -> "-FLAGS" in 121 Fmt.pf ppf "STORE %a %s (...)" Seq.pp sequence action_str 122 | Uid_copy { sequence; mailbox } -> Fmt.pf ppf "COPY %a %s" Seq.pp sequence mailbox 123 | Uid_move { sequence; mailbox } -> Fmt.pf ppf "MOVE %a %s" Seq.pp sequence mailbox 124 | Uid_search _ -> Fmt.string ppf "SEARCH (...)" 125 | Uid_sort _ -> Fmt.string ppf "SORT (...)" 126 | Uid_thread _ -> Fmt.string ppf "THREAD (...)" 127 | Uid_expunge seq -> Fmt.pf ppf "EXPUNGE %a" Seq.pp seq