1 /* annotate.c -- Annotation manipulation routines
2 *
3 * Copyright (c) 1994-2008 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 #include <config.h>
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #include <errno.h>
52 #ifdef HAVE_INTTYPES_H
53 # include <inttypes.h>
54 #elif defined(HAVE_STDINT_H)
55 # include <stdint.h>
56 #endif
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <sys/uio.h>
60 #include <fcntl.h>
61 #include <ctype.h>
62 #include <syslog.h>
63
64 #include "acl.h"
65 #include "assert.h"
66 #include "cyrusdb.h"
67 #include "exitcodes.h"
68 #include "glob.h"
69 #include "hash.h"
70 #include "imapd.h"
71 #include "global.h"
72 #include "times.h"
73 #include "mboxlist.h"
74 #include "partlist.h"
75 #include "util.h"
76 #include "xmalloc.h"
77 #include "ptrarray.h"
78 #include "xstrlcpy.h"
79 #include "xstrlcat.h"
80 #include "tok.h"
81 #include "quota.h"
82
83 /* generated headers are not necessarily in current directory */
84 #include "imap/imap_err.h"
85
86 #include "annotate.h"
87 #include "sync_log.h"
88
89 #define DEBUG 0
90
91 #define ANNOTATION_SCOPE_UNKNOWN (-1)
92 enum {
93 ANNOTATION_SCOPE_SERVER = 1,
94 ANNOTATION_SCOPE_MAILBOX = 2,
95 ANNOTATION_SCOPE_MESSAGE = 3
96 };
97
98 typedef struct annotate_entrydesc annotate_entrydesc_t;
99
100 struct annotate_entry_list
101 {
102 struct annotate_entry_list *next;
103 const annotate_entrydesc_t *desc;
104 char *name;
105 /* used for storing */
106 struct buf shared;
107 struct buf priv;
108 int have_shared;
109 int have_priv;
110 };
111
112 /* Encapsulates all the state involved in providing the scope
113 * for setting or getting a single annotation */
114 struct annotate_state
115 {
116 /*
117 * Common between storing and fetching
118 */
119 int which; /* ANNOTATION_SCOPE_* */
120 const mbentry_t *mbentry; /* for _MAILBOX */
121 mbentry_t *ourmbentry;
122 struct mailbox *mailbox; /* for _MAILBOX, _MESSAGE */
123 struct mailbox *ourmailbox;
124 unsigned int uid; /* for _MESSAGE */
125 const char *acl; /* for _MESSAGE */
126 annotate_db_t *d;
127
128 /* authentication state */
129 const char *userid;
130 int isadmin;
131 const struct auth_state *auth_state;
132
133 struct annotate_entry_list *entry_list;
134 /* for proxies */
135 struct hash_table entry_table;
136 struct hash_table server_table;
137
138 /*
139 * Fetching.
140 */
141 unsigned attribs;
142 struct entryattlist **entryatts;
143 unsigned found;
144
145 /* For proxies (a null entry_list indicates that we ONLY proxy) */
146 /* if these are NULL, we have had a local exact match, and we
147 DO NOT proxy */
148 const char *orig_mailbox;
149 const strarray_t *orig_entry;
150 const strarray_t *orig_attribute;
151
152 /* state for output_entryatt */
153 struct attvaluelist *attvalues;
154 char lastname[MAX_MAILBOX_BUFFER]; /* internal */
155 char lastentry[MAX_MAILBOX_BUFFER];
156 uint32_t lastuid;
157 annotate_fetch_cb_t callback;
158 void *callback_rock;
159
160 /*
161 * Storing.
162 */
163 /* number of mailboxes matching the pattern */
164 unsigned count;
165 };
166
167 enum {
168 ATTRIB_VALUE_SHARED = (1<<0),
169 ATTRIB_VALUE_PRIV = (1<<1),
170 ATTRIB_SIZE_SHARED = (1<<2),
171 ATTRIB_SIZE_PRIV = (1<<3),
172 ATTRIB_DEPRECATED = (1<<4)
173 };
174
175 typedef enum {
176 ANNOTATION_PROXY_T_INVALID = 0,
177
178 PROXY_ONLY = 1,
179 BACKEND_ONLY = 2,
180 PROXY_AND_BACKEND = 3
181 } annotation_proxy_t;
182
183 enum {
184 ATTRIB_TYPE_STRING,
185 ATTRIB_TYPE_BOOLEAN,
186 ATTRIB_TYPE_UINT,
187 ATTRIB_TYPE_INT
188 };
189 #define ATTRIB_NO_FETCH_ACL_CHECK (1<<30)
190
191 struct annotate_entrydesc
192 {
193 const char *name; /* entry name */
194 int type; /* entry type */
195 annotation_proxy_t proxytype; /* mask of allowed server types */
196 int attribs; /* mask of allowed attributes */
197 int extra_rights; /* for set of shared mailbox annotations */
198 /* function to get the entry */
199 void (*get)(annotate_state_t *state,
200 struct annotate_entry_list *entry);
201 /* function to set the entry */
202 int (*set)(annotate_state_t *state,
203 struct annotate_entry_list *entry);
204 void *rock; /* rock passed to get() function */
205 };
206
207 struct annotate_db
208 {
209 annotate_db_t *next;
210 int refcount;
211 char *mboxname;
212 char *filename;
213 struct db *db;
214 struct txn *txn;
215 int in_txn;
216 };
217
218 #define DB config_annotation_db
219
220 static annotate_db_t *all_dbs_head = NULL;
221 static annotate_db_t *all_dbs_tail = NULL;
222 #define tid(d) ((d)->in_txn ? &(d)->txn : NULL)
223 static int (*proxy_fetch_func)(const char *server, const char *mbox_pat,
224 const strarray_t *entry_pat,
225 const strarray_t *attribute_pat) = NULL;
226 static int (*proxy_store_func)(const char *server, const char *mbox_pat,
227 struct entryattlist *entryatts) = NULL;
228 static ptrarray_t message_entries = PTRARRAY_INITIALIZER;
229 static ptrarray_t mailbox_entries = PTRARRAY_INITIALIZER;
230 static ptrarray_t server_entries = PTRARRAY_INITIALIZER;
231
232 static void annotate_state_unset_scope(annotate_state_t *state);
233 static int annotate_state_set_scope(annotate_state_t *state,
234 const mbentry_t *mbentry,
235 struct mailbox *mailbox,
236 unsigned int uid);
237 static void init_annotation_definitions(void);
238 static int annotation_set_tofile(annotate_state_t *state,
239 struct annotate_entry_list *entry);
240 static int annotation_set_todb(annotate_state_t *state,
241 struct annotate_entry_list *entry);
242 static int annotation_set_mailboxopt(annotate_state_t *state,
243 struct annotate_entry_list *entry);
244 static int annotation_set_pop3showafter(annotate_state_t *state,
245 struct annotate_entry_list *entry);
246 static int annotation_set_specialuse(annotate_state_t *state,
247 struct annotate_entry_list *entry);
248 static int _annotate_rewrite(struct mailbox *oldmailbox,
249 uint32_t olduid,
250 const char *olduserid,
251 struct mailbox *newmailbox,
252 uint32_t newuid,
253 const char *newuserid,
254 int copy);
255 static int _annotate_may_store(annotate_state_t *state,
256 int is_shared,
257 const annotate_entrydesc_t *desc);
258 static void annotate_begin(annotate_db_t *d);
259 static void annotate_abort(annotate_db_t *d);
260 static int annotate_commit(annotate_db_t *d);
261
262 /* String List Management */
263 /*
264 * Append 's' to the strlist 'l'.
265 */
appendstrlist(struct strlist ** l,char * s)266 EXPORTED void appendstrlist(struct strlist **l, char *s)
267 {
268 struct strlist **tail = l;
269
270 while (*tail) tail = &(*tail)->next;
271
272 *tail = (struct strlist *)xmalloc(sizeof(struct strlist));
273 (*tail)->s = xstrdup(s);
274 (*tail)->p = 0;
275 (*tail)->next = 0;
276 }
277
278 /*
279 * Append 's' to the strlist 'l', compiling it as a pattern.
280 * Caller must pass in memory that is freed when the strlist is freed.
281 */
appendstrlistpat(struct strlist ** l,char * s)282 EXPORTED void appendstrlistpat(struct strlist **l, char *s)
283 {
284 struct strlist **tail = l;
285
286 while (*tail) tail = &(*tail)->next;
287
288 *tail = (struct strlist *)xmalloc(sizeof(struct strlist));
289 (*tail)->s = s;
290 (*tail)->p = charset_compilepat(s);
291 (*tail)->next = 0;
292 }
293
294 /*
295 * Free the strlist 'l'
296 */
freestrlist(struct strlist * l)297 EXPORTED void freestrlist(struct strlist *l)
298 {
299 struct strlist *n;
300
301 while (l) {
302 n = l->next;
303 free(l->s);
304 if (l->p) charset_freepat(l->p);
305 free((char *)l);
306 l = n;
307 }
308 }
309
310 /* Attribute Management (also used by the ID command) */
311
312 /*
313 * Append the 'attrib'/'value' pair to the attvaluelist 'l'.
314 */
appendattvalue(struct attvaluelist ** l,const char * attrib,const struct buf * value)315 EXPORTED void appendattvalue(struct attvaluelist **l,
316 const char *attrib,
317 const struct buf *value)
318 {
319 struct attvaluelist **tail = l;
320
321 while (*tail) tail = &(*tail)->next;
322
323 *tail = xzmalloc(sizeof(struct attvaluelist));
324 (*tail)->attrib = xstrdup(attrib);
325 buf_copy(&(*tail)->value, value);
326 }
327
328 /*
329 * Duplicate the attvaluelist @src to @dst.
330 */
dupattvalues(struct attvaluelist ** dst,const struct attvaluelist * src)331 void dupattvalues(struct attvaluelist **dst,
332 const struct attvaluelist *src)
333 {
334 for ( ; src ; src = src->next)
335 appendattvalue(dst, src->attrib, &src->value);
336 }
337
338 /*
339 * Free the attvaluelist 'l'
340 */
freeattvalues(struct attvaluelist * l)341 EXPORTED void freeattvalues(struct attvaluelist *l)
342 {
343 struct attvaluelist *n;
344
345 while (l) {
346 n = l->next;
347 free(l->attrib);
348 buf_free(&l->value);
349 free(l);
350 l = n;
351 }
352 }
353
354 /*
355 * Append the 'entry'/'attvalues' pair to the entryattlist 'l'.
356 */
appendentryatt(struct entryattlist ** l,const char * entry,struct attvaluelist * attvalues)357 EXPORTED void appendentryatt(struct entryattlist **l, const char *entry,
358 struct attvaluelist *attvalues)
359 {
360 struct entryattlist **tail = l;
361
362 while (*tail) tail = &(*tail)->next;
363
364 *tail = (struct entryattlist *)xmalloc(sizeof(struct entryattlist));
365 (*tail)->entry = xstrdup(entry);
366 (*tail)->attvalues = attvalues;
367 (*tail)->next = NULL;
368 }
369
setentryatt(struct entryattlist ** l,const char * entry,const char * attrib,const struct buf * value)370 EXPORTED void setentryatt(struct entryattlist **l, const char *entry,
371 const char *attrib, const struct buf *value)
372 {
373 struct entryattlist *ee;
374
375 for (ee = *l ; ee ; ee = ee->next) {
376 if (!strcmp(ee->entry, entry))
377 break;
378 }
379
380 if (!ee) {
381 struct attvaluelist *atts = NULL;
382 appendattvalue(&atts, attrib, value);
383 appendentryatt(l, entry, atts);
384 }
385 else {
386 struct attvaluelist *av;
387 for (av = ee->attvalues ; av ; av = av->next) {
388 if (!strcmp(av->attrib, attrib))
389 break;
390 }
391 if (av)
392 buf_copy(&av->value, value);
393 else
394 appendattvalue(&ee->attvalues, attrib, value);
395 }
396 }
397
clearentryatt(struct entryattlist ** l,const char * entry,const char * attrib)398 EXPORTED void clearentryatt(struct entryattlist **l, const char *entry,
399 const char *attrib)
400 {
401 struct entryattlist *ea, **pea;
402 struct attvaluelist *av, **pav;
403
404 for (pea = l ; *pea ; pea = &(*pea)->next) {
405 if (!strcmp((*pea)->entry, entry))
406 break;
407 }
408 ea = *pea;
409 if (!ea)
410 return; /* entry not found */
411
412 for (pav = &(*pea)->attvalues ; *pav ; pav = &(*pav)->next) {
413 if (!strcmp((*pav)->attrib, attrib))
414 break;
415 }
416 av = *pav;
417 if (!av)
418 return; /* attrib not found */
419
420 /* detach and free attvaluelist */
421 *pav = av->next;
422 free(av->attrib);
423 buf_free(&av->value);
424 free(av);
425
426 if (!ea->attvalues) {
427 /* ->attvalues is now empty, so we can detach and free *pea too */
428 *pea = ea->next;
429 free(ea->entry);
430 free(ea);
431 }
432 }
433
434 /*
435 * Duplicate the entryattlist @src to @dst.
436 */
dupentryatt(struct entryattlist ** dst,const struct entryattlist * src)437 void dupentryatt(struct entryattlist **dst,
438 const struct entryattlist *src)
439 {
440 for ( ; src ; src = src->next) {
441 struct attvaluelist *attvalues = NULL;
442 dupattvalues(&attvalues, src->attvalues);
443 appendentryatt(dst, src->entry, attvalues);
444 }
445 }
446
447 /*
448 * Count the storage used by entryattlist 'l'
449 */
sizeentryatts(const struct entryattlist * l)450 EXPORTED size_t sizeentryatts(const struct entryattlist *l)
451 {
452 size_t sz = 0;
453 struct attvaluelist *av;
454
455 for ( ; l ; l = l->next)
456 for (av = l->attvalues ; av ; av = av->next)
457 sz += av->value.len;
458 return sz;
459 }
460
461 /*
462 * Free the entryattlist 'l'
463 */
freeentryatts(struct entryattlist * l)464 EXPORTED void freeentryatts(struct entryattlist *l)
465 {
466 struct entryattlist *n;
467
468 while (l) {
469 n = l->next;
470 free(l->entry);
471 freeattvalues(l->attvalues);
472 free(l);
473 l = n;
474 }
475 }
476
477 /* must be called after cyrus_init */
annotate_init(int (* fetch_func)(const char *,const char *,const strarray_t *,const strarray_t *),int (* store_func)(const char *,const char *,struct entryattlist *))478 EXPORTED void annotate_init(int (*fetch_func)(const char *, const char *,
479 const strarray_t *, const strarray_t *),
480 int (*store_func)(const char *, const char *,
481 struct entryattlist *))
482 {
483 if (fetch_func) {
484 proxy_fetch_func = fetch_func;
485 }
486 if (store_func) {
487 proxy_store_func = store_func;
488 }
489
490 init_annotation_definitions();
491 }
492
493 /* detach the db_t from the global list */
detach_db(annotate_db_t * prev,annotate_db_t * d)494 static void detach_db(annotate_db_t *prev, annotate_db_t *d)
495 {
496 if (prev)
497 prev->next = d->next;
498 else
499 all_dbs_head = d->next;
500 if (all_dbs_tail == d)
501 all_dbs_tail = prev;
502 }
503
504 /* append the db_t to the global list */
append_db(annotate_db_t * d)505 static void append_db(annotate_db_t *d)
506 {
507 if (all_dbs_tail)
508 all_dbs_tail->next = d;
509 else
510 all_dbs_head = d;
511 all_dbs_tail = d;
512 d->next = NULL;
513 }
514
515 /*
516 * Generate a new string containing the db filename
517 * for the given @mboxname, (or the global db if
518 * @mboxname is NULL). Returns the new string in
519 * *@fnamep. Returns an error code.
520 */
annotate_dbname_mbentry(const mbentry_t * mbentry,char ** fnamep)521 static int annotate_dbname_mbentry(const mbentry_t *mbentry,
522 char **fnamep)
523 {
524 const char *conf_fname;
525
526 if (mbentry) {
527 /* per-mbox database */
528 conf_fname = mbentry_metapath(mbentry, META_ANNOTATIONS, /*isnew*/0);
529 if (!conf_fname)
530 return IMAP_MAILBOX_BADNAME;
531 *fnamep = xstrdup(conf_fname);
532 }
533 else {
534 /* global database */
535 conf_fname = config_getstring(IMAPOPT_ANNOTATION_DB_PATH);
536
537 if (conf_fname)
538 *fnamep = xstrdup(conf_fname);
539 else
540 *fnamep = strconcat(config_dir, FNAME_GLOBALANNOTATIONS, (char *)NULL);
541 }
542
543 return 0;
544 }
545
annotate_dbname_mailbox(struct mailbox * mailbox,char ** fnamep)546 static int annotate_dbname_mailbox(struct mailbox *mailbox, char **fnamep)
547 {
548 const char *conf_fname;
549
550 if (!mailbox) return annotate_dbname_mbentry(NULL, fnamep);
551
552 conf_fname = mailbox_meta_fname(mailbox, META_ANNOTATIONS);
553 if (!conf_fname) return IMAP_MAILBOX_BADNAME;
554 *fnamep = xstrdup(conf_fname);
555
556 return 0;
557 }
558
559
annotate_dbname(const char * mboxname,char ** fnamep)560 static int annotate_dbname(const char *mboxname, char **fnamep)
561 {
562 int r = 0;
563 mbentry_t *mbentry = NULL;
564
565 if (mboxname) {
566 r = mboxlist_lookup(mboxname, &mbentry, NULL);
567 if (r) goto out;
568 }
569
570 r = annotate_dbname_mbentry(mbentry, fnamep);
571
572 out:
573 mboxlist_entry_free(&mbentry);
574 return r;
575 }
576
_annotate_getdb(const char * mboxname,unsigned int uid,int dbflags,annotate_db_t ** dbp)577 static int _annotate_getdb(const char *mboxname,
578 unsigned int uid,
579 int dbflags,
580 annotate_db_t **dbp)
581 {
582 annotate_db_t *d, *prev = NULL;
583 char *fname = NULL;
584 struct db *db;
585 int r;
586
587 *dbp = NULL;
588
589 /*
590 * The incoming (mboxname,uid) tuple tells us which scope we
591 * need a database for. Translate into the mboxname used to
592 * key annotate_db_t's, which is slightly different: message
593 * scope goes into a per-mailbox db, others in the global db.
594 */
595 if (!strcmpsafe(mboxname, NULL) /*server scope*/ ||
596 !uid /* mailbox scope*/)
597 mboxname = NULL;
598
599 /* try to find an existing db for the mbox */
600 for (d = all_dbs_head ; d ; prev = d, d = d->next) {
601 if (!strcmpsafe(mboxname, d->mboxname)) {
602 /* found it, bump the refcount */
603 d->refcount++;
604 *dbp = d;
605 /*
606 * Splay the db_t to the end of the global list.
607 * This ensures the list remains in getdb() call
608 * order, and in particular that the dbs are
609 * committed in getdb() call order. This is
610 * necessary to ensure safety should a commit fail
611 * while moving annotations between per-mailbox dbs
612 */
613 detach_db(prev, d);
614 append_db(d);
615 return 0;
616 }
617 }
618 /* not found, open/create a new one */
619
620 r = annotate_dbname(mboxname, &fname);
621 if (r)
622 goto error;
623 #if DEBUG
624 syslog(LOG_ERR, "Opening annotations db %s\n", fname);
625 #endif
626
627 r = cyrusdb_open(DB, fname, dbflags | CYRUSDB_CONVERT, &db);
628 if (r != 0) {
629 if (!(dbflags & CYRUSDB_CREATE) && r == CYRUSDB_NOTFOUND)
630 goto error;
631 syslog(LOG_ERR, "DBERROR: opening %s: %s",
632 fname, cyrusdb_strerror(r));
633 goto error;
634 }
635
636 /* record all the above */
637 d = xzmalloc(sizeof(*d));
638 d->refcount = 1;
639 d->mboxname = xstrdupnull(mboxname);
640 d->filename = fname;
641 d->db = db;
642
643 append_db(d);
644
645 *dbp = d;
646 return 0;
647
648 error:
649 free(fname);
650 *dbp = NULL;
651 return r;
652 }
653
annotate_getdb(const char * mboxname,annotate_db_t ** dbp)654 HIDDEN int annotate_getdb(const char *mboxname, annotate_db_t **dbp)
655 {
656 if (!mboxname || !*mboxname) {
657 syslog(LOG_ERR, "IOERROR: annotate_getdb called with no mboxname");
658 return IMAP_INTERNAL; /* we don't return the global db */
659 }
660 /* synthetic UID '1' forces per-mailbox mode */
661 return _annotate_getdb(mboxname, 1, CYRUSDB_CREATE, dbp);
662 }
663
annotate_closedb(annotate_db_t * d)664 static void annotate_closedb(annotate_db_t *d)
665 {
666 annotate_db_t *dx, *prev = NULL;
667 int r;
668
669 /* detach from the global list */
670 for (dx = all_dbs_head ; dx && dx != d ; prev = dx, dx = dx->next)
671 ;
672 assert(dx);
673 assert(d == dx);
674 detach_db(prev, d);
675
676 #if DEBUG
677 syslog(LOG_ERR, "Closing annotations db %s\n", d->filename);
678 #endif
679
680 r = cyrusdb_close(d->db);
681 if (r)
682 syslog(LOG_ERR, "DBERROR: error closing annotations %s: %s",
683 d->filename, cyrusdb_strerror(r));
684
685 free(d->filename);
686 free(d->mboxname);
687 memset(d, 0, sizeof(*d)); /* JIC */
688 free(d);
689 }
690
annotate_putdb(annotate_db_t ** dbp)691 HIDDEN void annotate_putdb(annotate_db_t **dbp)
692 {
693 annotate_db_t *d;
694
695 if (!dbp || !(d = *dbp))
696 return;
697 assert(d->refcount > 0);
698 if (--d->refcount == 0) {
699 if (d->in_txn && d->txn) {
700 syslog(LOG_ERR, "IOERROR: dropped last reference on "
701 "database %s with uncommitted updates, "
702 "aborting - DATA LOST!",
703 d->filename);
704 annotate_abort(d);
705 }
706 assert(!d->in_txn);
707 annotate_closedb(d);
708 }
709 *dbp = NULL;
710 }
711
annotatemore_open(void)712 EXPORTED void annotatemore_open(void)
713 {
714 int r;
715 annotate_db_t *d = NULL;
716
717 /* force opening the global annotations db */
718 r = _annotate_getdb(NULL, 0, CYRUSDB_CREATE, &d);
719 if (r)
720 fatal("can't open global annotations database", EC_TEMPFAIL);
721 }
722
annotatemore_close(void)723 EXPORTED void annotatemore_close(void)
724 {
725 /* close all the open databases */
726 while (all_dbs_head)
727 annotate_closedb(all_dbs_head);
728 }
729
730 /* Begin a txn if one is not already started. Can be called multiple
731 * times */
annotate_begin(annotate_db_t * d)732 static void annotate_begin(annotate_db_t *d)
733 {
734 if (d)
735 d->in_txn = 1;
736 }
737
annotate_abort(annotate_db_t * d)738 static void annotate_abort(annotate_db_t *d)
739 {
740 /* don't double-abort */
741 if (!d || !d->in_txn) return;
742
743 if (d->txn) {
744 #if DEBUG
745 syslog(LOG_ERR, "Aborting annotations db %s\n", d->filename);
746 #endif
747 cyrusdb_abort(d->db, d->txn);
748 }
749 d->txn = NULL;
750 d->in_txn = 0;
751 }
752
annotate_commit(annotate_db_t * d)753 static int annotate_commit(annotate_db_t *d)
754 {
755 int r = 0;
756
757 /* silently succeed if not in a txn */
758 if (!d || !d->in_txn) return 0;
759
760 if (d->txn) {
761 #if DEBUG
762 syslog(LOG_ERR, "Committing annotations db %s\n", d->filename);
763 #endif
764 r = cyrusdb_commit(d->db, d->txn);
765 if (r)
766 r = IMAP_IOERROR;
767 d->txn = NULL;
768 }
769 d->in_txn = 0;
770
771 return r;
772 }
773
annotate_done(void)774 EXPORTED void annotate_done(void)
775 {
776 /* DB->done() handled by cyrus_done() */
777 }
778
make_key(const char * mboxname,unsigned int uid,const char * entry,const char * userid,char * key,size_t keysize)779 static int make_key(const char *mboxname,
780 unsigned int uid,
781 const char *entry,
782 const char *userid,
783 char *key, size_t keysize)
784 {
785 int keylen = 0;
786
787 if (!uid) {
788 strlcpy(key+keylen, mboxname, keysize-keylen);
789 keylen += strlen(mboxname) + 1;
790 }
791 else if (uid == ANNOTATE_ANY_UID) {
792 strlcpy(key+keylen, "*", keysize-keylen);
793 keylen += strlen(key+keylen) + 1;
794 }
795 else {
796 snprintf(key+keylen, keysize-keylen, "%u", uid);
797 keylen += strlen(key+keylen) + 1;
798 }
799 strlcpy(key+keylen, entry, keysize-keylen);
800 keylen += strlen(entry);
801 /* if we don't have a userid, we're doing a foreach() */
802 if (userid) {
803 keylen++;
804 strlcpy(key+keylen, userid, keysize-keylen);
805 keylen += strlen(userid) + 1;
806 }
807
808 return keylen;
809 }
810
split_key(const annotate_db_t * d,const char * key,int keysize,const char ** mboxnamep,unsigned int * uidp,const char ** entryp,const char ** useridp)811 static int split_key(const annotate_db_t *d,
812 const char *key, int keysize,
813 const char **mboxnamep,
814 unsigned int *uidp,
815 const char **entryp,
816 const char **useridp)
817 {
818 static struct buf keybuf;
819 const char *p;
820 const char *end;
821
822 buf_setmap(&keybuf, key, keysize);
823 buf_putc(&keybuf, '\0'); /* safety tricks due to broken FM code */
824 p = buf_cstring(&keybuf);
825 end = p + keysize;
826
827 /*
828 * paranoia: split the key into fields on NUL characters.
829 * We would use strarray_nsplit() for this, except that
830 * by design that function cannot split on NULs and does
831 * not handle embedded NULs.
832 */
833
834 if (d->mboxname) {
835 *mboxnamep = d->mboxname;
836 *uidp = 0;
837 while (*p && p < end) *uidp = (10 * (*uidp)) + (*p++ - '0');
838 if (p < end) p++;
839 else return IMAP_ANNOTATION_BADENTRY;
840 }
841 else {
842 /* global db for mailnbox & server scope annotations */
843 *uidp = 0;
844 *mboxnamep = p;
845 while (*p && p < end) p++;
846 if (p < end) p++;
847 else return IMAP_ANNOTATION_BADENTRY;
848 }
849
850 *entryp = p; /* XXX: trailing NULLs on non-userid keys? Bogus just at FM */
851 while (*p && p < end) p++;
852 if (p < end && !*p)
853 *useridp = p+1;
854 else
855 *useridp = NULL;
856 return 0;
857 }
858
859 #if DEBUG
key_as_string(const annotate_db_t * d,const char * key,int keylen)860 static const char *key_as_string(const annotate_db_t *d,
861 const char *key, int keylen)
862 {
863 const char *mboxname, *entry, *userid;
864 unsigned int uid;
865 int r;
866 static struct buf buf = BUF_INITIALIZER;
867
868 buf_reset(&buf);
869 r = split_key(d, key, keylen, &mboxname, &uid, &entry, &userid);
870 if (r)
871 buf_appendcstr(&buf, "invalid");
872 else
873 buf_printf(&buf, "{ mboxname=\"%s\" uid=%u entry=\"%s\" userid=\"%s\" }",
874 mboxname, uid, entry, userid);
875 return buf_cstring(&buf);
876 }
877 #endif
878
split_attribs(const char * data,int datalen,struct buf * value)879 static int split_attribs(const char *data, int datalen __attribute__((unused)),
880 struct buf *value)
881 {
882 unsigned long tmp; /* for alignment */
883
884 /* xxx use datalen? */
885 /* xxx sanity check the data? */
886 /*
887 * Sigh...this is dumb. We take care to be machine independent by
888 * storing the length in network byte order...but the size of the
889 * length field depends on whether we're running on a 32b or 64b
890 * platform.
891 */
892 memcpy(&tmp, data, sizeof(unsigned long));
893 data += sizeof(unsigned long); /* skip to value */
894
895 buf_init_ro(value, data, ntohl(tmp));
896
897 /*
898 * In records written by older versions of Cyrus, there will be
899 * binary encoded content-type and modifiedsince values after the
900 * data. We don't care about those anymore, so we just ignore them.
901 */
902 return 0;
903 }
904
905 struct find_rock {
906 struct glob *mglob;
907 struct glob *eglob;
908 unsigned int uid;
909 annotate_db_t *d;
910 annotatemore_find_proc_t proc;
911 void *rock;
912 };
913
find_p(void * rock,const char * key,size_t keylen,const char * data,size_t datalen)914 static int find_p(void *rock, const char *key, size_t keylen,
915 const char *data __attribute__((unused)),
916 size_t datalen __attribute__((unused)))
917 {
918 struct find_rock *frock = (struct find_rock *) rock;
919 const char *mboxname, *entry, *userid;
920 unsigned int uid;
921 int r;
922
923 r = split_key(frock->d, key, keylen, &mboxname,
924 &uid, &entry, &userid);
925 if (r < 0)
926 return 0;
927
928 if (frock->uid &&
929 frock->uid != ANNOTATE_ANY_UID &&
930 frock->uid != uid)
931 return 0;
932 if (!GLOB_MATCH(frock->mglob, mboxname))
933 return 0;
934 if (!GLOB_MATCH(frock->eglob, entry))
935 return 0;
936 return 1;
937 }
938
find_cb(void * rock,const char * key,size_t keylen,const char * data,size_t datalen)939 static int find_cb(void *rock, const char *key, size_t keylen,
940 const char *data, size_t datalen)
941 {
942 struct find_rock *frock = (struct find_rock *) rock;
943 const char *mboxname, *entry, *userid;
944 unsigned int uid;
945 char newkey[MAX_MAILBOX_NAME+1];
946 size_t newkeylen;
947 struct buf value = BUF_INITIALIZER;
948 int r;
949
950 assert(keylen < MAX_MAILBOX_PATH);
951
952 #if DEBUG
953 syslog(LOG_ERR, "find_cb: found key %s in %s",
954 key_as_string(frock->d, key, keylen), frock->d->filename);
955 #endif
956
957 r = split_key(frock->d, key, keylen, &mboxname,
958 &uid, &entry, &userid);
959 if (r)
960 return r;
961
962 newkeylen = make_key(mboxname, uid, entry, userid, newkey, sizeof(newkey));
963 if (keylen != newkeylen || strncmp(newkey, key, keylen)) {
964 syslog(LOG_ERR, "find_cb: bogus key %s %d %s %s (%d %d)", mboxname, uid, entry, userid, (int)keylen, (int)newkeylen);
965 }
966
967 r = split_attribs(data, datalen, &value);
968
969 if (!r) r = frock->proc(mboxname, uid, entry, userid, &value, frock->rock);
970
971 buf_free(&value);
972
973 return r;
974 }
975
annotatemore_findall(const char * mboxname,unsigned int uid,const char * entry,annotatemore_find_proc_t proc,void * rock)976 EXPORTED int annotatemore_findall(const char *mboxname, /* internal */
977 unsigned int uid,
978 const char *entry,
979 annotatemore_find_proc_t proc,
980 void *rock)
981 {
982 char key[MAX_MAILBOX_PATH+1], *p;
983 size_t keylen;
984 int r;
985 struct find_rock frock;
986
987 assert(mboxname);
988 assert(entry);
989 frock.mglob = glob_init(mboxname, '.');
990 frock.eglob = glob_init(entry, '/');
991 frock.uid = uid;
992 frock.proc = proc;
993 frock.rock = rock;
994 r = _annotate_getdb(mboxname, uid, 0, &frock.d);
995 if (r) {
996 if (r == CYRUSDB_NOTFOUND)
997 r = 0;
998 goto out;
999 }
1000
1001 /* Find fixed-string pattern prefix */
1002 keylen = make_key(mboxname, uid,
1003 entry, NULL, key, sizeof(key));
1004
1005 for (p = key; keylen; p++, keylen--) {
1006 if (*p == '*' || *p == '%') break;
1007 }
1008 keylen = p - key;
1009
1010 r = cyrusdb_foreach(frock.d->db, key, keylen, &find_p, &find_cb,
1011 &frock, tid(frock.d));
1012
1013 out:
1014 glob_free(&frock.mglob);
1015 glob_free(&frock.eglob);
1016 annotate_putdb(&frock.d);
1017
1018 return r;
1019 }
1020
1021 /*************************** Annotate State Management ***************************/
1022
annotate_state_new(void)1023 EXPORTED annotate_state_t *annotate_state_new(void)
1024 {
1025 annotate_state_t *state;
1026
1027 state = xzmalloc(sizeof(*state));
1028 state->which = ANNOTATION_SCOPE_UNKNOWN;
1029
1030 return state;
1031 }
1032
annotate_state_start(annotate_state_t * state)1033 static void annotate_state_start(annotate_state_t *state)
1034 {
1035 /* xxx better way to determine a size for this table? */
1036 construct_hash_table(&state->entry_table, 100, 1);
1037 construct_hash_table(&state->server_table, 10, 1);
1038 }
1039
annotate_state_finish(annotate_state_t * state)1040 static void annotate_state_finish(annotate_state_t *state)
1041 {
1042 /* Free the entry list */
1043 while (state->entry_list) {
1044 struct annotate_entry_list *ee = state->entry_list;
1045 state->entry_list = ee->next;
1046 buf_free(&ee->shared);
1047 buf_free(&ee->priv);
1048 free(ee->name);
1049 free(ee);
1050 }
1051
1052 free_hash_table(&state->entry_table, NULL);
1053 free_hash_table(&state->server_table, NULL);
1054 }
1055
1056
annotate_state_free(annotate_state_t ** statep)1057 static void annotate_state_free(annotate_state_t **statep)
1058 {
1059 annotate_state_t *state = *statep;
1060
1061 if (!state)
1062 return;
1063
1064 annotate_state_finish(state);
1065 annotate_state_unset_scope(state);
1066 free(state);
1067 *statep = NULL;
1068 }
1069
annotate_state_begin(annotate_state_t * state)1070 EXPORTED void annotate_state_begin(annotate_state_t *state)
1071 {
1072 annotate_begin(state->d);
1073 }
1074
annotate_state_abort(annotate_state_t ** statep)1075 EXPORTED void annotate_state_abort(annotate_state_t **statep)
1076 {
1077 if (*statep)
1078 annotate_abort((*statep)->d);
1079
1080 annotate_state_free(statep);
1081 }
1082
annotate_state_commit(annotate_state_t ** statep)1083 EXPORTED int annotate_state_commit(annotate_state_t **statep)
1084 {
1085 int r = 0;
1086 if (*statep)
1087 r = annotate_commit((*statep)->d);
1088
1089 annotate_state_free(statep);
1090 return r;
1091 }
1092
1093
1094 static struct annotate_entry_list *
_annotate_state_add_entry(annotate_state_t * state,const annotate_entrydesc_t * desc,const char * name)1095 _annotate_state_add_entry(annotate_state_t *state,
1096 const annotate_entrydesc_t *desc,
1097 const char *name)
1098 {
1099 struct annotate_entry_list *ee;
1100
1101 ee = xzmalloc(sizeof(*ee));
1102 ee->name = xstrdup(name);
1103 ee->desc = desc;
1104
1105 ee->next = state->entry_list;
1106 state->entry_list = ee;
1107
1108 return ee;
1109 }
1110
annotate_state_set_auth(annotate_state_t * state,int isadmin,const char * userid,const struct auth_state * auth_state)1111 EXPORTED void annotate_state_set_auth(annotate_state_t *state,
1112 int isadmin, const char *userid,
1113 const struct auth_state *auth_state)
1114 {
1115 /* Note: lmtpd sometimes calls through the append code with
1116 * auth_state=NULL, so we cannot rely on it being non-NULL */
1117 state->userid = userid;
1118 state->isadmin = isadmin;
1119 state->auth_state = auth_state;
1120 }
1121
annotate_state_set_server(annotate_state_t * state)1122 EXPORTED int annotate_state_set_server(annotate_state_t *state)
1123 {
1124 return annotate_state_set_scope(state, NULL, NULL, 0);
1125 }
1126
annotate_state_set_mailbox(annotate_state_t * state,struct mailbox * mailbox)1127 EXPORTED int annotate_state_set_mailbox(annotate_state_t *state,
1128 struct mailbox *mailbox)
1129 {
1130 return annotate_state_set_scope(state, NULL, mailbox, 0);
1131 }
1132
annotate_state_set_mailbox_mbe(annotate_state_t * state,const mbentry_t * mbentry)1133 EXPORTED int annotate_state_set_mailbox_mbe(annotate_state_t *state,
1134 const mbentry_t *mbentry)
1135 {
1136 return annotate_state_set_scope(state, mbentry, NULL, 0);
1137 }
1138
annotate_state_set_message(annotate_state_t * state,struct mailbox * mailbox,unsigned int uid)1139 HIDDEN int annotate_state_set_message(annotate_state_t *state,
1140 struct mailbox *mailbox,
1141 unsigned int uid)
1142 {
1143 return annotate_state_set_scope(state, NULL, mailbox, uid);
1144 }
1145
1146 /* unset any state from a previous scope */
annotate_state_unset_scope(annotate_state_t * state)1147 static void annotate_state_unset_scope(annotate_state_t *state)
1148 {
1149 if (state->ourmailbox)
1150 mailbox_close(&state->ourmailbox);
1151 state->mailbox = NULL;
1152
1153 if (state->ourmbentry)
1154 mboxlist_entry_free(&state->ourmbentry);
1155 state->mbentry = NULL;
1156
1157 state->uid = 0;
1158 state->which = ANNOTATION_SCOPE_UNKNOWN;
1159 annotate_putdb(&state->d);
1160 }
1161
annotate_state_set_scope(annotate_state_t * state,const mbentry_t * mbentry,struct mailbox * mailbox,unsigned int uid)1162 static int annotate_state_set_scope(annotate_state_t *state,
1163 const mbentry_t *mbentry,
1164 struct mailbox *mailbox,
1165 unsigned int uid)
1166 {
1167 int r = 0;
1168 annotate_db_t *oldd = NULL;
1169 int oldwhich = state->which;
1170
1171 /* Carefully preserve the reference on the old DB just in case it
1172 * turns out to be the same as the new DB, so we avoid the overhead
1173 * of an unnecessary cyrusdb_open/close pair. */
1174 oldd = state->d;
1175 state->d = NULL;
1176
1177 annotate_state_unset_scope(state);
1178
1179 if (mbentry) {
1180 assert(!mailbox);
1181 assert(!uid);
1182 if (!mbentry->server) {
1183 /* local mailbox */
1184 r = mailbox_open_iwl(mbentry->name, &mailbox);
1185 if (r)
1186 goto out;
1187 state->ourmailbox = mailbox;
1188 }
1189 state->mbentry = mbentry;
1190 state->which = ANNOTATION_SCOPE_MAILBOX;
1191 }
1192
1193 else if (uid) {
1194 assert(mailbox);
1195 state->which = ANNOTATION_SCOPE_MESSAGE;
1196 }
1197 else if (mailbox) {
1198 assert(!uid);
1199 state->which = ANNOTATION_SCOPE_MAILBOX;
1200 }
1201 else {
1202 assert(!mailbox);
1203 assert(!uid);
1204 state->which = ANNOTATION_SCOPE_SERVER;
1205 }
1206 assert(oldwhich == ANNOTATION_SCOPE_UNKNOWN ||
1207 oldwhich == state->which);
1208 state->mailbox = mailbox;
1209 state->uid = uid;
1210
1211 r = _annotate_getdb(mailbox ? mailbox->name : NULL, uid,
1212 CYRUSDB_CREATE, &state->d);
1213
1214 out:
1215 annotate_putdb(&oldd);
1216 return r;
1217 }
1218
annotate_state_need_mbentry(annotate_state_t * state)1219 static int annotate_state_need_mbentry(annotate_state_t *state)
1220 {
1221 int r = 0;
1222
1223 if (!state->mbentry && state->mailbox) {
1224 r = mboxlist_lookup(state->mailbox->name, &state->ourmbentry, NULL);
1225 if (r) {
1226 syslog(LOG_ERR, "Failed to lookup mbentry for %s: %s",
1227 state->mailbox->name, error_message(r));
1228 goto out;
1229 }
1230 state->mbentry = state->ourmbentry;
1231 }
1232
1233 out:
1234 return r;
1235 }
1236
1237 /*************************** Annotation Fetching ***************************/
1238
flush_entryatt(annotate_state_t * state)1239 static void flush_entryatt(annotate_state_t *state)
1240 {
1241 if (!state->attvalues)
1242 return; /* nothing to flush */
1243
1244 state->callback(state->lastname,
1245 state->lastuid,
1246 state->lastentry,
1247 state->attvalues,
1248 state->callback_rock);
1249 freeattvalues(state->attvalues);
1250 state->attvalues = NULL;
1251 }
1252
1253 /* Output a single entry and attributes for a single mailbox.
1254 * Shared and private annotations are output together by caching
1255 * the attributes until the mailbox and/or entry changes.
1256 *
1257 * The cache is reset by calling with a NULL mboxname or entry.
1258 * The last entry is flushed by calling with a NULL attrib.
1259 */
output_entryatt(annotate_state_t * state,const char * entry,const char * userid,const struct buf * value)1260 static void output_entryatt(annotate_state_t *state, const char *entry,
1261 const char *userid, const struct buf *value)
1262 {
1263 const char *mboxname;
1264 char key[MAX_MAILBOX_BUFFER]; /* XXX MAX_MAILBOX_NAME + entry + userid */
1265 struct buf buf = BUF_INITIALIZER;
1266
1267 if (!userid) userid = "";
1268
1269 /* We don't put any funny interpretations on NULL values for
1270 * some of these anymore, now that the dirty hacks are gone. */
1271 assert(state);
1272 assert(entry);
1273 assert(userid);
1274 assert(value);
1275
1276 if (state->mailbox)
1277 mboxname = state->mailbox->name;
1278 else if (state->mbentry)
1279 mboxname = state->mbentry->name;
1280 else
1281 mboxname = "";
1282 /* @mboxname is now an internal mailbox name */
1283
1284 /* Check if this is a new entry.
1285 * If so, flush our current entry.
1286 */
1287 if (state->uid != state->lastuid ||
1288 strcmp(mboxname, state->lastname) ||
1289 strcmp(entry, state->lastentry))
1290 flush_entryatt(state);
1291
1292 strlcpy(state->lastname, mboxname, sizeof(state->lastname));
1293 strlcpy(state->lastentry, entry, sizeof(state->lastentry));
1294 state->lastuid = state->uid;
1295
1296 /* check if we already returned this entry */
1297 strlcpy(key, mboxname, sizeof(key));
1298 if (state->uid) {
1299 char uidbuf[32];
1300 snprintf(uidbuf, sizeof(uidbuf), "/UID%u/", state->uid);
1301 strlcat(key, uidbuf, sizeof(key));
1302 }
1303 strlcat(key, entry, sizeof(key));
1304 strlcat(key, userid, sizeof(key));
1305 if (hash_lookup(key, &state->entry_table)) return;
1306 hash_insert(key, (void *)0xDEADBEEF, &state->entry_table);
1307
1308 if (!userid[0]) { /* shared annotation */
1309 if ((state->attribs & ATTRIB_VALUE_SHARED)) {
1310 appendattvalue(&state->attvalues, "value.shared", value);
1311 state->found |= ATTRIB_VALUE_SHARED;
1312 }
1313
1314 if ((state->attribs & ATTRIB_SIZE_SHARED)) {
1315 buf_reset(&buf);
1316 buf_printf(&buf, SIZE_T_FMT, value->len);
1317 appendattvalue(&state->attvalues, "size.shared", &buf);
1318 state->found |= ATTRIB_SIZE_SHARED;
1319 }
1320 }
1321 else { /* private annotation */
1322 if ((state->attribs & ATTRIB_VALUE_PRIV)) {
1323 appendattvalue(&state->attvalues, "value.priv", value);
1324 state->found |= ATTRIB_VALUE_PRIV;
1325 }
1326
1327 if ((state->attribs & ATTRIB_SIZE_PRIV)) {
1328 buf_reset(&buf);
1329 buf_printf(&buf, SIZE_T_FMT, value->len);
1330 appendattvalue(&state->attvalues, "size.priv", &buf);
1331 state->found |= ATTRIB_SIZE_PRIV;
1332 }
1333 }
1334 buf_free(&buf);
1335 }
1336
1337 /* Note that unlike store access control, fetch access control
1338 * is identical between shared and private annotations */
_annotate_may_fetch(annotate_state_t * state,const annotate_entrydesc_t * desc)1339 static int _annotate_may_fetch(annotate_state_t *state,
1340 const annotate_entrydesc_t *desc)
1341 {
1342 unsigned int my_rights;
1343 unsigned int needed = 0;
1344 const char *acl = NULL;
1345
1346 /* Admins can do anything */
1347 if (state->isadmin)
1348 return 1;
1349
1350 /* Some entries need to do their own access control */
1351 if ((desc->type & ATTRIB_NO_FETCH_ACL_CHECK))
1352 return 1;
1353
1354 if (state->which == ANNOTATION_SCOPE_SERVER) {
1355 /* RFC5464 doesn't mention access control for server
1356 * annotations, but this seems a sensible practice and is
1357 * consistent with past Cyrus behaviour */
1358 return 1;
1359 }
1360 else if (state->which == ANNOTATION_SCOPE_MAILBOX) {
1361 assert(state->mailbox || state->mbentry);
1362
1363 /* Make sure its a local mailbox annotation */
1364 if (state->mbentry && state->mbentry->server)
1365 return 0;
1366
1367 if (state->mailbox) acl = state->mailbox->acl;
1368 else if (state->mbentry) acl = state->mbentry->acl;
1369 /* RFC5464 is a trifle vague about access control for mailbox
1370 * annotations but this seems to be compliant */
1371 needed = ACL_LOOKUP|ACL_READ;
1372 /* fall through to ACL check */
1373 }
1374 else if (state->which == ANNOTATION_SCOPE_MESSAGE) {
1375 assert(state->mailbox);
1376 acl = state->mailbox->acl;
1377 /* RFC5257: reading from a private annotation needs 'r'.
1378 * Reading from a shared annotation needs 'r' */
1379 needed = ACL_READ;
1380 /* fall through to ACL check */
1381 }
1382
1383 if (!acl)
1384 return 0;
1385
1386 my_rights = cyrus_acl_myrights(state->auth_state, acl);
1387
1388 return ((my_rights & needed) == needed);
1389 }
1390
annotation_get_fromfile(annotate_state_t * state,struct annotate_entry_list * entry)1391 static void annotation_get_fromfile(annotate_state_t *state,
1392 struct annotate_entry_list *entry)
1393 {
1394 const char *filename = (const char *) entry->desc->rock;
1395 char path[MAX_MAILBOX_PATH+1];
1396 struct buf value = BUF_INITIALIZER;
1397 FILE *f;
1398
1399 snprintf(path, sizeof(path), "%s/msg/%s", config_dir, filename);
1400 if ((f = fopen(path, "r")) && buf_getline(&value, f)) {
1401
1402 /* TODO: we need a buf_chomp() */
1403 if (value.s[value.len-1] == '\r')
1404 buf_truncate(&value, value.len-1);
1405 }
1406 if (f) fclose(f);
1407 output_entryatt(state, entry->name, "", &value);
1408 buf_free(&value);
1409 }
1410
annotation_get_freespace(annotate_state_t * state,struct annotate_entry_list * entry)1411 static void annotation_get_freespace(annotate_state_t *state,
1412 struct annotate_entry_list *entry)
1413 {
1414 uint64_t tavail = 0;
1415 struct buf value = BUF_INITIALIZER;
1416
1417 (void) partlist_local_find_freespace_most(0, NULL, NULL, &tavail, NULL);
1418 buf_printf(&value, "%" PRIuMAX, (uintmax_t)tavail);
1419 output_entryatt(state, entry->name, "", &value);
1420 buf_free(&value);
1421 }
1422
annotation_get_freespace_total(annotate_state_t * state,struct annotate_entry_list * entry)1423 static void annotation_get_freespace_total(annotate_state_t *state,
1424 struct annotate_entry_list *entry)
1425 {
1426 uint64_t tavail = 0;
1427 uint64_t ttotal = 0;
1428 struct buf value = BUF_INITIALIZER;
1429
1430 (void) partlist_local_find_freespace_most(0, NULL, NULL, &tavail, &ttotal);
1431 buf_printf(&value, "%" PRIuMAX ";%" PRIuMAX, (uintmax_t)tavail, (uintmax_t)ttotal);
1432 output_entryatt(state, entry->name, "", &value);
1433 buf_free(&value);
1434 }
1435
annotation_get_freespace_percent_most(annotate_state_t * state,struct annotate_entry_list * entry)1436 static void annotation_get_freespace_percent_most(annotate_state_t *state,
1437 struct annotate_entry_list *entry)
1438 {
1439 uint64_t avail = 0;
1440 uint64_t total = 0;
1441 struct buf value = BUF_INITIALIZER;
1442
1443 (void) partlist_local_find_freespace_most(1, &avail, &total, NULL, NULL);
1444 buf_printf(&value, "%" PRIuMAX ";%" PRIuMAX, (uintmax_t)avail, (uintmax_t)total);
1445 output_entryatt(state, entry->name, "", &value);
1446 buf_free(&value);
1447 }
1448
annotation_get_server(annotate_state_t * state,struct annotate_entry_list * entry)1449 static void annotation_get_server(annotate_state_t *state,
1450 struct annotate_entry_list *entry)
1451 {
1452 struct buf value = BUF_INITIALIZER;
1453 int r;
1454
1455 assert(state);
1456 assert(state->which == ANNOTATION_SCOPE_MAILBOX);
1457 r = annotate_state_need_mbentry(state);
1458 assert(r == 0);
1459
1460 /* Make sure its a remote mailbox */
1461 if (!state->mbentry->server) goto out;
1462
1463 /* Check ACL */
1464 /* Note that we use a weaker form of access control than
1465 * normal - we only check for ACL_LOOKUP and we don't refuse
1466 * access if the mailbox is not local */
1467 if (!state->isadmin &&
1468 (!state->mbentry->acl ||
1469 !(cyrus_acl_myrights(state->auth_state, state->mbentry->acl) & ACL_LOOKUP)))
1470 goto out;
1471
1472 buf_appendcstr(&value, state->mbentry->server);
1473
1474 output_entryatt(state, entry->name, "", &value);
1475 out:
1476 buf_free(&value);
1477 }
1478
annotation_get_partition(annotate_state_t * state,struct annotate_entry_list * entry)1479 static void annotation_get_partition(annotate_state_t *state,
1480 struct annotate_entry_list *entry)
1481 {
1482 struct buf value = BUF_INITIALIZER;
1483 int r;
1484
1485 assert(state);
1486 assert(state->which == ANNOTATION_SCOPE_MAILBOX);
1487 r = annotate_state_need_mbentry(state);
1488 assert(r == 0);
1489
1490 /* Make sure its a local mailbox */
1491 if (state->mbentry->server) goto out;
1492
1493 /* Check ACL */
1494 if (!state->isadmin &&
1495 (!state->mbentry->acl ||
1496 !(cyrus_acl_myrights(state->auth_state, state->mbentry->acl) & ACL_LOOKUP)))
1497 goto out;
1498
1499 buf_appendcstr(&value, state->mbentry->partition);
1500
1501 output_entryatt(state, entry->name, "", &value);
1502 out:
1503 buf_free(&value);
1504 }
1505
annotation_get_annotsize(annotate_state_t * state,struct annotate_entry_list * entry)1506 static void annotation_get_annotsize(annotate_state_t *state,
1507 struct annotate_entry_list *entry)
1508 {
1509 struct mailbox *mailbox = state->mailbox;
1510 struct buf value = BUF_INITIALIZER;
1511
1512 assert(mailbox);
1513
1514 buf_printf(&value, QUOTA_T_FMT, mailbox->i.quota_annot_used);
1515 output_entryatt(state, entry->name, "", &value);
1516 buf_free(&value);
1517 }
1518
annotation_get_size(annotate_state_t * state,struct annotate_entry_list * entry)1519 static void annotation_get_size(annotate_state_t *state,
1520 struct annotate_entry_list *entry)
1521 {
1522 struct mailbox *mailbox = state->mailbox;
1523 struct buf value = BUF_INITIALIZER;
1524
1525 assert(mailbox);
1526
1527 buf_printf(&value, QUOTA_T_FMT, mailbox->i.quota_mailbox_used);
1528 output_entryatt(state, entry->name, "", &value);
1529 buf_free(&value);
1530 }
1531
annotation_get_lastupdate(annotate_state_t * state,struct annotate_entry_list * entry)1532 static void annotation_get_lastupdate(annotate_state_t *state,
1533 struct annotate_entry_list *entry)
1534 {
1535 struct stat sbuf;
1536 char valuebuf[RFC3501_DATETIME_MAX+1];
1537 struct buf value = BUF_INITIALIZER;
1538 char *fname;
1539 int r;
1540
1541 r = annotate_state_need_mbentry(state);
1542 if (r)
1543 goto out;
1544
1545 fname = mbentry_metapath(state->mbentry, META_INDEX, 0);
1546 if (!fname)
1547 goto out;
1548 if (stat(fname, &sbuf) == -1)
1549 goto out;
1550
1551 time_to_rfc3501(sbuf.st_mtime, valuebuf, sizeof(valuebuf));
1552 buf_appendcstr(&value, valuebuf);
1553
1554 output_entryatt(state, entry->name, "", &value);
1555 out:
1556 buf_free(&value);
1557 }
1558
annotation_get_lastpop(annotate_state_t * state,struct annotate_entry_list * entry)1559 static void annotation_get_lastpop(annotate_state_t *state,
1560 struct annotate_entry_list *entry)
1561 {
1562 struct mailbox *mailbox = state->mailbox;
1563 char valuebuf[RFC3501_DATETIME_MAX+1];
1564 struct buf value = BUF_INITIALIZER;
1565
1566 assert(mailbox);
1567
1568 if (mailbox->i.pop3_last_login) {
1569 time_to_rfc3501(mailbox->i.pop3_last_login, valuebuf,
1570 sizeof(valuebuf));
1571 buf_appendcstr(&value, valuebuf);
1572 }
1573
1574 output_entryatt(state, entry->name, "", &value);
1575 buf_free(&value);
1576 }
1577
annotation_get_mailboxopt(annotate_state_t * state,struct annotate_entry_list * entry)1578 static void annotation_get_mailboxopt(annotate_state_t *state,
1579 struct annotate_entry_list *entry)
1580 {
1581 struct mailbox *mailbox = state->mailbox;
1582 uint32_t flag = (unsigned long)entry->desc->rock;
1583 struct buf value = BUF_INITIALIZER;
1584
1585 assert(mailbox);
1586
1587 buf_appendcstr(&value,
1588 (mailbox->i.options & flag ? "true" : "false"));
1589 output_entryatt(state, entry->name, "", &value);
1590 buf_free(&value);
1591 }
1592
annotation_get_pop3showafter(annotate_state_t * state,struct annotate_entry_list * entry)1593 static void annotation_get_pop3showafter(annotate_state_t *state,
1594 struct annotate_entry_list *entry)
1595 {
1596 struct mailbox *mailbox = state->mailbox;
1597 char valuebuf[RFC3501_DATETIME_MAX+1];
1598 struct buf value = BUF_INITIALIZER;
1599
1600 assert(mailbox);
1601
1602 if (mailbox->i.pop3_show_after)
1603 {
1604 time_to_rfc3501(mailbox->i.pop3_show_after, valuebuf, sizeof(valuebuf));
1605 buf_appendcstr(&value, valuebuf);
1606 }
1607
1608 output_entryatt(state, entry->name, "", &value);
1609 buf_free(&value);
1610 }
1611
annotation_get_synccrcs(annotate_state_t * state,struct annotate_entry_list * entry)1612 static void annotation_get_synccrcs(annotate_state_t *state,
1613 struct annotate_entry_list *entry)
1614 {
1615 struct mailbox *mailbox = state->mailbox;
1616 struct buf value = BUF_INITIALIZER;
1617
1618 assert(mailbox);
1619
1620 buf_printf(&value, "%u %u", mailbox->i.synccrcs.basic,
1621 mailbox->i.synccrcs.annot);
1622
1623 output_entryatt(state, entry->name, "", &value);
1624 buf_free(&value);
1625 }
1626
annotation_get_usermodseq(annotate_state_t * state,struct annotate_entry_list * entry)1627 static void annotation_get_usermodseq(annotate_state_t *state,
1628 struct annotate_entry_list *entry)
1629 {
1630 struct buf value = BUF_INITIALIZER;
1631 struct mboxname_counters counters;
1632 char *mboxname = NULL;
1633
1634 memset(&counters, 0, sizeof(struct mboxname_counters));
1635
1636 assert(state);
1637 assert(state->userid);
1638
1639 mboxname = mboxname_user_mbox(state->userid, NULL);
1640 mboxname_read_counters(mboxname, &counters);
1641
1642 buf_printf(&value, "%llu", counters.highestmodseq);
1643
1644 output_entryatt(state, entry->name, state->userid, &value);
1645 free(mboxname);
1646 buf_free(&value);
1647 }
1648
annotation_get_usercounters(annotate_state_t * state,struct annotate_entry_list * entry)1649 static void annotation_get_usercounters(annotate_state_t *state,
1650 struct annotate_entry_list *entry)
1651 {
1652 struct buf value = BUF_INITIALIZER;
1653 struct mboxname_counters counters;
1654 char *mboxname = NULL;
1655
1656 assert(state);
1657 assert(state->userid);
1658
1659 mboxname = mboxname_user_mbox(state->userid, NULL);
1660 int r = mboxname_read_counters(mboxname, &counters);
1661
1662 if (!r) buf_printf(&value, "%u %llu %llu %llu %llu %llu %llu %llu %llu %llu %u",
1663 counters.version, counters.highestmodseq,
1664 counters.mailmodseq, counters.caldavmodseq,
1665 counters.carddavmodseq, counters.notesmodseq,
1666 counters.mailfoldersmodseq, counters.caldavfoldersmodseq,
1667 counters.carddavfoldersmodseq, counters.notesfoldersmodseq,
1668 counters.uidvalidity);
1669
1670 output_entryatt(state, entry->name, state->userid, &value);
1671 free(mboxname);
1672 buf_free(&value);
1673 }
1674
annotation_get_uniqueid(annotate_state_t * state,struct annotate_entry_list * entry)1675 static void annotation_get_uniqueid(annotate_state_t *state,
1676 struct annotate_entry_list *entry)
1677 {
1678 struct buf value = BUF_INITIALIZER;
1679
1680 assert(state->mailbox);
1681
1682 if (state->mailbox->uniqueid)
1683 buf_appendcstr(&value, state->mailbox->uniqueid);
1684
1685 output_entryatt(state, entry->name, "", &value);
1686 buf_free(&value);
1687 }
1688
rw_cb(const char * mailbox,uint32_t uid,const char * entry,const char * userid,const struct buf * value,void * rock)1689 static int rw_cb(const char *mailbox __attribute__((unused)),
1690 uint32_t uid __attribute__((unused)),
1691 const char *entry, const char *userid,
1692 const struct buf *value, void *rock)
1693 {
1694 annotate_state_t *state = (annotate_state_t *)rock;
1695
1696 if (!userid) userid = "";
1697
1698 if (!userid[0] || !strcmp(userid, state->userid)) {
1699 output_entryatt(state, entry, userid, value);
1700 }
1701
1702 return 0;
1703 }
1704
annotation_get_fromdb(annotate_state_t * state,struct annotate_entry_list * entry)1705 static void annotation_get_fromdb(annotate_state_t *state,
1706 struct annotate_entry_list *entry)
1707 {
1708 const char *mboxname = (state->mailbox ? state->mailbox->name : "");
1709 state->found = 0;
1710
1711 annotatemore_findall(mboxname, state->uid, entry->name, &rw_cb, state);
1712
1713 if (state->found != state->attribs &&
1714 (!strchr(entry->name, '%') && !strchr(entry->name, '*'))) {
1715 /* some results not found for an explicitly specified entry,
1716 * make sure we emit explicit NILs */
1717 struct buf empty = BUF_INITIALIZER;
1718 if (!(state->found & (ATTRIB_VALUE_PRIV|ATTRIB_SIZE_PRIV)) &&
1719 (state->attribs & (ATTRIB_VALUE_PRIV|ATTRIB_SIZE_PRIV))) {
1720 /* store up value.priv and/or size.priv */
1721 output_entryatt(state, entry->name, state->userid, &empty);
1722 }
1723 if (!(state->found & (ATTRIB_VALUE_SHARED|ATTRIB_SIZE_SHARED)) &&
1724 (state->attribs & (ATTRIB_VALUE_SHARED|ATTRIB_SIZE_SHARED))) {
1725 /* store up value.shared and/or size.shared */
1726 output_entryatt(state, entry->name, "", &empty);
1727 }
1728 /* flush any stored attribute-value pairs */
1729 flush_entryatt(state);
1730 }
1731 }
1732
1733 /* TODO: need to handle /<section-part>/ somehow */
1734 static const annotate_entrydesc_t message_builtin_entries[] =
1735 {
1736 {
1737 /* RFC5257 defines /altsubject with both .shared & .priv */
1738 "/altsubject",
1739 ATTRIB_TYPE_STRING,
1740 BACKEND_ONLY,
1741 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1742 0,
1743 annotation_get_fromdb,
1744 annotation_set_todb,
1745 NULL
1746 },{
1747 /* RFC5257 defines /comment with both .shared & .priv */
1748 "/comment",
1749 ATTRIB_TYPE_STRING,
1750 BACKEND_ONLY,
1751 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1752 0,
1753 annotation_get_fromdb,
1754 annotation_set_todb,
1755 NULL
1756 },{ NULL, 0, ANNOTATION_PROXY_T_INVALID, 0, 0, NULL, NULL, NULL }
1757 };
1758
1759 static const annotate_entrydesc_t message_db_entry =
1760 {
1761 NULL,
1762 ATTRIB_TYPE_STRING,
1763 BACKEND_ONLY,
1764 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1765 0,
1766 annotation_get_fromdb,
1767 annotation_set_todb,
1768 NULL
1769 };
1770
1771 static const annotate_entrydesc_t mailbox_builtin_entries[] =
1772 {
1773 {
1774 /*
1775 * This entry was defined in the early ANNOTATMORE drafts but
1776 * disappeared as of draft 13 and didn't make it into the final
1777 * RFC. We keep it around because it's not too hard to
1778 * implement.
1779 */
1780 "/check",
1781 ATTRIB_TYPE_BOOLEAN,
1782 BACKEND_ONLY,
1783 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1784 0,
1785 annotation_get_fromdb,
1786 annotation_set_todb,
1787 NULL
1788 },{
1789 /*
1790 * This entry was defined in the early ANNOTATMORE drafts but
1791 * disappeared as of draft 13 and didn't make it into the final
1792 * RFC. We keep it around because it's not too hard to
1793 * implement.
1794 */
1795 "/checkperiod",
1796 ATTRIB_TYPE_UINT,
1797 BACKEND_ONLY,
1798 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1799 0,
1800 annotation_get_fromdb,
1801 annotation_set_todb,
1802 NULL
1803 },{
1804 /* RFC5464 defines /shared/comment and /private/comment */
1805 "/comment",
1806 ATTRIB_TYPE_STRING,
1807 BACKEND_ONLY,
1808 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1809 0,
1810 annotation_get_fromdb,
1811 annotation_set_todb,
1812 NULL
1813 },{
1814 /*
1815 * This entry was defined in the early ANNOTATMORE drafts but
1816 * disappeared as of draft 13 and didn't make it into the final
1817 * RFC. We keep it around because it's not too hard to
1818 * implement, even though we don't check the format.
1819 */
1820 "/sort",
1821 ATTRIB_TYPE_STRING,
1822 BACKEND_ONLY,
1823 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1824 0,
1825 annotation_get_fromdb,
1826 annotation_set_todb,
1827 NULL
1828 },{
1829 /*
1830 * RFC6154 defines /private/specialuse.
1831 */
1832 "/specialuse",
1833 ATTRIB_TYPE_STRING,
1834 BACKEND_ONLY,
1835 ATTRIB_VALUE_PRIV,
1836 0,
1837 annotation_get_fromdb,
1838 annotation_set_specialuse,
1839 NULL
1840 },{
1841 /*
1842 * This entry was defined in the early ANNOTATMORE drafts but
1843 * disappeared as of draft 13 and didn't make it into the final
1844 * RFC. We keep it around because it's not too hard to
1845 * implement, even though we don't check the format.
1846 */
1847 "/thread",
1848 ATTRIB_TYPE_STRING,
1849 BACKEND_ONLY,
1850 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
1851 0,
1852 annotation_get_fromdb,
1853 annotation_set_todb,
1854 NULL
1855 },{
1856 IMAP_ANNOT_NS "annotsize",
1857 ATTRIB_TYPE_STRING,
1858 BACKEND_ONLY,
1859 ATTRIB_VALUE_SHARED,
1860 0,
1861 annotation_get_annotsize,
1862 /*set*/NULL,
1863 NULL
1864 },{
1865 IMAP_ANNOT_NS "duplicatedeliver",
1866 ATTRIB_TYPE_BOOLEAN,
1867 BACKEND_ONLY,
1868 ATTRIB_VALUE_SHARED,
1869 0,
1870 annotation_get_mailboxopt,
1871 annotation_set_mailboxopt,
1872 (void *)OPT_IMAP_DUPDELIVER
1873 },{
1874 IMAP_ANNOT_NS "expire",
1875 ATTRIB_TYPE_UINT,
1876 BACKEND_ONLY,
1877 ATTRIB_VALUE_SHARED,
1878 ACL_ADMIN,
1879 annotation_get_fromdb,
1880 annotation_set_todb,
1881 NULL
1882 },{
1883 IMAP_ANNOT_NS "lastpop",
1884 ATTRIB_TYPE_STRING,
1885 BACKEND_ONLY,
1886 ATTRIB_VALUE_SHARED,
1887 0,
1888 annotation_get_lastpop,
1889 /*set*/NULL,
1890 NULL
1891 },{
1892 IMAP_ANNOT_NS "lastupdate",
1893 ATTRIB_TYPE_STRING,
1894 BACKEND_ONLY,
1895 ATTRIB_VALUE_SHARED,
1896 0,
1897 annotation_get_lastupdate,
1898 /*set*/NULL,
1899 NULL
1900 },{
1901 IMAP_ANNOT_NS "news2mail",
1902 ATTRIB_TYPE_STRING,
1903 BACKEND_ONLY,
1904 ATTRIB_VALUE_SHARED,
1905 ACL_ADMIN,
1906 annotation_get_fromdb,
1907 annotation_set_todb,
1908 NULL
1909 },{
1910 IMAP_ANNOT_NS "partition",
1911 /* _get_partition does its own access control check */
1912 ATTRIB_TYPE_STRING | ATTRIB_NO_FETCH_ACL_CHECK,
1913 BACKEND_ONLY,
1914 ATTRIB_VALUE_SHARED,
1915 0,
1916 annotation_get_partition,
1917 /*set*/NULL,
1918 NULL
1919 },{
1920 IMAP_ANNOT_NS "pop3newuidl",
1921 ATTRIB_TYPE_BOOLEAN,
1922 BACKEND_ONLY,
1923 ATTRIB_VALUE_SHARED,
1924 0,
1925 annotation_get_mailboxopt,
1926 annotation_set_mailboxopt,
1927 (void *)OPT_POP3_NEW_UIDL
1928 },{
1929 IMAP_ANNOT_NS "pop3showafter",
1930 ATTRIB_TYPE_STRING,
1931 BACKEND_ONLY,
1932 ATTRIB_VALUE_SHARED,
1933 0,
1934 annotation_get_pop3showafter,
1935 annotation_set_pop3showafter,
1936 NULL
1937 },{
1938 IMAP_ANNOT_NS "server",
1939 /* _get_server does its own access control check */
1940 ATTRIB_TYPE_STRING | ATTRIB_NO_FETCH_ACL_CHECK,
1941 PROXY_ONLY,
1942 ATTRIB_VALUE_SHARED,
1943 0,
1944 annotation_get_server,
1945 /*set*/NULL,
1946 NULL
1947 },{
1948 IMAP_ANNOT_NS "sharedseen",
1949 ATTRIB_TYPE_BOOLEAN,
1950 BACKEND_ONLY,
1951 ATTRIB_VALUE_SHARED,
1952 0,
1953 annotation_get_mailboxopt,
1954 annotation_set_mailboxopt,
1955 (void *)OPT_IMAP_SHAREDSEEN
1956 },{
1957 IMAP_ANNOT_NS "sieve",
1958 ATTRIB_TYPE_STRING,
1959 BACKEND_ONLY,
1960 ATTRIB_VALUE_SHARED,
1961 ACL_ADMIN,
1962 annotation_get_fromdb,
1963 annotation_set_todb,
1964 NULL
1965 },{
1966 IMAP_ANNOT_NS "size",
1967 ATTRIB_TYPE_STRING,
1968 BACKEND_ONLY,
1969 ATTRIB_VALUE_SHARED,
1970 0,
1971 annotation_get_size,
1972 /*set*/NULL,
1973 NULL
1974 },{
1975 IMAP_ANNOT_NS "squat",
1976 ATTRIB_TYPE_BOOLEAN,
1977 BACKEND_ONLY,
1978 ATTRIB_VALUE_SHARED,
1979 ACL_ADMIN,
1980 annotation_get_fromdb,
1981 annotation_set_todb,
1982 NULL
1983 },{
1984 IMAP_ANNOT_NS "synccrcs",
1985 ATTRIB_TYPE_STRING,
1986 BACKEND_ONLY,
1987 ATTRIB_VALUE_SHARED,
1988 0,
1989 annotation_get_synccrcs,
1990 NULL,
1991 NULL,
1992 },{
1993 IMAP_ANNOT_NS "uniqueid",
1994 ATTRIB_TYPE_STRING,
1995 BACKEND_ONLY,
1996 ATTRIB_VALUE_SHARED,
1997 0,
1998 annotation_get_uniqueid,
1999 NULL,
2000 NULL
2001 },{ NULL, 0, ANNOTATION_PROXY_T_INVALID, 0, 0, NULL, NULL, NULL }
2002 };
2003
2004 static const annotate_entrydesc_t mailbox_db_entry =
2005 {
2006 NULL,
2007 ATTRIB_TYPE_STRING,
2008 BACKEND_ONLY,
2009 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
2010 0,
2011 annotation_get_fromdb,
2012 annotation_set_todb,
2013 NULL
2014 };
2015
2016 static const annotate_entrydesc_t server_builtin_entries[] =
2017 {
2018 {
2019 /* RFC5464 defines /shared/admin. */
2020 "/admin",
2021 ATTRIB_TYPE_STRING,
2022 PROXY_AND_BACKEND,
2023 ATTRIB_VALUE_SHARED,
2024 ACL_ADMIN,
2025 annotation_get_fromdb,
2026 annotation_set_todb,
2027 NULL
2028 },{
2029 /* RFC5464 defines /shared/comment. */
2030 "/comment",
2031 ATTRIB_TYPE_STRING,
2032 PROXY_AND_BACKEND,
2033 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
2034 ACL_ADMIN,
2035 annotation_get_fromdb,
2036 annotation_set_todb,
2037 NULL
2038 },{
2039 /*
2040 * This entry was defined in the early ANNOTATMORE drafts but
2041 * disappeared as of draft 13 and didn't make it into the final
2042 * RFC. We keep it around because it's not too hard to
2043 * implement.
2044 */
2045 "/motd",
2046 ATTRIB_TYPE_STRING,
2047 PROXY_AND_BACKEND,
2048 ATTRIB_VALUE_SHARED,
2049 0,
2050 annotation_get_fromfile,
2051 annotation_set_tofile,
2052 (void *)"motd"
2053 },{
2054 /* The "usemodseq" was added with conversations support, to allow
2055 * a single value to show any changes to anything about a user */
2056 IMAP_ANNOT_NS "usermodseq",
2057 ATTRIB_TYPE_UINT,
2058 BACKEND_ONLY,
2059 ATTRIB_VALUE_PRIV,
2060 0,
2061 annotation_get_usermodseq,
2062 /*set*/NULL,
2063 NULL
2064 },{
2065 /* The "usemodseq" was added with conversations support, to allow
2066 * a single value to show any changes to anything about a user */
2067 IMAP_ANNOT_NS "usercounters",
2068 ATTRIB_TYPE_UINT,
2069 BACKEND_ONLY,
2070 ATTRIB_VALUE_PRIV,
2071 0,
2072 annotation_get_usercounters,
2073 /*set*/NULL,
2074 NULL
2075 },{
2076 IMAP_ANNOT_NS "expire",
2077 ATTRIB_TYPE_UINT,
2078 PROXY_AND_BACKEND,
2079 ATTRIB_VALUE_SHARED,
2080 ACL_ADMIN,
2081 annotation_get_fromdb,
2082 annotation_set_todb,
2083 NULL
2084 },{
2085 IMAP_ANNOT_NS "freespace",
2086 ATTRIB_TYPE_STRING,
2087 BACKEND_ONLY,
2088 ATTRIB_VALUE_SHARED,
2089 0,
2090 annotation_get_freespace,
2091 /*set*/NULL,
2092 NULL
2093 },{
2094 IMAP_ANNOT_NS "freespace/total",
2095 ATTRIB_TYPE_STRING,
2096 BACKEND_ONLY,
2097 ATTRIB_VALUE_SHARED,
2098 0,
2099 annotation_get_freespace_total,
2100 /*set*/NULL,
2101 NULL
2102 },{
2103 IMAP_ANNOT_NS "freespace/percent/most",
2104 ATTRIB_TYPE_STRING,
2105 BACKEND_ONLY,
2106 ATTRIB_VALUE_SHARED,
2107 0,
2108 annotation_get_freespace_percent_most,
2109 /*set*/NULL,
2110 NULL
2111 },{
2112 IMAP_ANNOT_NS "shutdown",
2113 ATTRIB_TYPE_STRING,
2114 PROXY_AND_BACKEND,
2115 ATTRIB_VALUE_SHARED,
2116 0,
2117 annotation_get_fromfile,
2118 annotation_set_tofile,
2119 (void *)"shutdown"
2120 },{
2121 IMAP_ANNOT_NS "squat",
2122 ATTRIB_TYPE_BOOLEAN,
2123 PROXY_AND_BACKEND,
2124 ATTRIB_VALUE_SHARED,
2125 ACL_ADMIN,
2126 annotation_get_fromdb,
2127 annotation_set_todb,
2128 NULL
2129 },{ NULL, 0, ANNOTATION_PROXY_T_INVALID,
2130 0, 0, NULL, NULL, NULL }
2131 };
2132
2133 static const annotate_entrydesc_t server_db_entry =
2134 {
2135 NULL,
2136 ATTRIB_TYPE_STRING,
2137 PROXY_AND_BACKEND,
2138 ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV,
2139 0,
2140 annotation_get_fromdb,
2141 annotation_set_todb,
2142 NULL
2143 };
2144
2145 /* Annotation attributes and their flags */
2146 struct annotate_attrib
2147 {
2148 const char *name;
2149 int entry;
2150 };
2151
2152 static const struct annotate_attrib annotation_attributes[] =
2153 {
2154 { "value", ATTRIB_VALUE_SHARED | ATTRIB_VALUE_PRIV },
2155 { "value.shared", ATTRIB_VALUE_SHARED },
2156 { "value.priv", ATTRIB_VALUE_PRIV },
2157 { "size", ATTRIB_SIZE_SHARED | ATTRIB_SIZE_PRIV },
2158 { "size.shared", ATTRIB_SIZE_SHARED },
2159 { "size.priv", ATTRIB_SIZE_PRIV },
2160 /*
2161 * The following attribute names appeared in the first drafts of the
2162 * ANNOTATEMORE extension but did not make it to the final RFC, or
2163 * even to draft 11 which we also officially support. They might
2164 * appear in old annotation definition files, so we map them to
2165 * ATTRIB_DEPRECATED and issue a warning rather then remove them
2166 * entirely.
2167 */
2168 { "modifiedsince", ATTRIB_DEPRECATED },
2169 { "modifiedsince.shared", ATTRIB_DEPRECATED },
2170 { "modifiedsince.priv", ATTRIB_DEPRECATED },
2171 { "content-type", ATTRIB_DEPRECATED },
2172 { "content-type.shared", ATTRIB_DEPRECATED },
2173 { "content-type.priv", ATTRIB_DEPRECATED },
2174 { NULL, 0 }
2175 };
2176
_annotate_fetch_entries(annotate_state_t * state,int proxy_check)2177 static void _annotate_fetch_entries(annotate_state_t *state,
2178 int proxy_check)
2179 {
2180 struct annotate_entry_list *ee;
2181
2182 /* Loop through the list of provided entries to get */
2183 for (ee = state->entry_list; ee; ee = ee->next) {
2184
2185 if (proxy_check) {
2186 if (ee->desc->proxytype == BACKEND_ONLY &&
2187 proxy_fetch_func &&
2188 !config_getstring(IMAPOPT_PROXYSERVERS))
2189 continue;
2190 }
2191
2192 if (!_annotate_may_fetch(state, ee->desc))
2193 continue;
2194
2195 ee->desc->get(state, ee);
2196 }
2197 }
2198
annotate_state_fetch(annotate_state_t * state,const strarray_t * entries,const strarray_t * attribs,annotate_fetch_cb_t callback,void * rock)2199 EXPORTED int annotate_state_fetch(annotate_state_t *state,
2200 const strarray_t *entries, const strarray_t *attribs,
2201 annotate_fetch_cb_t callback, void *rock)
2202 {
2203 int i;
2204 struct glob *g;
2205 const ptrarray_t *non_db_entries;
2206 const annotate_entrydesc_t *db_entry;
2207 int r = 0;
2208
2209 annotate_state_start(state);
2210 state->callback = callback;
2211 state->callback_rock = rock;
2212
2213 /* Build list of attributes to fetch */
2214 for (i = 0 ; i < attribs->count ; i++)
2215 {
2216 const char *s = attribs->data[i];
2217 int attribcount;
2218
2219 /*
2220 * TODO: this is bogus. The * and % wildcard characters applied
2221 * to attributes in the early drafts of the ANNOTATEMORE
2222 * extension, but not in later drafts where those characters are
2223 * actually illegal in attribute names.
2224 */
2225 g = glob_init(s, '.');
2226
2227 for (attribcount = 0;
2228 annotation_attributes[attribcount].name;
2229 attribcount++) {
2230 if (GLOB_MATCH(g, annotation_attributes[attribcount].name)) {
2231 if (annotation_attributes[attribcount].entry & ATTRIB_DEPRECATED) {
2232 if (strcmp(s, "*"))
2233 syslog(LOG_WARNING, "annotatemore_fetch: client used "
2234 "deprecated attribute \"%s\", ignoring",
2235 annotation_attributes[attribcount].name);
2236 }
2237 else
2238 state->attribs |= annotation_attributes[attribcount].entry;
2239 }
2240 }
2241
2242 glob_free(&g);
2243 }
2244
2245 if (!state->attribs)
2246 goto out;
2247
2248 if (state->which == ANNOTATION_SCOPE_SERVER) {
2249 non_db_entries = &server_entries;
2250 db_entry = &server_db_entry;
2251 }
2252 else if (state->which == ANNOTATION_SCOPE_MAILBOX) {
2253 non_db_entries = &mailbox_entries;
2254 db_entry = &mailbox_db_entry;
2255 }
2256 else if (state->which == ANNOTATION_SCOPE_MESSAGE) {
2257 non_db_entries = &message_entries;
2258 db_entry = &message_db_entry;
2259 }
2260 else {
2261 syslog(LOG_ERR, "IOERROR: unknown annotation scope %d", state->which);
2262 r = IMAP_INTERNAL;
2263 goto out;
2264 }
2265
2266 /* Build a list of callbacks for fetching the annotations */
2267 for (i = 0 ; i < entries->count ; i++)
2268 {
2269 const char *s = entries->data[i];
2270 int j;
2271 int check_db = 0; /* should we check the db for this entry? */
2272
2273 g = glob_init(s, '/');
2274
2275 for (j = 0 ; j < non_db_entries->count ; j++) {
2276 const annotate_entrydesc_t *desc = non_db_entries->data[j];
2277
2278 if (!desc->get)
2279 continue;
2280
2281 if (GLOB_MATCH(g, desc->name)) {
2282 /* Add this entry to our list only if it
2283 applies to our particular server type */
2284 if ((desc->proxytype != PROXY_ONLY)
2285 || proxy_fetch_func)
2286 _annotate_state_add_entry(state, desc, desc->name);
2287 }
2288
2289 if (!strcmp(s, desc->name)) {
2290 /* exact match */
2291 if (desc->proxytype != PROXY_ONLY) {
2292 state->orig_entry = entries; /* proxy it */
2293 }
2294 break;
2295 }
2296 }
2297
2298 if (j == non_db_entries->count) {
2299 /* no [exact] match */
2300 state->orig_entry = entries; /* proxy it */
2301 check_db = 1;
2302 }
2303
2304 /* Add the db entry to our list if only if it
2305 applies to our particular server type */
2306 if (check_db &&
2307 ((db_entry->proxytype != PROXY_ONLY) || proxy_fetch_func)) {
2308 /* Add the db entry to our list */
2309 _annotate_state_add_entry(state, db_entry, s);
2310 }
2311
2312 glob_free(&g);
2313 }
2314
2315 if (state->which == ANNOTATION_SCOPE_SERVER) {
2316 _annotate_fetch_entries(state, /*proxy_check*/1);
2317 }
2318 else if (state->which == ANNOTATION_SCOPE_MAILBOX) {
2319
2320 if (state->entry_list || proxy_fetch_func) {
2321 if (proxy_fetch_func) {
2322 r = annotate_state_need_mbentry(state);
2323 if (r)
2324 goto out;
2325 assert(state->mbentry);
2326 }
2327
2328 if (proxy_fetch_func && state->orig_entry) {
2329 state->orig_mailbox = state->mbentry->name;
2330 state->orig_attribute = attribs;
2331 }
2332
2333 _annotate_fetch_entries(state, /*proxy_check*/1);
2334
2335 if (proxy_fetch_func && state->orig_entry && state->mbentry->server &&
2336 !hash_lookup(state->mbentry->server, &state->server_table)) {
2337 /* xxx ignoring result */
2338 proxy_fetch_func(state->mbentry->server, state->mbentry->ext_name,
2339 state->orig_entry, state->orig_attribute);
2340 hash_insert(state->mbentry->server, (void *)0xDEADBEEF, &state->server_table);
2341 }
2342 }
2343 }
2344 else if (state->which == ANNOTATION_SCOPE_MESSAGE) {
2345 _annotate_fetch_entries(state, /*proxy_check*/0);
2346 }
2347
2348 /* Flush last cached entry in output_entryatt() */
2349 flush_entryatt(state);
2350
2351 out:
2352 annotate_state_finish(state);
2353 return r;
2354 }
2355
2356 /************************** Annotation Storing *****************************/
2357
annotatemore_lookup(const char * mboxname,const char * entry,const char * userid,struct buf * value)2358 EXPORTED int annotatemore_lookup(const char *mboxname, const char *entry,
2359 const char *userid, struct buf *value)
2360 {
2361 return annotatemore_msg_lookup(mboxname, /*uid*/0, entry, userid, value);
2362 }
2363
annotatemore_lookupmask(const char * mboxname,const char * entry,const char * userid,struct buf * value)2364 EXPORTED int annotatemore_lookupmask(const char *mboxname, const char *entry,
2365 const char *userid, struct buf *value)
2366 {
2367 return annotatemore_msg_lookupmask(mboxname, /*uid*/0, entry, userid, value);
2368 }
2369
annotatemore_msg_lookup(const char * mboxname,uint32_t uid,const char * entry,const char * userid,struct buf * value)2370 EXPORTED int annotatemore_msg_lookup(const char *mboxname, uint32_t uid, const char *entry,
2371 const char *userid, struct buf *value)
2372 {
2373 char key[MAX_MAILBOX_PATH+1];
2374 size_t keylen, datalen;
2375 int r;
2376 const char *data;
2377 annotate_db_t *d = NULL;
2378
2379 r = _annotate_getdb(mboxname, uid, 0, &d);
2380 if (r)
2381 return (r == CYRUSDB_NOTFOUND ? 0 : r);
2382
2383 keylen = make_key(mboxname, uid, entry, userid, key, sizeof(key));
2384
2385 do {
2386 r = cyrusdb_fetch(d->db, key, keylen, &data, &datalen, tid(d));
2387 } while (r == CYRUSDB_AGAIN);
2388
2389 if (!r && data) {
2390 r = split_attribs(data, datalen, value);
2391 if (!r) {
2392 /* Force a copy, in case the putdb() call destroys
2393 * the per-db data area that @data points to. */
2394 buf_cstring(value);
2395 }
2396 }
2397 else if (r == CYRUSDB_NOTFOUND) r = 0;
2398
2399 annotate_putdb(&d);
2400 return r;
2401 }
2402
annotatemore_msg_lookupmask(const char * mboxname,uint32_t uid,const char * entry,const char * userid,struct buf * value)2403 EXPORTED int annotatemore_msg_lookupmask(const char *mboxname, uint32_t uid, const char *entry,
2404 const char *userid, struct buf *value)
2405 {
2406 int r = 0;
2407 value->len = 0; /* just in case! */
2408 /* only if the user isn't the owner, we look for a masking value */
2409 if (!mboxname_userownsmailbox(userid, mboxname))
2410 r = annotatemore_msg_lookup(mboxname, uid, entry, userid, value);
2411 /* and if there isn't one, we fall through to the shared value */
2412 if (value->len == 0)
2413 r = annotatemore_msg_lookup(mboxname, uid, entry, "", value);
2414 /* and because of Bron's use of NULL rather than "" at FastMail... */
2415 if (value->len == 0)
2416 r = annotatemore_msg_lookup(mboxname, uid, entry, NULL, value);
2417 return r;
2418 }
2419
read_old_value(annotate_db_t * d,const char * key,int keylen,struct buf * valp)2420 static int read_old_value(annotate_db_t *d,
2421 const char *key, int keylen,
2422 struct buf *valp)
2423 {
2424 int r;
2425 size_t datalen;
2426 const char *data;
2427
2428 do {
2429 r = cyrusdb_fetch(d->db, key, keylen, &data, &datalen, tid(d));
2430 } while (r == CYRUSDB_AGAIN);
2431
2432 if (r == CYRUSDB_NOTFOUND) {
2433 r = 0;
2434 goto out;
2435 }
2436 if (r || !data)
2437 goto out;
2438
2439 r = split_attribs(data, datalen, valp);
2440
2441 out:
2442 return r;
2443 }
2444
write_entry(struct mailbox * mailbox,unsigned int uid,const char * entry,const char * userid,const struct buf * value,int ignorequota)2445 static int write_entry(struct mailbox *mailbox,
2446 unsigned int uid,
2447 const char *entry,
2448 const char *userid,
2449 const struct buf *value,
2450 int ignorequota)
2451 {
2452 char key[MAX_MAILBOX_PATH+1];
2453 int keylen, r;
2454 annotate_db_t *d = NULL;
2455 struct buf oldval = BUF_INITIALIZER;
2456 const char *mboxname = mailbox ? mailbox->name : "";
2457
2458 r = _annotate_getdb(mboxname, uid, CYRUSDB_CREATE, &d);
2459 if (r)
2460 return r;
2461
2462 /* must be in a transaction to modify the db */
2463 annotate_begin(d);
2464
2465 keylen = make_key(mboxname, uid, entry, userid, key, sizeof(key));
2466
2467 if (mailbox) {
2468 r = read_old_value(d, key, keylen, &oldval);
2469 if (r) goto out;
2470
2471 if (!ignorequota) {
2472 quota_t qdiffs[QUOTA_NUMRESOURCES] = QUOTA_DIFFS_DONTCARE_INITIALIZER;
2473 qdiffs[QUOTA_ANNOTSTORAGE] = value->len - (quota_t)oldval.len;
2474 r = mailbox_quota_check(mailbox, qdiffs);
2475 if (r) goto out;
2476 }
2477
2478 /* do the annot-changed here before altering the DB */
2479 mailbox_annot_changed(mailbox, uid, entry, userid, &oldval, value);
2480 }
2481
2482 /* zero length annotation is also deletion I think */
2483 if (!value->len) {
2484
2485 #if DEBUG
2486 syslog(LOG_ERR, "write_entry: deleting key %s from %s",
2487 key_as_string(d, key, keylen), d->filename);
2488 #endif
2489
2490 do {
2491 r = cyrusdb_delete(d->db, key, keylen, tid(d), /*force*/1);
2492 } while (r == CYRUSDB_AGAIN);
2493 }
2494 else {
2495 struct buf data = BUF_INITIALIZER;
2496 unsigned long l;
2497 static const char contenttype[] = "text/plain"; /* fake */
2498
2499 l = htonl(value->len);
2500 buf_appendmap(&data, (const char *)&l, sizeof(l));
2501
2502 buf_appendmap(&data, value->s, value->len);
2503 buf_putc(&data, '\0');
2504
2505 /*
2506 * Older versions of Cyrus expected content-type and
2507 * modifiedsince fields after the value. We don't support those
2508 * but we write out default values just in case the database
2509 * needs to be read by older versions of Cyrus
2510 */
2511 buf_appendcstr(&data, contenttype);
2512 buf_putc(&data, '\0');
2513
2514 l = 0; /* fake modifiedsince */
2515 buf_appendmap(&data, (const char *)&l, sizeof(l));
2516
2517 #if DEBUG
2518 syslog(LOG_ERR, "write_entry: storing key %s to %s",
2519 key_as_string(d, key, keylen), d->filename);
2520 #endif
2521
2522 do {
2523 r = cyrusdb_store(d->db, key, keylen, data.s, data.len, tid(d));
2524 } while (r == CYRUSDB_AGAIN);
2525 buf_free(&data);
2526 }
2527
2528 if (!mailbox)
2529 sync_log_annotation("");
2530
2531 out:
2532 annotate_putdb(&d);
2533 buf_free(&oldval);
2534
2535 return r;
2536 }
2537
annotatemore_rawwrite(const char * mboxname,const char * entry,const char * userid,const struct buf * value)2538 EXPORTED int annotatemore_rawwrite(const char *mboxname, const char *entry,
2539 const char *userid, const struct buf *value)
2540 {
2541 char key[MAX_MAILBOX_PATH+1];
2542 int keylen, r;
2543 annotate_db_t *d = NULL;
2544 uint32_t uid = 0;
2545
2546 r = _annotate_getdb(mboxname, uid, CYRUSDB_CREATE, &d);
2547 if (r) goto done;
2548
2549 /* must be in a transaction to modify the db */
2550 annotate_begin(d);
2551
2552 keylen = make_key(mboxname, uid, entry, userid, key, sizeof(key));
2553
2554 if (value->s == NULL) {
2555 do {
2556 r = cyrusdb_delete(d->db, key, keylen, tid(d), /*force*/1);
2557 } while (r == CYRUSDB_AGAIN);
2558 }
2559 else {
2560 struct buf data = BUF_INITIALIZER;
2561 unsigned long l;
2562 static const char contenttype[] = "text/plain"; /* fake */
2563
2564 l = htonl(value->len);
2565 buf_appendmap(&data, (const char *)&l, sizeof(l));
2566
2567 buf_appendmap(&data, value->s, value->len);
2568 buf_putc(&data, '\0');
2569
2570 /*
2571 * Older versions of Cyrus expected content-type and
2572 * modifiedsince fields after the value. We don't support those
2573 * but we write out default values just in case the database
2574 * needs to be read by older versions of Cyrus
2575 */
2576 buf_appendcstr(&data, contenttype);
2577 buf_putc(&data, '\0');
2578
2579 l = 0; /* fake modifiedsince */
2580 buf_appendmap(&data, (const char *)&l, sizeof(l));
2581
2582 do {
2583 r = cyrusdb_store(d->db, key, keylen, data.s, data.len, tid(d));
2584 } while (r == CYRUSDB_AGAIN);
2585
2586 buf_free(&data);
2587 }
2588
2589 if (r) goto done;
2590 r = annotate_commit(d);
2591
2592 done:
2593 annotate_putdb(&d);
2594
2595 return r;
2596 }
2597
annotatemore_write(const char * mboxname,const char * entry,const char * userid,const struct buf * value)2598 EXPORTED int annotatemore_write(const char *mboxname, const char *entry,
2599 const char *userid, const struct buf *value)
2600 {
2601 return annotatemore_msg_write(mboxname, /*uid*/0, entry, userid, value);
2602 }
2603
annotatemore_msg_write(const char * mboxname,uint32_t uid,const char * entry,const char * userid,const struct buf * value)2604 EXPORTED int annotatemore_msg_write(const char *mboxname, uint32_t uid, const char *entry,
2605 const char *userid, const struct buf *value)
2606 {
2607 struct mailbox *mailbox = NULL;
2608 int r = 0;
2609 annotate_db_t *d = NULL;
2610
2611 r = _annotate_getdb(mboxname, uid, CYRUSDB_CREATE, &d);
2612 if (r) goto done;
2613
2614 if (mboxname) {
2615 r = mailbox_open_iwl(mboxname, &mailbox);
2616 if (r) goto done;
2617 }
2618
2619 r = write_entry(mailbox, uid, entry, userid, value, /*ignorequota*/1);
2620 if (r) goto done;
2621
2622 r = annotate_commit(d);
2623
2624 done:
2625 annotate_putdb(&d);
2626 mailbox_close(&mailbox);
2627
2628 return r;
2629 }
2630
annotate_state_write(annotate_state_t * state,const char * entry,const char * userid,const struct buf * value)2631 EXPORTED int annotate_state_write(annotate_state_t *state,
2632 const char *entry,
2633 const char *userid,
2634 const struct buf *value)
2635 {
2636 return write_entry(state->mailbox, state->uid,
2637 entry, userid, value, /*ignorequota*/1);
2638 }
2639
annotate_state_writemask(annotate_state_t * state,const char * entry,const char * userid,const struct buf * value)2640 EXPORTED int annotate_state_writemask(annotate_state_t *state,
2641 const char *entry,
2642 const char *userid,
2643 const struct buf *value)
2644 {
2645 /* if the user is the owner, then write to the shared namespace */
2646 if (mboxname_userownsmailbox(userid, state->mailbox->name))
2647 return annotate_state_write(state, entry, "", value);
2648 else
2649 return annotate_state_write(state, entry, userid, value);
2650 }
2651
annotate_canon_value(struct buf * value,int type)2652 static int annotate_canon_value(struct buf *value, int type)
2653 {
2654 char *p = NULL;
2655 unsigned long uwhatever = 0;
2656 long whatever = 0;
2657
2658 /* check for NIL */
2659 if (value->s == NULL)
2660 return 0;
2661
2662 switch (type) {
2663 case ATTRIB_TYPE_STRING:
2664 /* free form */
2665 break;
2666
2667 case ATTRIB_TYPE_BOOLEAN:
2668 /* make sure its "true" or "false" */
2669 if (value->len == 4 && !strncasecmp(value->s, "true", 4)) {
2670 buf_reset(value);
2671 buf_appendcstr(value, "true");
2672 buf_cstring(value);
2673 }
2674 else if (value->len == 5 && !strncasecmp(value->s, "false", 5)) {
2675 buf_reset(value);
2676 buf_appendcstr(value, "false");
2677 buf_cstring(value);
2678 }
2679 else return IMAP_ANNOTATION_BADVALUE;
2680 break;
2681
2682 case ATTRIB_TYPE_UINT:
2683 /* make sure its a valid ulong ( >= 0 ) */
2684 errno = 0;
2685 buf_cstring(value);
2686 uwhatever = strtoul(value->s, &p, 10);
2687 if ((p == value->s) /* no value */
2688 || (*p != '\0') /* illegal char */
2689 || (unsigned)(p - value->s) != value->len
2690 /* embedded NUL */
2691 || errno /* overflow */
2692 || strchr(value->s, '-')) { /* negative number */
2693 return IMAP_ANNOTATION_BADVALUE;
2694 }
2695 break;
2696
2697 case ATTRIB_TYPE_INT:
2698 /* make sure its a valid long */
2699 errno = 0;
2700 buf_cstring(value);
2701 whatever = strtol(value->s, &p, 10);
2702 if ((p == value->s) /* no value */
2703 || (*p != '\0') /* illegal char */
2704 || (unsigned)(p - value->s) != value->len
2705 /* embedded NUL */
2706 || errno) { /* underflow/overflow */
2707 return IMAP_ANNOTATION_BADVALUE;
2708 }
2709 break;
2710
2711 default:
2712 /* unknown type */
2713 return IMAP_ANNOTATION_BADVALUE;
2714 }
2715
2716 if (whatever || uwhatever) /* filthy compiler magic */
2717 return 0;
2718
2719 return 0;
2720 }
2721
_annotate_store_entries(annotate_state_t * state)2722 static int _annotate_store_entries(annotate_state_t *state)
2723 {
2724 struct annotate_entry_list *ee;
2725 int r;
2726
2727 /* Loop through the list of provided entries to set */
2728 for (ee = state->entry_list ; ee ; ee = ee->next) {
2729
2730 /* Skip annotations that can't be stored on frontend */
2731 if ((ee->desc->proxytype == BACKEND_ONLY) &&
2732 (state->mbentry && state->mbentry->server))
2733 continue;
2734
2735 if (ee->have_shared &&
2736 !_annotate_may_store(state, /*shared*/1, ee->desc))
2737 return IMAP_PERMISSION_DENIED;
2738
2739 if (ee->have_priv &&
2740 !_annotate_may_store(state, /*shared*/0, ee->desc))
2741 return IMAP_PERMISSION_DENIED;
2742
2743 r = ee->desc->set(state, ee);
2744 if (r)
2745 return r;
2746 }
2747 return 0;
2748 }
2749
2750
2751 struct proxy_rock {
2752 const char *mbox_pat;
2753 struct entryattlist *entryatts;
2754 };
2755
store_proxy(const char * server,void * data,void * rock)2756 static void store_proxy(const char *server, void *data __attribute__((unused)),
2757 void *rock)
2758 {
2759 struct proxy_rock *prock = (struct proxy_rock *) rock;
2760
2761 proxy_store_func(server, prock->mbox_pat, prock->entryatts);
2762 }
2763
_annotate_may_store(annotate_state_t * state,int is_shared,const annotate_entrydesc_t * desc)2764 static int _annotate_may_store(annotate_state_t *state,
2765 int is_shared,
2766 const annotate_entrydesc_t *desc)
2767 {
2768 unsigned int my_rights;
2769 unsigned int needed = 0;
2770 const char *acl = NULL;
2771
2772 /* Admins can do anything */
2773 if (state->isadmin)
2774 return 1;
2775
2776 if (state->which == ANNOTATION_SCOPE_SERVER) {
2777 /* RFC5464 doesn't mention access control for server
2778 * annotations, but this seems a sensible practice and is
2779 * consistent with past Cyrus behaviour */
2780 return !is_shared;
2781 }
2782 else if (state->which == ANNOTATION_SCOPE_MAILBOX) {
2783 assert(state->mailbox);
2784
2785 /* Make sure its a local mailbox annotation */
2786 if (state->mbentry && state->mbentry->server)
2787 return 0;
2788
2789 acl = state->mailbox->acl;
2790 /* RFC5464 is a trifle vague about access control for mailbox
2791 * annotations but this seems to be compliant */
2792 needed = ACL_LOOKUP;
2793 if (is_shared)
2794 needed |= ACL_READ|ACL_WRITE|desc->extra_rights;
2795 /* fall through to ACL check */
2796 }
2797 else if (state->which == ANNOTATION_SCOPE_MESSAGE) {
2798 assert(state->mailbox);
2799 acl = state->mailbox->acl;
2800 /* RFC5257: writing to a private annotation needs 'r'.
2801 * Writing to a shared annotation needs 'n' */
2802 needed = (is_shared ? ACL_ANNOTATEMSG : ACL_READ);
2803 /* fall through to ACL check */
2804 }
2805
2806 if (!acl)
2807 return 0;
2808
2809 my_rights = cyrus_acl_myrights(state->auth_state, acl);
2810
2811 return ((my_rights & needed) == needed);
2812 }
2813
annotation_set_tofile(annotate_state_t * state,struct annotate_entry_list * entry)2814 static int annotation_set_tofile(annotate_state_t *state
2815 __attribute__((unused)),
2816 struct annotate_entry_list *entry)
2817 {
2818 const char *filename = (const char *)entry->desc->rock;
2819 char path[MAX_MAILBOX_PATH+1];
2820 int r;
2821 FILE *f;
2822
2823 snprintf(path, sizeof(path), "%s/msg/%s", config_dir, filename);
2824
2825 /* XXX how do we do this atomically with other annotations? */
2826 if (entry->shared.s == NULL)
2827 return unlink(path);
2828 else {
2829 r = cyrus_mkdir(path, 0755);
2830 if (r)
2831 return r;
2832 f = fopen(path, "w");
2833 if (!f) {
2834 syslog(LOG_ERR, "cannot open %s for writing: %m", path);
2835 return IMAP_IOERROR;
2836 }
2837 fwrite(entry->shared.s, 1, entry->shared.len, f);
2838 fputc('\n', f);
2839 return fclose(f);
2840 }
2841
2842 return IMAP_IOERROR;
2843 }
2844
annotation_set_todb(annotate_state_t * state,struct annotate_entry_list * entry)2845 static int annotation_set_todb(annotate_state_t *state,
2846 struct annotate_entry_list *entry)
2847 {
2848 int r = 0;
2849
2850 if (entry->have_shared)
2851 r = write_entry(state->mailbox, state->uid,
2852 entry->name, "",
2853 &entry->shared, 0);
2854 if (!r && entry->have_priv)
2855 r = write_entry(state->mailbox, state->uid,
2856 entry->name, state->userid,
2857 &entry->priv, 0);
2858
2859 return r;
2860 }
2861
annotation_set_mailboxopt(annotate_state_t * state,struct annotate_entry_list * entry)2862 static int annotation_set_mailboxopt(annotate_state_t *state,
2863 struct annotate_entry_list *entry)
2864 {
2865 struct mailbox *mailbox = state->mailbox;
2866 uint32_t flag = (unsigned long)entry->desc->rock;
2867 unsigned long newopts;
2868
2869 assert(mailbox);
2870
2871 newopts = mailbox->i.options;
2872
2873 if (entry->shared.s &&
2874 !strcmp(entry->shared.s, "true")) {
2875 newopts |= flag;
2876 } else {
2877 newopts &= ~flag;
2878 }
2879
2880 /* only mark dirty if there's been a change */
2881 if (mailbox->i.options != newopts) {
2882 mailbox_index_dirty(mailbox);
2883 mailbox->i.options = newopts;
2884 mboxlist_foldermodseq_dirty(mailbox);
2885 }
2886
2887 return 0;
2888 }
2889
annotation_set_pop3showafter(annotate_state_t * state,struct annotate_entry_list * entry)2890 static int annotation_set_pop3showafter(annotate_state_t *state,
2891 struct annotate_entry_list *entry)
2892 {
2893 struct mailbox *mailbox = state->mailbox;
2894 int r = 0;
2895 time_t date;
2896
2897 assert(mailbox);
2898
2899 if (entry->shared.s == NULL) {
2900 /* Effectively removes the annotation */
2901 date = 0;
2902 }
2903 else {
2904 r = time_from_rfc3501(buf_cstring(&entry->shared), &date);
2905 if (r < 0)
2906 return IMAP_PROTOCOL_BAD_PARAMETERS;
2907 }
2908
2909 if (date != mailbox->i.pop3_show_after) {
2910 mailbox->i.pop3_show_after = date;
2911 mailbox_index_dirty(mailbox);
2912 }
2913
2914 return 0;
2915 }
2916
specialuse_validate(const char * userid,const char * src,struct buf * dest)2917 EXPORTED int specialuse_validate(const char *userid, const char *src, struct buf *dest)
2918 {
2919 const char *specialuse_extra_opt = config_getstring(IMAPOPT_SPECIALUSE_EXTRA);
2920 char *strval = NULL;
2921 strarray_t *valid = NULL;
2922 strarray_t *values = NULL;
2923 int i, j;
2924 int r = 0;
2925
2926 if (!src) {
2927 buf_reset(dest);
2928 return 0;
2929 }
2930
2931 /* check specialuse_extra option if set */
2932 if (specialuse_extra_opt)
2933 valid = strarray_split(specialuse_extra_opt, NULL, 0);
2934 else
2935 valid = strarray_new();
2936
2937 /* strarray_add(valid, "\\All"); -- we don't support virtual folders right now */
2938 strarray_add(valid, "\\Archive");
2939 strarray_add(valid, "\\Drafts");
2940 /* strarray_add(valid, "\\Flagged"); -- we don't support virtual folders right now */
2941 strarray_add(valid, "\\Junk");
2942 strarray_add(valid, "\\Sent");
2943 strarray_add(valid, "\\Trash");
2944
2945 values = strarray_split(src, NULL, 0);
2946
2947 for (i = 0; i < values->count; i++) {
2948 const char *item = strarray_nth(values, i);
2949
2950 for (j = 0; j < valid->count; j++) { /* can't use find here */
2951 if (!strcasecmp(strarray_nth(valid, j), item))
2952 break;
2953 /* or without the leading '\' */
2954 if (!strcasecmp(strarray_nth(valid, j) + 1, item))
2955 break;
2956 }
2957 if (j >= valid->count) {
2958 r = IMAP_ANNOTATION_BADENTRY;
2959 goto done;
2960 }
2961
2962 /* don't allow names that are already in use */
2963 char *mboxname = mboxlist_find_specialuse(strarray_nth(valid, j), userid);
2964 if (mboxname) {
2965 free(mboxname);
2966 r = IMAP_MAILBOX_SPECIALUSE;
2967 goto done;
2968 }
2969
2970 /* normalise the value */
2971 strarray_set(values, i, strarray_nth(valid, j));
2972 }
2973
2974 strval = strarray_join(values, " ");
2975 buf_setcstr(dest, strval);
2976
2977 done:
2978 free(strval);
2979 strarray_free(valid);
2980 strarray_free(values);
2981 return r;
2982 }
2983
annotation_set_specialuse(annotate_state_t * state,struct annotate_entry_list * entry)2984 static int annotation_set_specialuse(annotate_state_t *state,
2985 struct annotate_entry_list *entry)
2986 {
2987 struct buf res = BUF_INITIALIZER;
2988 int r = IMAP_PERMISSION_DENIED;
2989
2990 assert(state->mailbox);
2991
2992 /* Effectively removes the annotation */
2993 if (entry->priv.s == NULL) {
2994 r = write_entry(state->mailbox, state->uid, entry->name, state->userid, &entry->priv, 0);
2995 goto done;
2996 }
2997
2998 r = specialuse_validate(state->userid, buf_cstring(&entry->priv), &res);
2999 if (r) goto done;
3000
3001 r = write_entry(state->mailbox, state->uid, entry->name, state->userid, &res, 0);
3002
3003 done:
3004 buf_free(&res);
3005
3006 return r;
3007 }
3008
find_desc_store(annotate_state_t * state,const char * name,const annotate_entrydesc_t ** descp)3009 static int find_desc_store(annotate_state_t *state,
3010 const char *name,
3011 const annotate_entrydesc_t **descp)
3012 {
3013 int scope = state->which;
3014 const ptrarray_t *descs;
3015 const annotate_entrydesc_t *db_entry;
3016 annotate_entrydesc_t *desc;
3017 int i;
3018
3019 if (scope == ANNOTATION_SCOPE_SERVER) {
3020 descs = &server_entries;
3021 db_entry = &server_db_entry;
3022 }
3023 else if (scope == ANNOTATION_SCOPE_MAILBOX) {
3024 descs = &mailbox_entries;
3025 db_entry = &mailbox_db_entry;
3026 }
3027 else if (scope == ANNOTATION_SCOPE_MESSAGE) {
3028 descs = &message_entries;
3029 db_entry = &message_db_entry;
3030 }
3031 else {
3032 syslog(LOG_ERR, "IOERROR: unknown scope in find_desc_store %d", scope);
3033 return IMAP_INTERNAL;
3034 }
3035
3036 /* check for DAV annotations */
3037 if (state->mailbox && (state->mailbox->mbtype & MBTYPES_DAV) &&
3038 !strncmp(name, DAV_ANNOT_NS, strlen(DAV_ANNOT_NS))) {
3039 *descp = db_entry;
3040 return 0;
3041 }
3042
3043 /* check known IMAP annotations */
3044 for (i = 0 ; i < descs->count ; i++) {
3045 desc = descs->data[i];
3046 if (strcmp(name, desc->name))
3047 continue;
3048 if (!desc->set) {
3049 /* read-only annotation */
3050 return IMAP_PERMISSION_DENIED;
3051 }
3052 *descp = desc;
3053 return 0;
3054 }
3055
3056 /* unknown annotation */
3057 if (!config_getswitch(IMAPOPT_ANNOTATION_ALLOW_UNDEFINED))
3058 return IMAP_PERMISSION_DENIED;
3059
3060 /* check for /flags and /vendor/cmu */
3061 if (scope == ANNOTATION_SCOPE_MESSAGE &&
3062 !strncmp(name, "/flags/", 7))
3063 return IMAP_PERMISSION_DENIED;
3064
3065 if (!strncmp(name, IMAP_ANNOT_NS, strlen(IMAP_ANNOT_NS)))
3066 return IMAP_PERMISSION_DENIED;
3067
3068 *descp = db_entry;
3069 return 0;
3070 }
3071
annotate_state_store(annotate_state_t * state,struct entryattlist * l)3072 EXPORTED int annotate_state_store(annotate_state_t *state, struct entryattlist *l)
3073 {
3074 int r = 0;
3075 struct entryattlist *e = l;
3076 struct attvaluelist *av;
3077
3078 annotate_state_start(state);
3079
3080 /* Build a list of callbacks for storing the annotations */
3081 while (e) {
3082 int attribs;
3083 const annotate_entrydesc_t *desc = NULL;
3084 struct annotate_entry_list *nentry = NULL;
3085
3086 /* See if we support this entry */
3087 r = find_desc_store(state, e->entry, &desc);
3088 if (r)
3089 goto cleanup;
3090
3091 /* Add this entry to our list only if it
3092 applies to our particular server type */
3093 if ((desc->proxytype != PROXY_ONLY)
3094 || proxy_store_func)
3095 nentry = _annotate_state_add_entry(state, desc, e->entry);
3096
3097 /* See if we are allowed to set the given attributes. */
3098 attribs = desc->attribs;
3099 av = e->attvalues;
3100 while (av) {
3101 if (!strcmp(av->attrib, "value.shared")) {
3102 if (!(attribs & ATTRIB_VALUE_SHARED)) {
3103 r = IMAP_PERMISSION_DENIED;
3104 goto cleanup;
3105 }
3106 r = annotate_canon_value(&av->value,
3107 desc->type);
3108 if (r)
3109 goto cleanup;
3110 if (nentry) {
3111 buf_init_ro(&nentry->shared, av->value.s, av->value.len);
3112 nentry->have_shared = 1;
3113 }
3114 }
3115 else if (!strcmp(av->attrib, "content-type.shared") ||
3116 !strcmp(av->attrib, "content-type.priv")) {
3117 syslog(LOG_WARNING, "annotatemore_store: client used "
3118 "deprecated attribute \"%s\", ignoring",
3119 av->attrib);
3120 }
3121 else if (!strcmp(av->attrib, "value.priv")) {
3122 if (!(attribs & ATTRIB_VALUE_PRIV)) {
3123 r = IMAP_PERMISSION_DENIED;
3124 goto cleanup;
3125 }
3126 r = annotate_canon_value(&av->value,
3127 desc->type);
3128 if (r)
3129 goto cleanup;
3130 if (nentry) {
3131 buf_init_ro(&nentry->priv, av->value.s, av->value.len);
3132 nentry->have_priv = 1;
3133 }
3134 }
3135 else {
3136 r = IMAP_PERMISSION_DENIED;
3137 goto cleanup;
3138 }
3139
3140 av = av->next;
3141 }
3142
3143 e = e->next;
3144 }
3145
3146 if (state->which == ANNOTATION_SCOPE_SERVER) {
3147 r = _annotate_store_entries(state);
3148 }
3149
3150 else if (state->which == ANNOTATION_SCOPE_MAILBOX) {
3151 if (proxy_store_func) {
3152 r = annotate_state_need_mbentry(state);
3153 if (r)
3154 goto cleanup;
3155 assert(state->mbentry);
3156 }
3157 else assert(state->mailbox);
3158
3159 r = _annotate_store_entries(state);
3160 if (r)
3161 goto cleanup;
3162
3163 state->count++;
3164
3165 if (proxy_store_func && state->mbentry->server &&
3166 !hash_lookup(state->mbentry->server, &state->server_table)) {
3167 hash_insert(state->mbentry->server, (void *)0xDEADBEEF, &state->server_table);
3168 }
3169
3170 if (!r && !state->count) r = IMAP_MAILBOX_NONEXISTENT;
3171
3172 if (proxy_store_func) {
3173 if (!r) {
3174 /* proxy command to backends */
3175 struct proxy_rock prock = { NULL, NULL };
3176 prock.mbox_pat = state->mbentry->ext_name;
3177 prock.entryatts = l;
3178 hash_enumerate(&state->server_table, store_proxy, &prock);
3179 }
3180 }
3181 }
3182 else if (state->which == ANNOTATION_SCOPE_MESSAGE) {
3183 r = _annotate_store_entries(state);
3184 if (r)
3185 goto cleanup;
3186 }
3187
3188 cleanup:
3189 annotate_state_finish(state);
3190 return r;
3191 }
3192
3193 struct rename_rock {
3194 struct mailbox *oldmailbox;
3195 struct mailbox *newmailbox;
3196 const char *olduserid;
3197 const char *newuserid;
3198 uint32_t olduid;
3199 uint32_t newuid;
3200 int copy;
3201 };
3202
rename_cb(const char * mboxname,uint32_t uid,const char * entry,const char * userid,const struct buf * value,void * rock)3203 static int rename_cb(const char *mboxname __attribute__((unused)),
3204 uint32_t uid,
3205 const char *entry,
3206 const char *userid, const struct buf *value,
3207 void *rock)
3208 {
3209 struct rename_rock *rrock = (struct rename_rock *) rock;
3210 int r = 0;
3211
3212 if (rrock->newmailbox &&
3213 /* displayname stores the UTF-8 encoded JMAP name of a mailbox */
3214 strcmp(entry, IMAP_ANNOT_NS "displayname")) {
3215 /* create newly renamed entry */
3216 const char *newuserid = userid;
3217
3218 if (rrock->olduserid && rrock->newuserid &&
3219 !strcmpsafe(rrock->olduserid, userid)) {
3220 /* renaming a user, so change the userid for priv annots */
3221 newuserid = rrock->newuserid;
3222 }
3223 r = write_entry(rrock->newmailbox, rrock->newuid, entry, newuserid, value, 0);
3224 }
3225
3226 if (!rrock->copy && !r) {
3227 /* delete existing entry */
3228 struct buf dattrib = BUF_INITIALIZER;
3229 r = write_entry(rrock->oldmailbox, uid, entry, userid, &dattrib, 0);
3230 }
3231
3232 return r;
3233 }
3234
annotate_rename_mailbox(struct mailbox * oldmailbox,struct mailbox * newmailbox)3235 EXPORTED int annotate_rename_mailbox(struct mailbox *oldmailbox,
3236 struct mailbox *newmailbox)
3237 {
3238 /* rename one mailbox */
3239 char *olduserid = mboxname_to_userid(oldmailbox->name);
3240 char *newuserid = mboxname_to_userid(newmailbox->name);
3241 annotate_db_t *d = NULL;
3242 int r = 0;
3243
3244 /* rewrite any per-folder annotations from the global db */
3245 r = _annotate_getdb(NULL, 0, /*don't create*/0, &d);
3246 if (r == CYRUSDB_NOTFOUND) {
3247 /* no global database, must not be anything to rename */
3248 r = 0;
3249 goto done;
3250 }
3251 if (r) goto done;
3252
3253 annotate_begin(d);
3254
3255 /* copy here - delete will dispose of old records later */
3256 r = _annotate_rewrite(oldmailbox, 0, olduserid,
3257 newmailbox, 0, newuserid,
3258 /*copy*/1);
3259 if (r) goto done;
3260
3261 r = annotate_commit(d);
3262 if (r) goto done;
3263
3264 /*
3265 * The per-folder database got moved or linked by mailbox_copy_files().
3266 */
3267
3268 done:
3269 annotate_putdb(&d);
3270 free(olduserid);
3271 free(newuserid);
3272
3273 return r;
3274 }
3275
3276
3277 /*
3278 * Perform a scan-and-rewrite through the database(s) for
3279 * a given set of criteria; common code for several higher
3280 * level operations.
3281 */
_annotate_rewrite(struct mailbox * oldmailbox,uint32_t olduid,const char * olduserid,struct mailbox * newmailbox,uint32_t newuid,const char * newuserid,int copy)3282 static int _annotate_rewrite(struct mailbox *oldmailbox,
3283 uint32_t olduid,
3284 const char *olduserid,
3285 struct mailbox *newmailbox,
3286 uint32_t newuid,
3287 const char *newuserid,
3288 int copy)
3289 {
3290 struct rename_rock rrock;
3291
3292 rrock.oldmailbox = oldmailbox;
3293 rrock.newmailbox = newmailbox;
3294 rrock.olduserid = olduserid;
3295 rrock.newuserid = newuserid;
3296 rrock.olduid = olduid;
3297 rrock.newuid = newuid;
3298 rrock.copy = copy;
3299
3300 return annotatemore_findall(oldmailbox->name, olduid, "*", &rename_cb, &rrock);
3301 }
3302
annotate_delete_mailbox(struct mailbox * mailbox)3303 EXPORTED int annotate_delete_mailbox(struct mailbox *mailbox)
3304 {
3305 int r = 0;
3306 char *fname = NULL;
3307 annotate_db_t *d = NULL;
3308
3309 assert(mailbox);
3310
3311 /* remove any per-folder annotations from the global db */
3312 r = _annotate_getdb(NULL, 0, /*don't create*/0, &d);
3313 if (r == CYRUSDB_NOTFOUND) {
3314 /* no global database, must not be anything to rename */
3315 r = 0;
3316 goto out;
3317 }
3318 if (r) goto out;
3319
3320 annotate_begin(d);
3321
3322 r = _annotate_rewrite(mailbox,
3323 /*olduid*/0, /*olduserid*/NULL,
3324 /*newmailbox*/NULL,
3325 /*newuid*/0, /*newuserid*/NULL,
3326 /*copy*/0);
3327 if (r) goto out;
3328
3329 /* remove the entire per-folder database */
3330 r = annotate_dbname_mailbox(mailbox, &fname);
3331 if (r) goto out;
3332
3333 /* (gnb)TODO: do we even need to do this?? */
3334 if (unlink(fname) < 0 && errno != ENOENT) {
3335 syslog(LOG_ERR, "cannot unlink %s: %m", fname);
3336 }
3337
3338 r = annotate_commit(d);
3339
3340 out:
3341 annotate_putdb(&d);
3342 free(fname);
3343 return r;
3344 }
3345
annotate_msg_copy(struct mailbox * oldmailbox,uint32_t olduid,struct mailbox * newmailbox,uint32_t newuid,const char * userid)3346 EXPORTED int annotate_msg_copy(struct mailbox *oldmailbox, uint32_t olduid,
3347 struct mailbox *newmailbox, uint32_t newuid,
3348 const char *userid)
3349 {
3350 annotate_db_t *d = NULL;
3351 int r;
3352
3353 r = _annotate_getdb(newmailbox->name, newuid, CYRUSDB_CREATE, &d);
3354 if (r) return r;
3355
3356 annotate_begin(d);
3357
3358 /* If these are not true, nobody will ever commit the data we're
3359 * about to copy, and that would be sad */
3360 assert(newmailbox->annot_state != NULL);
3361 assert(newmailbox->annot_state->d == d);
3362
3363 r = _annotate_rewrite(oldmailbox, olduid, userid,
3364 newmailbox, newuid, userid,
3365 /*copy*/1);
3366
3367 annotate_putdb(&d);
3368 return r;
3369 }
3370
cleanup_cb(void * rock,const char * key,size_t keylen,const char * data,size_t datalen)3371 static int cleanup_cb(void *rock,
3372 const char *key, size_t keylen,
3373 const char *data __attribute__((unused)),
3374 size_t datalen __attribute__((unused)))
3375 {
3376 annotate_db_t *d = (annotate_db_t *)rock;
3377
3378 return cyrusdb_delete(d->db, key, keylen, tid(d), /*force*/1);
3379 }
3380
3381 /* clean up WITHOUT counting usage again, we already removed that when
3382 * we expunged the record */
annotate_msg_cleanup(struct mailbox * mailbox,unsigned int uid)3383 HIDDEN int annotate_msg_cleanup(struct mailbox *mailbox, unsigned int uid)
3384 {
3385 char key[MAX_MAILBOX_PATH+1];
3386 size_t keylen;
3387 int r = 0;
3388 annotate_db_t *d = NULL;
3389
3390 assert(uid);
3391
3392 r = _annotate_getdb(mailbox->name, uid, 0, &d);
3393 if (r) return r;
3394
3395 /* must be in a transaction to modify the db */
3396 annotate_begin(d);
3397
3398 /* If these are not true, nobody will ever commit the data we're
3399 * about to copy, and that would be sad */
3400 assert(mailbox->annot_state != NULL);
3401 assert(mailbox->annot_state->d == d);
3402
3403 keylen = make_key(mailbox->name, uid, "", NULL, key, sizeof(key));
3404
3405 r = cyrusdb_foreach(d->db, key, keylen, NULL, &cleanup_cb, d, tid(d));
3406
3407 annotate_putdb(&d);
3408 return r;
3409 }
3410
3411 /************************* Annotation Initialization ************************/
3412
3413 /* The following code is courtesy of Thomas Viehmann <tv@beamnet.de> */
3414
3415
3416 static const struct annotate_attrib annotation_scope_names[] =
3417 {
3418 { "server", ANNOTATION_SCOPE_SERVER },
3419 { "mailbox", ANNOTATION_SCOPE_MAILBOX },
3420 { "message", ANNOTATION_SCOPE_MESSAGE },
3421 { NULL, 0 }
3422 };
3423
3424 static const struct annotate_attrib annotation_proxy_type_names[] =
3425 {
3426 { "proxy", PROXY_ONLY },
3427 { "backend", BACKEND_ONLY },
3428 { "proxy_and_backend", PROXY_AND_BACKEND },
3429 { NULL, 0 }
3430 };
3431
3432 static const struct annotate_attrib attribute_type_names[] =
3433 {
3434 /*
3435 * The "content-type" type was only used for protocol features which
3436 * were dropped before the RFCs became final. We accept it in
3437 * annotation definition files only for backwards compatibility with
3438 * earlier Cyrus versions.
3439 */
3440 { "content-type", ATTRIB_TYPE_STRING },
3441 { "string", ATTRIB_TYPE_STRING },
3442 { "boolean", ATTRIB_TYPE_BOOLEAN },
3443 { "uint", ATTRIB_TYPE_UINT },
3444 { "int", ATTRIB_TYPE_INT },
3445 { NULL, 0 }
3446 };
3447
3448 #define ANNOT_DEF_MAXLINELEN 1024
3449 #define ANNOT_MAX_ERRORS 64
3450
3451 struct parse_state
3452 {
3453 const char *filename;
3454 const char *context;
3455 unsigned int lineno;
3456 unsigned int nerrors;
3457 tok_t tok;
3458 };
3459
parse_error(struct parse_state * state,const char * err)3460 static void parse_error(struct parse_state *state, const char *err)
3461 {
3462 if (++state->nerrors < ANNOT_MAX_ERRORS)
3463 {
3464 struct buf msg = BUF_INITIALIZER;
3465
3466 buf_printf(&msg, "%s:%u:%u:error: %s",
3467 state->filename, state->lineno,
3468 tok_offset(&state->tok), err);
3469 if (state->context && *state->context)
3470 buf_printf(&msg, ", at or near '%s'", state->context);
3471 syslog(LOG_ERR, "%s", buf_cstring(&msg));
3472 buf_free(&msg);
3473 }
3474
3475 state->context = NULL;
3476 }
3477
3478 /* Search in table for the value given by @name and return
3479 * the corresponding enum value, or -1 on error.
3480 * @state and @errmsg is used to hint the user where we failed.
3481 */
table_lookup(const struct annotate_attrib * table,const char * name)3482 static int table_lookup(const struct annotate_attrib *table,
3483 const char *name)
3484 {
3485 for ( ; table->name ; table++) {
3486 if (!strcasecmp(table->name, name))
3487 return table->entry;
3488 }
3489 return -1;
3490 }
3491
3492
3493 /*
3494 * Parse and return the next token from the line buffer. Tokens are
3495 * separated by comma ',' characters but leading and trailing whitespace
3496 * is trimmed. Tokens are made up of alphanumeric characters (as
3497 * defined by libc's isalnum()) plus additional allowable characters
3498 * defined by @extra.
3499 *
3500 * At start *@state points into the buffer, and will be adjusted to
3501 * point further along in the buffer. Returns the beginning of the
3502 * token or NULL (and whines to syslog) if an error was encountered.
3503 */
get_token(struct parse_state * state,const char * extra)3504 static char *get_token(struct parse_state *state, const char *extra)
3505 {
3506 char *token;
3507 char *p;
3508
3509 token = tok_next(&state->tok);
3510 if (!token) {
3511 parse_error(state, "invalid annotation attributes");
3512 return NULL;
3513 }
3514
3515 /* check the token */
3516 if (extra == NULL)
3517 extra = "";
3518 for (p = token ; *p && (isalnum(*p) || strchr(extra, *p)) ; p++)
3519 ;
3520 if (*p) {
3521 state->context = p;
3522 parse_error(state, "invalid character");
3523 return NULL;
3524 }
3525
3526 state->context = token;
3527 return token;
3528 }
3529
3530 /* Parses strings of the form value1 [ value2 [ ... ]].
3531 * value1 is mapped via table to ints and the result or'ed.
3532 * Whitespace is allowed between value names and punctuation.
3533 * The field must end in '\0' or ','.
3534 * s is advanced to '\0' or ','.
3535 * On error errmsg is used to identify item to be parsed.
3536 */
parse_table_lookup_bitmask(const struct annotate_attrib * table,struct parse_state * state)3537 static int parse_table_lookup_bitmask(const struct annotate_attrib *table,
3538 struct parse_state *state)
3539 {
3540 char *token = get_token(state, ".-_/ ");
3541 char *p;
3542 int i;
3543 int result = 0;
3544 tok_t tok;
3545
3546 if (!token)
3547 return -1;
3548 tok_initm(&tok, token, NULL, 0);
3549
3550 while ((p = tok_next(&tok))) {
3551 state->context = p;
3552 i = table_lookup(table, p);
3553 if (i < 0)
3554 return i;
3555 result |= i;
3556 }
3557
3558 return result;
3559 }
3560
normalise_attribs(struct parse_state * state,int attribs)3561 static int normalise_attribs(struct parse_state *state, int attribs)
3562 {
3563 int nattribs = 0;
3564 static int deprecated_warnings = 0;
3565
3566 /* always provide size.shared if value.shared specified */
3567 if ((attribs & ATTRIB_VALUE_SHARED))
3568 nattribs |= ATTRIB_VALUE_SHARED|ATTRIB_SIZE_SHARED;
3569
3570 /* likewise size.priv */
3571 if ((attribs & ATTRIB_VALUE_PRIV))
3572 nattribs |= ATTRIB_VALUE_PRIV|ATTRIB_SIZE_PRIV;
3573
3574 /* ignore any other specified attributes */
3575
3576 if ((attribs & ATTRIB_DEPRECATED)) {
3577 if (!deprecated_warnings++)
3578 parse_error(state, "deprecated attribute names such as "
3579 "content-type or modified-since (ignoring)");
3580 }
3581
3582 return nattribs;
3583 }
3584
3585 /* Create array of allowed annotations, both internally & externally defined */
init_annotation_definitions(void)3586 static void init_annotation_definitions(void)
3587 {
3588 char *p;
3589 char aline[ANNOT_DEF_MAXLINELEN];
3590 annotate_entrydesc_t *ae;
3591 int i;
3592 FILE* f;
3593 struct parse_state state;
3594 ptrarray_t *entries = NULL;
3595
3596 /* copy static entries into list */
3597 for (i = 0 ; server_builtin_entries[i].name ; i++)
3598 ptrarray_append(&server_entries, (void *)&server_builtin_entries[i]);
3599
3600 /* copy static entries into list */
3601 for (i = 0 ; mailbox_builtin_entries[i].name ; i++)
3602 ptrarray_append(&mailbox_entries, (void *)&mailbox_builtin_entries[i]);
3603
3604 /* copy static entries into list */
3605 for (i = 0 ; message_builtin_entries[i].name ; i++)
3606 ptrarray_append(&message_entries, (void *)&message_builtin_entries[i]);
3607
3608 memset(&state, 0, sizeof(state));
3609
3610 /* parse config file */
3611 state.filename = config_getstring(IMAPOPT_ANNOTATION_DEFINITIONS);
3612
3613 if (!state.filename)
3614 return;
3615
3616 f = fopen(state.filename,"r");
3617 if (!f) {
3618 syslog(LOG_ERR, "%s: could not open annotation definition file: %m",
3619 state.filename);
3620 return;
3621 }
3622
3623 while (fgets(aline, sizeof(aline), f)) {
3624 /* remove leading space, skip blank lines and comments */
3625 state.lineno++;
3626 for (p = aline; *p && isspace(*p); p++);
3627 if (!*p || *p == '#') continue;
3628 tok_initm(&state.tok, aline, ",", TOK_TRIMLEFT|TOK_TRIMRIGHT|TOK_EMPTY);
3629
3630 /* note, we only do the most basic validity checking and may
3631 be more restrictive than neccessary */
3632
3633 ae = xzmalloc(sizeof(*ae));
3634
3635 if (!(p = get_token(&state, ".-_/:"))) goto bad;
3636 /* TV-TODO: should test for empty */
3637
3638 if (!strncmp(p, IMAP_ANNOT_NS, strlen(IMAP_ANNOT_NS))) {
3639 parse_error(&state, "annotation under " IMAP_ANNOT_NS);
3640 goto bad;
3641 }
3642 ae->name = xstrdup(p);
3643
3644 if (!(p = get_token(&state, ".-_/"))) goto bad;
3645 switch (table_lookup(annotation_scope_names, p)) {
3646 case ANNOTATION_SCOPE_SERVER:
3647 entries = &server_entries;
3648 break;
3649 case ANNOTATION_SCOPE_MAILBOX:
3650 entries = &mailbox_entries;
3651 break;
3652 case ANNOTATION_SCOPE_MESSAGE:
3653 if (!strncmp(ae->name, "/flags/", 7)) {
3654 /* RFC5257 reserves the /flags/ hierarchy for future use */
3655 state.context = ae->name;
3656 parse_error(&state, "message entry under /flags/");
3657 goto bad;
3658 }
3659 entries = &message_entries;
3660 break;
3661 case -1:
3662 parse_error(&state, "invalid annotation scope");
3663 goto bad;
3664 }
3665
3666 if (!(p = get_token(&state, NULL))) goto bad;
3667 i = table_lookup(attribute_type_names, p);
3668 if (i < 0) {
3669 parse_error(&state, "invalid annotation type");
3670 goto bad;
3671 }
3672 ae->type = i;
3673
3674 i = parse_table_lookup_bitmask(annotation_proxy_type_names, &state);
3675 if (i < 0) {
3676 parse_error(&state, "invalid annotation proxy type");
3677 goto bad;
3678 }
3679 ae->proxytype = i;
3680
3681 i = parse_table_lookup_bitmask(annotation_attributes, &state);
3682 if (i < 0) {
3683 parse_error(&state, "invalid annotation attributes");
3684 goto bad;
3685 }
3686 ae->attribs = normalise_attribs(&state, i);
3687
3688 if (!(p = get_token(&state, NULL))) goto bad;
3689 cyrus_acl_strtomask(p, &ae->extra_rights);
3690 /* XXX and if strtomask fails? */
3691
3692 p = tok_next(&state.tok);
3693 if (p) {
3694 parse_error(&state, "junk at end of line");
3695 goto bad;
3696 }
3697
3698 ae->get = annotation_get_fromdb;
3699 ae->set = annotation_set_todb;
3700 ae->rock = NULL;
3701 ptrarray_append(entries, ae);
3702 continue;
3703
3704 bad:
3705 free((char *)ae->name);
3706 free(ae);
3707 tok_fini(&state.tok);
3708 continue;
3709 }
3710
3711
3712 #if 0
3713 /* Suppress the syslog message to fix the unit tests, but have the
3714 * syslog message to aid the admin ...
3715 */
3716 if (state.nerrors)
3717 syslog(LOG_ERR, "%s: encountered %u errors. Struggling on, but "
3718 "some of your annotation definitions may be "
3719 "ignored. Please fix this file!",
3720 state.filename, state.nerrors);
3721 #endif
3722
3723 fclose(f);
3724 }
3725