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