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