1This file describes the schemata used for notmuch's structured output 2format (currently JSON and S-Expressions). 3 4[]'s indicate lists. List items can be marked with a '?', meaning 5they are optional; or a '*', meaning there can be zero or more of that 6item. {}'s indicate an object that maps from field identifiers to 7values. An object field marked '?' is optional; one marked with '*' 8can repeat (with a different name). |'s indicate alternates (e.g., 9int|string means something can be an int or a string). 10 11For S-Expression output, lists are printed delimited by () instead of 12[]. Objects are printed as p-lists, i.e. lists where the keys and values 13are interleaved. Keys are printed as keywords (symbols preceded by a 14colon), e.g. (:id "123" :time 54321 :from "foobar"). Null is printed as 15nil, true as t and false as nil. 16 17This is version 5 of the structured output format. 18 19Version history 20--------------- 21 22v1 23- First versioned schema release. 24- Added part.content-length and part.content-transfer-encoding fields. 25 26v2 27- Added the thread_summary.query field. 28 29v3 30- Replaced message.filename string with a list of filenames. 31- Added part.content-disposition field. 32 33v4 34- replace signature error integer bitmask with a set of flags for 35 individual errors. 36- (notmuch 0.29) added message.crypto to identify overall message 37 cryptographic state 38 39v5 40- sorting support for notmuch show (no change to actual schema, 41 just new command line argument) 42 43Common non-terminals 44-------------------- 45 46# Number of seconds since the Epoch 47unix_time = int 48 49# Thread ID, sans "thread:" 50threadid = string 51 52# Message ID, sans "id:" 53messageid = string 54 55# E-mail header name, sans trailing colon, like "Subject" or "In-Reply-To" 56header_name = string 57 58notmuch show schema 59------------------- 60 61# A top-level set of threads (do_show) 62# Returned by notmuch show without a --part argument 63thread_set = [thread*] 64 65# Top-level messages in a thread (show_messages) 66thread = [thread_node*] 67 68# A message and its replies (show_messages) 69thread_node = [ 70 message|null, # null if not matched and not --entire-thread 71 [thread_node*] # children of message 72] 73 74# A message (format_part_sprinter) 75message = { 76 # (format_message_sprinter) 77 id: messageid, 78 match: bool, 79 filename: [string*], 80 timestamp: unix_time, # date header as unix time 81 date_relative: string, # user-friendly timestamp 82 tags: [string*], 83 84 headers: headers, 85 crypto: crypto, 86 body?: [part] # omitted if --body=false 87} 88 89# when showing the message, was any or all of it decrypted? 90msgdecstatus: "full"|"partial" 91 92# The overall cryptographic state of the message as a whole: 93crypto = { 94 signed?: { 95 status: sigstatus, 96 # was the set of signatures described under encrypted cover? 97 encrypted: bool, 98 # which of the headers is covered by sigstatus? 99 headers: [header_name*] 100 }, 101 decrypted?: { 102 status: msgdecstatus, 103 # map encrypted headers that differed from the outside headers. 104 # the value of each item in the map is what that field showed externally 105 # (maybe null if it was not present in the external headers). 106 header-mask: { header_name*: string|null } 107 } 108} 109 110# A MIME part (format_part_sprinter) 111part = { 112 id: int|string, # part id (currently DFS part number) 113 114 encstatus?: encstatus, 115 sigstatus?: sigstatus, 116 117 content-type: string, 118 content-disposition?: string, 119 content-id?: string, 120 # if content-type starts with "multipart/": 121 content: [part*], 122 # if content-type is "message/rfc822": 123 content: [{headers: headers, body: [part]}], 124 # otherwise (leaf parts): 125 filename?: string, 126 content-charset?: string, 127 # A leaf part's body content is optional, but may be included if 128 # it can be correctly encoded as a string. Consumers should use 129 # this in preference to fetching the part content separately. 130 content?: string, 131 # If a leaf part's body content is not included, the length of 132 # the encoded content (in bytes) may be given instead. 133 content-length?: int, 134 # If a leaf part's body content is not included, its transfer encoding 135 # may be given. Using this and the encoded content length, it is 136 # possible for the consumer to estimate the decoded content length. 137 content-transfer-encoding?: string 138} 139 140# The headers of a message or part (format_headers_sprinter with reply = FALSE) 141headers = { 142 Subject: string, 143 From: string, 144 To?: string, 145 Cc?: string, 146 Bcc?: string, 147 Reply-To?: string, 148 Date: string 149} 150 151# Encryption status (format_part_sprinter) 152encstatus = [{status: "good"|"bad"}] 153 154# Signature status (format_part_sigstatus_sprinter) 155sigstatus = [signature*] 156 157signature = { 158 # (signature_status_to_string) 159 status: "good"|"bad"|"error"|"unknown", 160 # if status is "good": 161 fingerprint?: string, 162 created?: unix_time, 163 expires?: unix_time, 164 userid?: string 165 email?: string 166 # if status is not "good": 167 keyid?: string 168 errors?: sig_errors 169} 170 171sig_errors = { 172 key-revoked?: bool, 173 key-expired?: bool, 174 sig-expired?: bool, 175 key-missing?: bool, 176 alg-unsupported?: bool, 177 crl-missing?: bool, 178 crl-too-old?: bool, 179 bad-policy?: bool, 180 sys-error?: bool, 181 tofu-conflict?: bool 182} 183 184notmuch search schema 185--------------------- 186 187# --output=summary 188search_summary = [thread_summary*] 189 190# --output=threads 191search_threads = [threadid*] 192 193# --output=messages 194search_messages = [messageid*] 195 196# --output=files 197search_files = [string*] 198 199# --output=tags 200search_tags = [string*] 201 202thread_summary = { 203 thread: threadid, 204 timestamp: unix_time, 205 date_relative: string, # user-friendly timestamp 206 matched: int, # number of matched messages 207 total: int, # total messages in thread 208 authors: string, # comma-separated names with | between 209 # matched and unmatched 210 subject: string, 211 tags: [string*], 212 213 # Two stable query strings identifying exactly the matched and 214 # unmatched messages currently in this thread. The messages 215 # matched by these queries will not change even if more messages 216 # arrive in the thread. If there are no matched or unmatched 217 # messages, the corresponding query will be null (there is no 218 # query that matches nothing). (Added in schema version 2.) 219 query: [string|null, string|null], 220} 221 222notmuch reply schema 223-------------------- 224 225reply = { 226 # The headers of the constructed reply 227 reply-headers: reply_headers, 228 229 # As in the show format (format_part_sprinter) 230 original: message 231} 232 233# Reply headers (format_headers_sprinter with reply = TRUE) 234reply_headers = { 235 Subject: string, 236 From: string, 237 To?: string, 238 Cc?: string, 239 Bcc?: string, 240 In-reply-to: string, 241 References: string 242} 243