1 /* http_api.h -- Routines for handling JMAP API requests
2  *
3  * Copyright (c) 1994-2019 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  *
42  */
43 
44 #ifndef JMAP_API_H
45 #define JMAP_API_H
46 
47 #include "acl.h"
48 #include "auth.h"
49 #include "conversations.h"
50 #include "hash.h"
51 #include "jmap_util.h"
52 #include "json_support.h"
53 #include "mailbox.h"
54 #include "mboxname.h"
55 #include "msgrecord.h"
56 #include "ptrarray.h"
57 #include "strarray.h"
58 
59 #define JMAP_URN_CORE       "urn:ietf:params:jmap:core"
60 #define JMAP_URN_MAIL       "urn:ietf:params:jmap:mail"
61 #define JMAP_URN_SUBMISSION "urn:ietf:params:jmap:submission"
62 #define JMAP_URN_VACATION   "urn:ietf:params:jmap:vacationresponse"
63 #define JMAP_URN_WEBSOCKET  "urn:ietf:params:jmap:websocket"
64 #define JMAP_URN_MDN        "urn:ietf:params:jmap:mdn"
65 
66 #define JMAP_BLOB_EXTENSION          "https://cyrusimap.org/ns/jmap/blob"
67 #define JMAP_CONTACTS_EXTENSION      "https://cyrusimap.org/ns/jmap/contacts"
68 #define JMAP_CALENDARS_EXTENSION     "https://cyrusimap.org/ns/jmap/calendars"
69 #define JMAP_MAIL_EXTENSION          "https://cyrusimap.org/ns/jmap/mail"
70 #define JMAP_PERFORMANCE_EXTENSION   "https://cyrusimap.org/ns/jmap/performance"
71 #define JMAP_DEBUG_EXTENSION         "https://cyrusimap.org/ns/jmap/debug"
72 #define JMAP_QUOTA_EXTENSION         "https://cyrusimap.org/ns/jmap/quota"
73 #define JMAP_BACKUP_EXTENSION        "https://cyrusimap.org/ns/jmap/backup"
74 #define JMAP_NOTES_EXTENSION         "https://cyrusimap.org/ns/jmap/notes"
75 #define JMAP_SIEVE_EXTENSION         "https://cyrusimap.org/ns/jmap/sieve"
76 #define JMAP_USERCOUNTERS_EXTENSION  "https://cyrusimap.org/ns/jmap/usercounters"
77 
78 enum {
79     MAX_SIZE_REQUEST = 0,
80     MAX_CALLS_IN_REQUEST,
81     MAX_CONCURRENT_REQUESTS,
82     MAX_OBJECTS_IN_GET,
83     MAX_OBJECTS_IN_SET,
84     MAX_SIZE_UPLOAD,
85     MAX_CONCURRENT_UPLOAD,
86     MAX_SIZE_BLOB_SET,
87     JMAP_NUM_LIMITS  /* MUST be last */
88 };
89 
90 /* JMAP Mail (RFC 8621) privileges */
91 #define JACL_READITEMS      (ACL_READ|ACL_LOOKUP)
92 #define JACL_ADDITEMS       ACL_INSERT
93 #define JACL_REMOVEITEMS    (ACL_DELETEMSG|ACL_EXPUNGE)
94 #define JACL_SETSEEN        ACL_SETSEEN
95 #define JACL_SETKEYWORDS    ACL_WRITE
96 #define JACL_CREATECHILD    ACL_CREATE
97 #define JACL_DELETE         ACL_DELETEMBOX
98 #define JACL_RENAME         (JACL_CREATECHILD|JACL_DELETE)
99 #define JACL_SUBMIT         ACL_POST
100 
101 /* JMAP Calendar (draft-ietf-jmap-calendars) privileges */
102 #define JACL_READFB         ACL_USER9      /* Keep sync'd with DACL_READFB */
103 #define JACL_RSVP           ACL_USER7      /* Keep sync'd with DACL_REPLY */
104 #define JACL_UPDATEPRIVATE
105 #define JACL_UPDATEOWN
106 #define JACL_UPDATEALL
107 #define JACL_REMOVEOWN
108 #define JACL_REMOVEALL
109 
110 /* Cyrus-specific privileges */
111 #define JACL_LOOKUP         ACL_LOOKUP
112 #define JACL_ADMIN          ACL_ADMIN
113 #define JACL_SETPROPERTIES  ACL_ANNOTATEMSG
114 #define JACL_UPDATEITEMS    (JACL_ADDITEMS|JACL_REMOVEITEMS)
115 #define JACL_SETMETADATA    (JACL_SETKEYWORDS|JACL_SETPROPERTIES)
116 #define JACL_WRITE          (JACL_UPDATEITEMS|JACL_SETSEEN|JACL_SETMETADATA)
117 #define JACL_ALL            (JACL_READITEMS|JACL_WRITE|JACL_RENAME|JACL_SUBMIT\
118                              |JACL_ADMIN|JACL_READFB|JACL_RSVP)
119 
120 typedef struct jmap_req {
121     const char           *method;
122     const char           *userid;
123     const char           *accountid;
124     struct conversations_state *cstate;
125     struct auth_state    *authstate;
126     json_t               *args;
127     json_t               *response;
128     const char           *tag;
129     struct transaction_t *txn;
130     struct mboxname_counters counters;
131 
132     double real_start;
133     double user_start;
134     double sys_start;
135     json_t *perf_details;
136 
137     /* The JMAP request keeps its own cache of opened mailboxes,
138      * which can be used by calling jmap_openmbox. If the
139      * force_openmboxrw is set, this causes all following
140      * mailboxes to be opened read-writeable, irrespective if
141      * the caller asked for a read-only lock. This allows to
142      * prevent lock promotion conflicts, in case a cached mailbox
143      * was opened read-only by a helper but it now asked to be
144      * locked exclusively. Since the mailbox lock does not
145      * support lock promition, this would currently abort with
146      * an error. */
147     int force_openmbox_rw;
148 
149     /* Internal state */
150     ptrarray_t *mboxes;
151     hash_table *mbstates;
152     hash_table *created_ids;
153     hash_table *inmemory_blobs;
154     hash_table *mbentry_byid;
155     ptrarray_t *method_calls;
156     const strarray_t *using_capabilities;
157 } jmap_req_t;
158 
159 /* Write the contents of the blob identified by blobid on the
160  * HTTP transaction embedded in JMAP request req.
161  * If not NULL, accept_mime defines the requested MIME type,
162  * either defined in the Accept header or {type} URI template
163  * parameter.
164  *
165  * Return HTTP_OK if the blob has been written or any other
166  * HTTP status on error.
167  * Return zero if the next blob handler should be called. */
168 typedef int jmap_getblob_handler(jmap_req_t *req,
169                                  const char *blobid,
170                                  const char *accept_mime);
171 
172 typedef struct {
173     hash_table methods;
174     json_t *server_capabilities;
175     long limits[JMAP_NUM_LIMITS];
176     ptrarray_t getblob_handlers; // array of jmap_getblob_handler
177 } jmap_settings_t;
178 
179 enum jmap_method_flags {
180     JMAP_READ_WRITE  = (1 << 0),  /* user can change state with this method */
181     JMAP_NEED_CSTATE = (1 << 1),  /* conv.db is required for this method
182                                      (lock type determined by r/w flag) */
183 };
184 
185 typedef struct {
186     const char *name;
187     const char *capability;
188     int (*proc)(struct jmap_req *req);
189     enum jmap_method_flags flags;
190 } jmap_method_t;
191 
192 extern int jmap_api(struct transaction_t *txn, json_t **res,
193                     jmap_settings_t *settings);
194 
195 extern int jmap_initreq(jmap_req_t *req);
196 extern void jmap_finireq(jmap_req_t *req);
197 
198 extern int jmap_is_using(jmap_req_t *req, const char *capa);
199 
200 /* Protocol implementations */
201 extern void jmap_core_init(jmap_settings_t *settings);
202 extern void jmap_mail_init(jmap_settings_t *settings);
203 extern void jmap_mdn_init(jmap_settings_t *settings);
204 extern void jmap_contact_init(jmap_settings_t *settings);
205 extern void jmap_calendar_init(jmap_settings_t *settings);
206 extern void jmap_vacation_init(jmap_settings_t *settings);
207 extern void jmap_backup_init(jmap_settings_t *settings);
208 extern void jmap_notes_init(jmap_settings_t *settings);
209 extern void jmap_sieve_init(jmap_settings_t *settings);
210 
211 extern void jmap_core_capabilities(json_t *account_capabilities);
212 extern void jmap_mail_capabilities(json_t *account_capabilities, int mayCreateTopLevel);
213 extern void jmap_emailsubmission_capabilities(json_t *account_capabilities);
214 extern void jmap_mdn_capabilities(json_t *account_capabilities);
215 extern void jmap_vacation_capabilities(json_t *account_capabilities);
216 extern void jmap_contact_capabilities(json_t *account_capabilities);
217 extern void jmap_calendar_capabilities(json_t *account_capabilities);
218 extern void jmap_vacation_capabilities(json_t *account_capabilities);
219 extern void jmap_backup_capabilities(json_t *account_capabilities);
220 extern void jmap_notes_capabilities(json_t *account_capabilities);
221 extern void jmap_sieve_capabilities(json_t *account_capabilities);
222 
223 extern void jmap_accounts(json_t *accounts, json_t *primary_accounts);
224 
225 /* Request-scoped mailbox cache */
226 extern int  jmap_openmbox(jmap_req_t *req, const char *name,
227                           struct mailbox **mboxp, int rw);
228 extern int  jmap_isopenmbox(jmap_req_t *req, const char *name);
229 extern void jmap_closembox(jmap_req_t *req, struct mailbox **mboxp);
230 
231 extern int jmap_mboxlist_lookup(const char *name,
232                                 mbentry_t **entryptr, struct txn **tid);
233 
234 /* Adds a JMAP sub request to be processed after req has
235  * finished. Method must be a regular JMAP method name,
236  * args the JSON-encoded method arguments. If client_id
237  * is NULL, the subrequest will use the same client id
238  * as req. The args argument will be unreferenced after
239  * completion. */
240 extern void jmap_add_subreq(jmap_req_t *req, const char *method,
241                             json_t *args, const char *client_id);
242 
243 /* Creation ids */
244 extern const char *jmap_lookup_id(jmap_req_t *req, const char *creation_id);
245 extern const char *jmap_id_string_value(jmap_req_t *req, json_t *item);
246 extern void jmap_add_id(jmap_req_t *req, const char *creation_id, const char *id);
247 extern int jmap_is_valid_id(const char *id);
248 
249 /* Request-scoped cache of mailbox rights for authenticated user */
250 
251 extern int  jmap_myrights_mbentry(jmap_req_t *req, const mbentry_t *mbentry);
252 extern int  jmap_hasrights_mbentry(jmap_req_t *req, const mbentry_t *mbentry,
253                            int rights);
254 extern int  jmap_myrights(jmap_req_t *req, const char *mboxname);
255 extern int  jmap_hasrights(jmap_req_t *req, const char *mboxname, int rights);
256 extern int  jmap_myrights_mboxid(jmap_req_t *req, const char *mboxid);
257 extern int  jmap_hasrights_mboxid(jmap_req_t *req, const char *mboxid, int rights);
258 extern void jmap_myrights_delete(jmap_req_t *req, const char *mboxname);
259 extern int  jmap_mbtype(jmap_req_t *req, const char *mboxname);
260 
261 /* Blob services */
262 extern int jmap_findblob(jmap_req_t *req, const char *accountid,
263                          const char *blobid,
264                          struct mailbox **mbox, msgrecord_t **mr,
265                          struct body **body, const struct body **part,
266                          struct buf *blob);
267 extern int jmap_findblob_exact(jmap_req_t *req, const char *accountid,
268                                const char *blobid,
269                                struct mailbox **mbox, msgrecord_t **mr,
270                                struct buf *blob);
271 
272 extern const struct body *jmap_contact_findblob(struct message_guid *content_guid,
273                                                 const char *part_id,
274                                                 struct mailbox *mbox,
275                                                 msgrecord_t *mr,
276                                                 struct buf *blob);
277 
278 #define JMAP_BLOBID_SIZE 42
279 extern void jmap_set_blobid(const struct message_guid *guid, char *buf);
280 
281 #define JMAP_EMAILID_SIZE 26
282 extern void jmap_set_emailid(const struct message_guid *guid, char *buf);
283 
284 #define JMAP_THREADID_SIZE 18
285 extern void jmap_set_threadid(conversation_id_t cid, char *buf);
286 
287 /* JMAP states */
288 extern json_t* jmap_getstate(jmap_req_t *req, int mbtype, int refresh);
289 extern json_t *jmap_fmtstate(modseq_t modseq);
290 extern int jmap_cmpstate(jmap_req_t *req, json_t *state, int mbtype);
291 extern modseq_t jmap_highestmodseq(jmap_req_t *req, int mbtype);
292 
293 /* Helpers for DAV-based JMAP types */
294 extern char *jmap_xhref(const char *mboxname, const char *resource);
295 
296 /* Patch-object support */
297 
298 extern void jmap_ok(jmap_req_t *req, json_t *res);
299 extern void jmap_error(jmap_req_t *req, json_t *err);
300 
301 extern int jmap_parse_strings(json_t *arg,
302                               struct jmap_parser *parser, const char *prop);
303 
304 typedef struct jmap_property {
305     const char *name;
306     const char *capability;
307     unsigned flags;
308 } jmap_property_t;
309 
310 enum {
311     JMAP_PROP_SERVER_SET = (1<<0),
312     JMAP_PROP_IMMUTABLE  = (1<<1),
313     JMAP_PROP_SKIP_GET   = (1<<2), // skip in Foo/get if not requested by client
314     JMAP_PROP_ALWAYS_GET = (1<<3)  // always include in Foo/get
315 };
316 
317 extern const jmap_property_t *jmap_property_find(const char *name,
318                                                  const jmap_property_t props[]);
319 
320 
321 /* Foo/get */
322 
323 struct jmap_get {
324     /* Request arguments */
325     json_t *ids;
326     json_t *properties;
327     hash_table *props;
328 
329     /* Response fields */
330     char *state;
331     json_t *list;
332     json_t *not_found;
333 };
334 
335 typedef int jmap_args_parse_cb(jmap_req_t *, struct jmap_parser *,
336                                const char *arg, json_t *val, void *);
337 
338 extern void jmap_get_parse(jmap_req_t *req, struct jmap_parser *parser,
339                            const jmap_property_t valid_props[],
340                            int allow_null_ids,
341                            jmap_args_parse_cb args_parse, void *args_rock,
342                            struct jmap_get *get,
343                            json_t **err);
344 
345 extern void jmap_get_fini(struct jmap_get *get);
346 extern json_t *jmap_get_reply(struct jmap_get *get);
347 
348 
349 /* Foo/set */
350 
351 struct jmap_set {
352     /* Request arguments */
353     const char *if_in_state;
354     json_t *create;
355     json_t *update;
356     json_t *destroy;
357 
358     /* Response fields */
359     char *old_state;
360     char *new_state;
361     json_t *created;
362     json_t *updated;
363     json_t *destroyed;
364     json_t *not_created;
365     json_t *not_updated;
366     json_t *not_destroyed;
367 };
368 
369 extern void jmap_set_parse(jmap_req_t *req, struct jmap_parser *parser,
370                            const jmap_property_t valid_props[],
371                            jmap_args_parse_cb args_parse, void *args_rock,
372                            struct jmap_set *set, json_t **err);
373 extern void jmap_set_fini(struct jmap_set *set);
374 extern json_t *jmap_set_reply(struct jmap_set *set);
375 
376 
377 /* Foo/changes */
378 
379 struct jmap_changes {
380     /* Request arguments */
381     modseq_t since_modseq;
382     size_t max_changes;
383 
384     /* Response fields */
385     modseq_t new_modseq;
386     short has_more_changes;
387     json_t *created;
388     json_t *updated;
389     json_t *destroyed;
390 };
391 
392 extern void jmap_changes_parse(jmap_req_t *req, struct jmap_parser *parser,
393                                modseq_t minmodseq,
394                                jmap_args_parse_cb args_parse, void *args_rock,
395                                struct jmap_changes *changes, json_t **err);
396 extern void jmap_changes_fini(struct jmap_changes *changes);
397 extern json_t *jmap_changes_reply(struct jmap_changes *changes);
398 
399 
400 /* Foo/copy */
401 
402 struct jmap_copy {
403     /* Request arguments */
404     const char *from_account_id;
405     json_t *create;
406     int blob_copy;
407     int on_success_destroy_original;
408 
409     /* Response fields */
410     json_t *created;
411     json_t *not_created;
412 };
413 
414 extern void jmap_copy_parse(jmap_req_t *req, struct jmap_parser *parser,
415                             jmap_args_parse_cb args_parse, void *args_rock,
416                             struct jmap_copy *copy, json_t **err);
417 extern void jmap_copy_fini(struct jmap_copy *copy);
418 extern json_t *jmap_copy_reply(struct jmap_copy *copy);
419 
420 
421 /* Foo/query */
422 
423 struct jmap_query {
424     /* Request arguments */
425     json_t *filter;
426     json_t *sort;
427     ssize_t position;
428     const char *anchor;
429     ssize_t anchor_offset;
430     size_t limit;
431     int have_limit;
432     int calculate_total;
433     int sort_savedate;
434 
435     /* Response fields */
436     char *query_state;
437     int can_calculate_changes;
438     size_t result_position;
439     size_t server_limit;
440     size_t total;
441     json_t *ids;
442 };
443 
444 enum jmap_filter_op   {
445     JMAP_FILTER_OP_NONE = 0,
446     JMAP_FILTER_OP_AND,
447     JMAP_FILTER_OP_OR,
448     JMAP_FILTER_OP_NOT
449 };
450 
451 typedef struct jmap_filter {
452     enum jmap_filter_op op;
453     ptrarray_t conditions;
454 } jmap_filter;
455 
456 typedef void* jmap_buildfilter_cb(json_t* arg);
457 typedef int   jmap_filtermatch_cb(void* cond, void* rock);
458 typedef void  jmap_filterfree_cb(void* cond);
459 
460 extern jmap_filter *jmap_buildfilter(json_t *arg, jmap_buildfilter_cb *parse);
461 extern int jmap_filter_match(jmap_filter *f,
462                              jmap_filtermatch_cb *match, void *rock);
463 extern void jmap_filter_free(jmap_filter *f, jmap_filterfree_cb *freecond);
464 
465 typedef void jmap_filter_parse_cb(jmap_req_t *req, struct jmap_parser *parser,
466                                   json_t *filter, json_t *unsupported,
467                                   void *rock, json_t **err);
468 
469 extern void jmap_filter_parse(jmap_req_t *req, struct jmap_parser *parser,
470                               json_t *filter, json_t *unsupported,
471                               jmap_filter_parse_cb parse_condition, void *cond_rock,
472                               json_t **err /* fatal, non-parsing error */);
473 
474 struct jmap_comparator {
475     const char *property;
476     short is_ascending;
477     const char *collation;
478 };
479 
480 typedef int jmap_comparator_parse_cb(jmap_req_t *req, struct jmap_comparator *comp,
481                                      void *rock, json_t **err);
482 
483 extern void jmap_comparator_parse(jmap_req_t *req, struct jmap_parser *parser,
484                                   json_t *jsort, json_t *unsupported,
485                                   jmap_comparator_parse_cb comp_cb, void *comp_rock,
486                                   json_t **err);
487 
488 extern void jmap_query_parse(jmap_req_t *req, struct jmap_parser *parser,
489                              jmap_args_parse_cb args_parse, void *args_rock,
490                              jmap_filter_parse_cb filter_cb, void *filter_rock,
491                              jmap_comparator_parse_cb comp_cb, void *comp_rock,
492                              struct jmap_query *query, json_t **err);
493 
494 extern void jmap_query_fini(struct jmap_query *query);
495 
496 extern json_t *jmap_query_reply(struct jmap_query *query);
497 
498 
499 /* Foo/queryChanges */
500 
501 struct jmap_querychanges {
502     /* Request arguments */
503     json_t *filter;
504     json_t *sort;
505     const char *since_querystate;
506     size_t max_changes;
507     const char *up_to_id;
508     int calculate_total;
509 
510     /* Response fields */
511     char *new_querystate;
512     size_t total;
513     json_t *removed;
514     json_t *added;
515 };
516 
517 extern void jmap_querychanges_parse(jmap_req_t *req,
518                                     struct jmap_parser *parser,
519                                     jmap_args_parse_cb args_parse, void *args_rock,
520                                     jmap_filter_parse_cb filter_cb, void *filter_rock,
521                                     jmap_comparator_parse_cb comp_cb, void *sort_rock,
522                                     struct jmap_querychanges *query,
523                                     json_t **err);
524 
525 extern void jmap_querychanges_fini(struct jmap_querychanges *query);
526 
527 extern json_t *jmap_querychanges_reply(struct jmap_querychanges *query);
528 
529 
530 /* Foo/parse */
531 
532 struct jmap_parse {
533     /* Request arguments */
534     const json_t *blob_ids;
535 
536     /* Response fields */
537     json_t *parsed;
538     json_t *not_parsable;
539     json_t *not_found;
540 };
541 
542 extern void jmap_parse_parse(jmap_req_t *req, struct jmap_parser *parser,
543                                  jmap_args_parse_cb args_parse, void *args_rock,
544                                  struct jmap_parse *parse,
545                                  json_t **err);
546 
547 extern void jmap_parse_fini(struct jmap_parse *parse);
548 
549 extern json_t *jmap_parse_reply(struct jmap_parse *parse);
550 
551 
552 extern json_t *jmap_get_sharewith(const mbentry_t *mbentry);
553 extern int jmap_set_sharewith(struct mailbox *mbox,
554                               json_t *shareWith, int overwrite);
555 extern void jmap_parse_sharewith_patch(json_t *arg, json_t **shareWith);
556 
557 extern void jmap_mbentry_cache_free(jmap_req_t *req);
558 extern const mbentry_t *jmap_mbentry_by_uniqueid(jmap_req_t *req, const char *id);
559 extern mbentry_t *jmap_mbentry_by_uniqueid_copy(jmap_req_t *req, const char *id);
560 
561 #endif /* JMAP_API_H */
562