1 /* lcb_indexr.c -- replication-based backup api - index reading functions
2 *
3 * Copyright (c) 1994-2015 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 <assert.h>
46 #include <syslog.h>
47
48 #include "lib/xmalloc.h"
49
50 #include "backup/backup.h"
51
52 #define LIBCYRUS_BACKUP_SOURCE /* this file is part of libcyrus_backup */
53 #include "backup/lcb_internal.h"
54 #include "backup/lcb_sqlconsts.h"
55
_column_int(sqlite3_stmt * stmt,int column)56 static int _column_int(sqlite3_stmt *stmt, int column)
57 {
58 assert(sqlite3_column_type(stmt, column) == SQLITE_INTEGER ||
59 sqlite3_column_type(stmt, column) == SQLITE_NULL);
60 return sqlite3_column_int(stmt, column);
61 }
62
_column_int64(sqlite3_stmt * stmt,int column)63 static sqlite3_int64 _column_int64(sqlite3_stmt *stmt, int column)
64 {
65 assert(sqlite3_column_type(stmt, column) == SQLITE_INTEGER ||
66 sqlite3_column_type(stmt, column) == SQLITE_NULL);
67 return sqlite3_column_int64(stmt, column);
68 }
69
_column_text(sqlite3_stmt * stmt,int column)70 static const char * _column_text(sqlite3_stmt *stmt, int column)
71 {
72 assert(sqlite3_column_type(stmt, column) == SQLITE_TEXT ||
73 sqlite3_column_type(stmt, column) == SQLITE_NULL);
74 return (const char *) sqlite3_column_text(stmt, column);
75 }
76
_get_mailbox_id_cb(sqlite3_stmt * stmt,void * rock)77 static int _get_mailbox_id_cb(sqlite3_stmt *stmt, void *rock) {
78 int *idp = (int *) rock;
79
80 *idp = _column_int(stmt, 0);
81
82 return 0;
83 }
84
backup_get_mailbox_id(struct backup * backup,const char * uniqueid)85 EXPORTED int backup_get_mailbox_id(struct backup *backup, const char *uniqueid)
86 {
87 struct sqldb_bindval bval[] = {
88 { ":uniqueid", SQLITE_TEXT, { .s = uniqueid } },
89 { NULL, SQLITE_NULL, { .s = NULL } },
90 };
91
92 int id = -1;
93
94 int r = sqldb_exec(backup->db, backup_index_mailbox_select_uniqueid_sql,
95 bval, _get_mailbox_id_cb, &id);
96 if (r) {
97 syslog(LOG_ERR, "%s: something went wrong: %i %s\n",
98 __func__, r, uniqueid);
99 }
100
101 return id;
102 }
103
backup_mailbox_message_list_add(struct backup_mailbox_message_list * list,struct backup_mailbox_message * mailbox_message)104 static void backup_mailbox_message_list_add(
105 struct backup_mailbox_message_list *list,
106 struct backup_mailbox_message *mailbox_message)
107 {
108 mailbox_message->next = NULL;
109
110 if (!list->head)
111 list->head = mailbox_message;
112
113 if (list->tail)
114 list->tail->next = mailbox_message;
115
116 list->tail = mailbox_message;
117
118 list->count++;
119 }
120
backup_mailbox_message_list_remove(struct backup_mailbox_message_list * list,struct backup_mailbox_message * mailbox_message)121 EXPORTED struct backup_mailbox_message *backup_mailbox_message_list_remove(
122 struct backup_mailbox_message_list *list,
123 struct backup_mailbox_message *mailbox_message)
124 {
125 struct backup_mailbox_message *node, *prev;
126
127 assert(list != NULL);
128 assert(mailbox_message != NULL);
129
130 prev = NULL;
131 node = list->head;
132 while (node && node != mailbox_message) {
133 prev = node;
134 node = node->next;
135 }
136
137 if (!node) return NULL;
138 assert(node == mailbox_message);
139
140 if (prev) {
141 prev->next = node->next;
142 }
143 else {
144 assert(node == list->head);
145 list->head = node->next;
146 }
147
148 if (!node->next) {
149 assert(node == list->tail);
150 list->tail = prev;
151 }
152
153 node->next = NULL;
154 list->count--;
155 return node;
156 }
157
backup_mailbox_message_list_empty(struct backup_mailbox_message_list * list)158 EXPORTED void backup_mailbox_message_list_empty(
159 struct backup_mailbox_message_list *list)
160 {
161 struct backup_mailbox_message *mailbox_message, *next;
162
163 mailbox_message = list->head;
164 while (mailbox_message) {
165 next = mailbox_message->next;
166 backup_mailbox_message_free(&mailbox_message);
167 mailbox_message = next;
168 }
169
170 memset(list, 0, sizeof(*list));
171 }
172
backup_mailbox_list_add(struct backup_mailbox_list * list,struct backup_mailbox * mailbox)173 EXPORTED void backup_mailbox_list_add(struct backup_mailbox_list *list,
174 struct backup_mailbox *mailbox)
175 {
176 mailbox->next = NULL;
177
178 if (!list->head)
179 list->head = mailbox;
180
181 if (list->tail)
182 list->tail->next = mailbox;
183
184 list->tail = mailbox;
185
186 list->count++;
187 }
188
backup_mailbox_list_remove(struct backup_mailbox_list * list,struct backup_mailbox * mailbox)189 EXPORTED struct backup_mailbox *backup_mailbox_list_remove(
190 struct backup_mailbox_list *list,
191 struct backup_mailbox *mailbox)
192 {
193 struct backup_mailbox *node, *prev;
194
195 assert(list != NULL);
196 assert(mailbox != NULL);
197
198 prev = NULL;
199 node = list->head;
200 while (node && node != mailbox) {
201 prev = node;
202 node = node->next;
203 }
204
205 if (!node) return NULL;
206 assert(node == mailbox);
207
208 if (prev) {
209 prev->next = node->next;
210 }
211 else {
212 assert(node == list->head);
213 list->head = node->next;
214 }
215
216 if (!node->next) {
217 assert(node == list->tail);
218 list->tail = prev;
219 }
220
221 node->next = NULL;
222 list->count--;
223 return node;
224 }
225
backup_mailbox_list_empty(struct backup_mailbox_list * list)226 EXPORTED void backup_mailbox_list_empty(struct backup_mailbox_list *list)
227 {
228 struct backup_mailbox *mailbox, *next;
229
230 mailbox = list->head;
231 while (mailbox) {
232 next = mailbox->next;
233 backup_mailbox_free(&mailbox);
234 mailbox = next;
235 }
236
237 memset(list, 0, sizeof(*list));
238 }
239
240 struct _mailbox_message_row_rock {
241 struct message_guid *match_guid;
242 struct backup_mailbox_message_list *save_list;
243 struct backup_mailbox_message **save_one;
244 };
245
_mailbox_message_row_cb(sqlite3_stmt * stmt,void * rock)246 static int _mailbox_message_row_cb(sqlite3_stmt *stmt, void *rock)
247 {
248 struct _mailbox_message_row_rock *mbrrock =
249 (struct _mailbox_message_row_rock *) rock;
250 struct backup_mailbox_message *mailbox_message;
251 char *guid_str = NULL;
252 int r = 0;
253
254 mailbox_message = xzmalloc(sizeof *mailbox_message);
255
256 int column = 0;
257 mailbox_message->id = _column_int(stmt, column++);
258 mailbox_message->mailbox_id = _column_int(stmt, column++);
259 mailbox_message->mailbox_uniqueid = xstrdupnull(_column_text(stmt, column++));
260 mailbox_message->message_id = _column_int(stmt, column++);
261 mailbox_message->last_chunk_id = _column_int(stmt, column++);
262 mailbox_message->uid = _column_int(stmt, column++);
263 mailbox_message->modseq = _column_int64(stmt, column++);
264 mailbox_message->last_updated = _column_int64(stmt, column++);
265 mailbox_message->flags = xstrdupnull(_column_text(stmt, column++));
266 mailbox_message->internaldate = _column_int64(stmt, column++);
267 guid_str = xstrdupnull(_column_text(stmt, column++));
268 mailbox_message->size = _column_int(stmt, column++);
269 mailbox_message->annotations = xstrdupnull(_column_text(stmt, column++));
270 mailbox_message->expunged = _column_int(stmt, column++);
271
272 message_guid_decode(&mailbox_message->guid, guid_str);
273 free(guid_str);
274
275 if (mbrrock->save_list) {
276 if (!mbrrock->match_guid
277 || message_guid_equal(mbrrock->match_guid, &mailbox_message->guid)) {
278 backup_mailbox_message_list_add(mbrrock->save_list, mailbox_message);
279 mailbox_message = NULL;
280 }
281 }
282 else if (mbrrock->save_one) {
283 *mbrrock->save_one = mailbox_message;
284 mailbox_message = NULL;
285 }
286
287 if (mailbox_message)
288 backup_mailbox_message_free(&mailbox_message);
289
290 return r;
291 }
292
backup_get_mailbox_messages(struct backup * backup,int chunk_id)293 EXPORTED struct backup_mailbox_message_list *backup_get_mailbox_messages(
294 struct backup *backup,
295 int chunk_id)
296 {
297 struct backup_mailbox_message_list *mailbox_message_list =
298 xzmalloc(sizeof *mailbox_message_list);
299
300 struct sqldb_bindval bval[] = {
301 { ":last_chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
302 { NULL, SQLITE_NULL, { .s = NULL } },
303 };
304
305 const char *sql = chunk_id ?
306 backup_index_mailbox_message_select_chunkid_sql :
307 backup_index_mailbox_message_select_all_sql;
308
309 struct _mailbox_message_row_rock mbrrock = {
310 NULL,
311 mailbox_message_list,
312 NULL,
313 };
314
315 int r = sqldb_exec(backup->db, sql, bval, _mailbox_message_row_cb,
316 &mbrrock);
317
318 if (r) {
319 backup_mailbox_message_list_empty(mailbox_message_list);
320 free(mailbox_message_list);
321 return NULL;
322 }
323
324 return mailbox_message_list;
325 }
326
backup_get_mailbox_message(struct backup * backup,const char * uniqueid,const char * guid)327 EXPORTED struct backup_mailbox_message *backup_get_mailbox_message(
328 struct backup *backup,
329 const char *uniqueid,
330 const char *guid)
331 {
332 struct backup_mailbox_message *mailbox_message = NULL;
333
334 struct sqldb_bindval bval[] = {
335 { ":uniqueid", SQLITE_TEXT, { .s = uniqueid } },
336 { ":guid", SQLITE_TEXT, { .s = guid } },
337 { NULL, SQLITE_NULL, { .s = NULL } },
338 };
339
340 struct _mailbox_message_row_rock mbrrock = {
341 NULL, NULL, &mailbox_message,
342 };
343
344 int r = sqldb_exec(backup->db, backup_index_mailbox_message_select_one_sql,
345 bval, _mailbox_message_row_cb, &mbrrock);
346
347 if (r) {
348 syslog(LOG_DEBUG, "%s: something went wrong: %i %s %s",
349 __func__, r, uniqueid, guid);
350 }
351
352 return mailbox_message;
353 }
354
355 struct _mailbox_row_rock {
356 sqldb_t *db;
357 backup_mailbox_foreach_cb proc;
358 void *rock;
359 struct message_guid *match_guid;
360 struct backup_mailbox_list *save_list;
361 struct backup_mailbox **save_one;
362 enum backup_mailbox_want_records want_records;
363 };
364
_mailbox_row_cb(sqlite3_stmt * stmt,void * rock)365 static int _mailbox_row_cb(sqlite3_stmt *stmt, void *rock)
366 {
367 struct _mailbox_row_rock *mbrock = (struct _mailbox_row_rock *) rock;
368 struct backup_mailbox *mailbox = xzmalloc(sizeof *mailbox);
369 int r = 0;
370
371 int column = 0;
372 mailbox->id = _column_int(stmt, column++);
373 mailbox->last_chunk_id = _column_int(stmt, column++);
374 mailbox->uniqueid = xstrdupnull(_column_text(stmt, column++));
375 mailbox->mboxname = xstrdupnull(_column_text(stmt, column++));
376 mailbox->mboxtype = xstrdupnull(_column_text(stmt, column++));
377 mailbox->last_uid = _column_int(stmt, column++);
378 mailbox->highestmodseq = _column_int64(stmt, column++);
379 mailbox->recentuid = _column_int(stmt, column++);
380 mailbox->recenttime = _column_int64(stmt, column++);
381 mailbox->last_appenddate = _column_int64(stmt, column++);
382 mailbox->pop3_last_login = _column_int64(stmt, column++);
383 mailbox->pop3_show_after = _column_int64(stmt, column++);
384 mailbox->uidvalidity = _column_int(stmt, column++);
385 mailbox->partition = xstrdupnull(_column_text(stmt, column++));
386 mailbox->acl = xstrdupnull(_column_text(stmt, column++));
387 mailbox->options = xstrdupnull(_column_text(stmt, column++));
388 mailbox->sync_crc = _column_int(stmt, column++);
389 mailbox->sync_crc_annot = _column_int(stmt, column++);
390 mailbox->quotaroot = xstrdupnull(_column_text(stmt, column++));
391 mailbox->xconvmodseq = _column_int64(stmt, column++);
392 mailbox->annotations = xstrdupnull(_column_text(stmt, column++));
393 mailbox->deleted = _column_int64(stmt, column++);
394
395 if (mbrock->want_records) {
396 if (mbrock->want_records == BACKUP_MAILBOX_MATCH_RECORDS && !mbrock->match_guid) {
397 syslog(LOG_WARNING, "%s: request for guid-matched records without guid",
398 __func__);
399 /* will return all records */
400 }
401
402 mailbox->records = xzmalloc(sizeof *mailbox->records);
403
404 struct sqldb_bindval bval[] = {
405 { ":mailbox_id", SQLITE_INTEGER, { .i = mailbox->id } },
406 { NULL, SQLITE_NULL, { .s = NULL } },
407 };
408
409 struct _mailbox_message_row_rock mbrrock = {
410 mbrock->match_guid,
411 mailbox->records,
412 NULL,
413 };
414
415 r = sqldb_exec(mbrock->db,
416 backup_index_mailbox_message_select_mailbox_sql,
417 bval,
418 _mailbox_message_row_cb, &mbrrock);
419
420 if (r) goto error;
421 }
422
423 if (mbrock->proc)
424 r = mbrock->proc(mailbox, mbrock->rock);
425
426 if (mbrock->save_list)
427 backup_mailbox_list_add(mbrock->save_list, mailbox);
428 else if (mbrock->save_one)
429 *mbrock->save_one = mailbox;
430 else
431 backup_mailbox_free(&mailbox);
432
433 return r;
434
435 error:
436 if (mailbox) backup_mailbox_free(&mailbox);
437 return -1;
438 }
439
backup_mailbox_foreach(struct backup * backup,int chunk_id,enum backup_mailbox_want_records want_records,backup_mailbox_foreach_cb cb,void * rock)440 EXPORTED int backup_mailbox_foreach(struct backup *backup,
441 int chunk_id,
442 enum backup_mailbox_want_records want_records,
443 backup_mailbox_foreach_cb cb,
444 void *rock)
445 {
446 struct _mailbox_row_rock mbrock = { backup->db, cb, rock, NULL,
447 NULL, NULL, want_records};
448
449 struct sqldb_bindval bval[] = {
450 { ":last_chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
451 { NULL, SQLITE_NULL, { .s = NULL } },
452 };
453
454 const char *sql = chunk_id ?
455 backup_index_mailbox_select_chunkid_sql :
456 backup_index_mailbox_select_all_sql;
457
458 int r = sqldb_exec(backup->db, sql, bval, _mailbox_row_cb, &mbrock);
459
460 return r;
461 }
462
backup_get_mailboxes(struct backup * backup,int chunk_id,enum backup_mailbox_want_records want_records)463 EXPORTED struct backup_mailbox_list *backup_get_mailboxes(
464 struct backup *backup,
465 int chunk_id,
466 enum backup_mailbox_want_records want_records)
467 {
468 struct backup_mailbox_list *mailbox_list = xzmalloc(sizeof *mailbox_list);
469
470 struct _mailbox_row_rock mbrock = { backup->db, NULL, NULL, NULL,
471 mailbox_list, NULL, want_records};
472
473 struct sqldb_bindval bval[] = {
474 { ":last_chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
475 { NULL, SQLITE_NULL, { .s = NULL } },
476 };
477
478 const char *sql = chunk_id ?
479 backup_index_mailbox_select_chunkid_sql :
480 backup_index_mailbox_select_all_sql;
481
482 int r = sqldb_exec(backup->db, sql, bval, _mailbox_row_cb, &mbrock);
483
484 if (r) {
485 backup_mailbox_list_empty(mailbox_list);
486 free(mailbox_list);
487 return NULL;
488 }
489
490 return mailbox_list;
491 }
492
backup_get_mailboxes_by_message(struct backup * backup,const struct backup_message * message,enum backup_mailbox_want_records want_records)493 EXPORTED struct backup_mailbox_list *backup_get_mailboxes_by_message(
494 struct backup *backup,
495 const struct backup_message *message,
496 enum backup_mailbox_want_records want_records)
497 {
498 char *guid = xstrdup(message_guid_encode(message->guid));
499 struct backup_mailbox_list *mailbox_list = xzmalloc(sizeof *mailbox_list);
500
501 struct _mailbox_row_rock mbrock = { backup->db, NULL, NULL, NULL,
502 mailbox_list, NULL, want_records };
503
504 if (want_records == BACKUP_MAILBOX_MATCH_RECORDS)
505 mbrock.match_guid = message->guid;
506
507 struct sqldb_bindval bval[] = {
508 { ":guid", SQLITE_TEXT, { .s = guid } },
509 { NULL, SQLITE_NULL, { .s = NULL } },
510 };
511
512 int r = sqldb_exec(backup->db, backup_index_mailbox_select_message_guid_sql,
513 bval, _mailbox_row_cb, &mbrock);
514
515 free(guid);
516
517 if (r) {
518 backup_mailbox_list_empty(mailbox_list);
519 free(mailbox_list);
520 return NULL;
521 }
522
523 return mailbox_list;
524 }
525
backup_get_mailbox_by_uniqueid(struct backup * backup,const char * uniqueid,enum backup_mailbox_want_records want_records)526 EXPORTED struct backup_mailbox *backup_get_mailbox_by_uniqueid(
527 struct backup *backup,
528 const char *uniqueid,
529 enum backup_mailbox_want_records want_records)
530 {
531 struct backup_mailbox *mailbox = NULL;
532
533 struct _mailbox_row_rock mbrock = { backup->db, NULL, NULL, NULL,
534 NULL, &mailbox, want_records };
535
536 struct sqldb_bindval bval[] = {
537 { ":uniqueid", SQLITE_TEXT, { .s = uniqueid } },
538 { NULL, SQLITE_NULL, { .s = NULL } },
539 };
540
541 int r = sqldb_exec(backup->db, backup_index_mailbox_select_uniqueid_sql,
542 bval, _mailbox_row_cb, &mbrock);
543
544 if (r) {
545 if (mailbox) backup_mailbox_free(&mailbox);
546 return NULL;
547 }
548
549 return mailbox;
550 }
551
backup_get_mailbox_by_name(struct backup * backup,const mbname_t * mbname,enum backup_mailbox_want_records want_records)552 EXPORTED struct backup_mailbox *backup_get_mailbox_by_name(
553 struct backup *backup,
554 const mbname_t *mbname,
555 enum backup_mailbox_want_records want_records)
556 {
557 struct backup_mailbox *mailbox = NULL;
558
559 struct _mailbox_row_rock mbrock = { backup->db, NULL, NULL, NULL,
560 NULL, &mailbox, want_records };
561
562 struct sqldb_bindval bval[] = {
563 { ":mboxname", SQLITE_TEXT, { .s = mbname_intname(mbname) } },
564 { NULL, SQLITE_NULL, { .s = NULL } },
565 };
566
567 int r = sqldb_exec(backup->db, backup_index_mailbox_select_mboxname_sql,
568 bval, _mailbox_row_cb, &mbrock);
569
570 if (r) {
571 if (mailbox) backup_mailbox_free(&mailbox);
572 return NULL;
573 }
574
575 return mailbox;
576 }
577
backup_mailbox_to_dlist(const struct backup_mailbox * mailbox)578 EXPORTED struct dlist *backup_mailbox_to_dlist(
579 const struct backup_mailbox *mailbox)
580 {
581 struct dlist *dl = dlist_newkvlist(NULL, "MAILBOX");
582
583 dlist_setatom(dl, "UNIQUEID", mailbox->uniqueid);
584 dlist_setatom(dl, "MBOXNAME", mailbox->mboxname);
585 dlist_setatom(dl, "MBOXTYPE", mailbox->mboxtype);
586 dlist_setnum32(dl, "LAST_UID", mailbox->last_uid);
587 dlist_setnum64(dl, "HIGHESTMODSEQ", mailbox->highestmodseq);
588 dlist_setnum32(dl, "RECENTUID", mailbox->recentuid);
589 dlist_setdate(dl, "RECENTTIME", mailbox->recenttime);
590 dlist_setdate(dl, "LAST_APPENDDATE", mailbox->last_appenddate);
591 dlist_setdate(dl, "POP3_LAST_LOGIN", mailbox->pop3_last_login);
592 dlist_setdate(dl, "POP3_SHOW_AFTER", mailbox->pop3_show_after);
593 dlist_setnum32(dl, "UIDVALIDITY", mailbox->uidvalidity);
594 dlist_setatom(dl, "PARTITION", mailbox->partition);
595 dlist_setatom(dl, "ACL", mailbox->acl);
596 dlist_setatom(dl, "OPTIONS", mailbox->options);
597 dlist_setnum32(dl, "SYNC_CRC", mailbox->sync_crc);
598 dlist_setnum32(dl, "SYNC_CRC_ANNOT", mailbox->sync_crc_annot);
599 dlist_setatom(dl, "QUOTAROOT", mailbox->quotaroot);
600 dlist_setnum64(dl, "XCONVMODSEQ", mailbox->xconvmodseq);
601
602 /* if any flags or annotations from the index can't be parsed into dlist
603 * format, we just quietly leave them out, and trust sync_client to notice
604 * the difference and send updates to fix them */
605
606 if (mailbox->annotations) {
607 struct dlist *annots = NULL;
608 dlist_parsemap(&annots, 0, 1, mailbox->annotations,
609 strlen(mailbox->annotations));
610 if (annots) {
611 annots->name = xstrdup("ANNOTATIONS");
612 dlist_stitch(dl, annots);
613 }
614 }
615
616 if (mailbox->records) {
617 struct dlist *records = dlist_newlist(NULL, "RECORD");
618 struct backup_mailbox_message *mailbox_message = mailbox->records->head;
619
620 while (mailbox_message) {
621 struct dlist *record = dlist_newkvlist(records, NULL);
622 struct dlist *flags = NULL;
623
624 dlist_setnum32(record, "UID", mailbox_message->uid);
625 dlist_setnum64(record, "MODSEQ", mailbox_message->modseq);
626 dlist_setdate(record, "LAST_UPDATED", mailbox_message->last_updated);
627 dlist_setdate(record, "INTERNALDATE", mailbox_message->internaldate);
628 dlist_setguid(record, "GUID", &mailbox_message->guid);
629 dlist_setnum32(record, "SIZE", mailbox_message->size);
630
631 /* FLAGS field is mandatory */
632 if (mailbox_message->flags) {
633 dlist_parsemap(&flags, 0, 1, mailbox_message->flags,
634 strlen(mailbox_message->flags));
635 flags->name = xstrdup("FLAGS");
636 dlist_stitch(record, flags);
637 }
638 else {
639 flags = dlist_newlist(record, "FLAGS");
640 }
641
642 /* convert expunged to flag */
643 if (mailbox_message->expunged)
644 dlist_setflag(flags, "FLAG", "\\Expunged");
645
646 if (mailbox_message->annotations) {
647 struct dlist *annots = NULL;
648 dlist_parsemap(&annots, 0, 1, mailbox_message->annotations,
649 strlen(mailbox_message->annotations));
650 if (annots) {
651 annots->name = xstrdup("ANNOTATIONS");
652 dlist_stitch(record, annots);
653 }
654 }
655
656 mailbox_message = mailbox_message->next;
657 }
658
659 dlist_stitch(dl, records);
660 }
661
662 return dl;
663 }
664
backup_mailbox_clone(const struct backup_mailbox * mailbox)665 EXPORTED struct backup_mailbox *backup_mailbox_clone(
666 const struct backup_mailbox *mailbox)
667 {
668 struct backup_mailbox *clone = xzmalloc(sizeof *clone);
669
670 clone->id = mailbox->id;
671 clone->last_chunk_id = mailbox->last_chunk_id;
672 clone->last_uid = mailbox->last_uid;
673 clone->highestmodseq = mailbox->highestmodseq;
674 clone->recentuid = mailbox->recentuid;
675 clone->recenttime = mailbox->recenttime;
676 clone->last_appenddate = mailbox->last_appenddate;
677 clone->pop3_last_login = mailbox->pop3_last_login;
678 clone->pop3_show_after = mailbox->pop3_show_after;
679 clone->uidvalidity = mailbox->uidvalidity;
680 clone->sync_crc = mailbox->sync_crc;
681 clone->sync_crc_annot = mailbox->sync_crc_annot;
682 clone->xconvmodseq = mailbox->xconvmodseq;
683 clone->deleted = mailbox->deleted;
684
685 clone->uniqueid = xstrdupnull(mailbox->uniqueid);
686 clone->mboxname = xstrdupnull(mailbox->mboxname);
687 clone->mboxtype = xstrdupnull(mailbox->mboxtype);
688 clone->partition = xstrdupnull(mailbox->partition);
689 clone->acl = xstrdupnull(mailbox->acl);
690 clone->options = xstrdupnull(mailbox->options);
691 clone->quotaroot = xstrdupnull(mailbox->quotaroot);
692 clone->annotations = xstrdupnull(mailbox->annotations);
693
694 if (mailbox->records) {
695 struct backup_mailbox_message *iter;
696
697 clone->records = xzmalloc(sizeof *clone->records);
698
699 for (iter = mailbox->records->head; iter; iter = iter->next) {
700 backup_mailbox_message_list_add(clone->records,
701 backup_mailbox_message_clone(iter));
702 }
703 }
704
705 return clone;
706 }
707
backup_mailbox_message_clone(const struct backup_mailbox_message * orig)708 EXPORTED struct backup_mailbox_message *backup_mailbox_message_clone(
709 const struct backup_mailbox_message *orig)
710 {
711 struct backup_mailbox_message *clone = xzmalloc(sizeof *clone);
712
713 clone->id = orig->id;
714 clone->mailbox_id = orig->mailbox_id;
715 clone->message_id = orig->message_id;
716 clone->last_chunk_id = orig->last_chunk_id;
717 clone->uid = orig->uid;
718 clone->modseq = orig->modseq;
719 clone->last_updated = orig->last_updated;
720 clone->internaldate = orig->internaldate;
721 clone->guid = orig->guid;
722 clone->size = orig->size;
723 clone->expunged = orig->expunged;
724
725 clone->mailbox_uniqueid = xstrdupnull(orig->mailbox_uniqueid);
726 clone->flags = xstrdupnull(orig->flags);
727 clone->annotations = xstrdupnull(orig->annotations);
728
729 return clone;
730 }
731
backup_mailbox_message_free(struct backup_mailbox_message ** mailbox_messagep)732 EXPORTED void backup_mailbox_message_free(
733 struct backup_mailbox_message **mailbox_messagep)
734 {
735 struct backup_mailbox_message *mailbox_message = *mailbox_messagep;
736 *mailbox_messagep = NULL;
737
738 if (mailbox_message->flags) free(mailbox_message->flags);
739 if (mailbox_message->annotations) free(mailbox_message->annotations);
740 if (mailbox_message->mailbox_uniqueid) free(mailbox_message->mailbox_uniqueid);
741
742 free(mailbox_message);
743 }
744
backup_mailbox_free(struct backup_mailbox ** mailboxp)745 EXPORTED void backup_mailbox_free(struct backup_mailbox **mailboxp)
746 {
747 struct backup_mailbox *mailbox = *mailboxp;
748 *mailboxp = NULL;
749
750 if (mailbox->uniqueid) free(mailbox->uniqueid);
751 if (mailbox->mboxname) free(mailbox->mboxname);
752 if (mailbox->mboxtype) free(mailbox->mboxtype);
753 if (mailbox->partition) free(mailbox->partition);
754 if (mailbox->acl) free(mailbox->acl);
755 if (mailbox->options) free(mailbox->options);
756 if (mailbox->quotaroot) free(mailbox->quotaroot);
757 if (mailbox->annotations) free(mailbox->annotations);
758
759 if (mailbox->records) {
760 backup_mailbox_message_list_empty(mailbox->records);
761 free(mailbox->records);
762 }
763
764 free(mailbox);
765 }
766
_get_message_id_cb(sqlite3_stmt * stmt,void * rock)767 static int _get_message_id_cb(sqlite3_stmt *stmt, void *rock) {
768 int *idp = (int *) rock;
769
770 *idp = _column_int(stmt, 0);
771
772 return 0;
773 }
774
backup_get_message_id(struct backup * backup,const char * guid)775 EXPORTED int backup_get_message_id(struct backup *backup, const char *guid)
776 {
777 struct sqldb_bindval bval[] = {
778 { ":guid", SQLITE_TEXT, { .s = guid } },
779 { NULL, SQLITE_NULL, { .s = NULL } },
780 };
781
782 int id = 0;
783
784 int r = sqldb_exec(backup->db, backup_index_message_select_guid_sql, bval,
785 _get_message_id_cb, &id);
786 if (r) {
787 syslog(LOG_ERR, "%s: something went wrong: %i %s\n",
788 __func__, r, guid);
789 return -1;
790 }
791
792 return id;
793 }
794
backup_message_free(struct backup_message ** messagep)795 EXPORTED void backup_message_free(struct backup_message **messagep)
796 {
797 struct backup_message *message = *messagep;
798 *messagep = NULL;
799
800 if (message->guid) free(message->guid);
801 if (message->partition) free(message->partition);
802
803 free(message);
804 }
805
806 struct message_row_rock {
807 backup_message_foreach_cb proc;
808 void *rock;
809 struct backup_message **save_one;
810 };
811
_message_row_cb(sqlite3_stmt * stmt,void * rock)812 static int _message_row_cb(sqlite3_stmt *stmt, void *rock)
813 {
814 struct message_row_rock *mrock = (struct message_row_rock *) rock;
815 struct backup_message *message = xzmalloc(sizeof *message);
816 const char *guid_str = NULL;
817 int column = 0;
818 int r = 0;
819
820 message->id = _column_int(stmt, column++);
821 guid_str = _column_text(stmt, column++);
822 message->partition = xstrdupnull(_column_text(stmt, column++));
823 message->chunk_id = _column_int(stmt, column++);
824 message->offset = _column_int64(stmt, column++);
825 message->length = _column_int64(stmt, column++);
826
827 message->guid = xzmalloc(sizeof *message->guid);
828 if (!message_guid_decode(message->guid, guid_str)) goto error;
829
830 if (mrock->proc)
831 r = mrock->proc(message, mrock->rock);
832
833 if (mrock->save_one)
834 *mrock->save_one = message;
835 else
836 backup_message_free(&message);
837
838 return r;
839
840 error:
841 if (message) backup_message_free(&message);
842 return -1;
843 }
844
backup_get_message(struct backup * backup,const struct message_guid * guid)845 EXPORTED struct backup_message *backup_get_message(struct backup *backup,
846 const struct message_guid *guid)
847 {
848 struct sqldb_bindval bval[] = {
849 { ":guid", SQLITE_TEXT, { .s = message_guid_encode(guid) } },
850 { NULL, SQLITE_NULL, { .s = NULL } },
851 };
852
853 struct backup_message *bm = NULL;
854
855 struct message_row_rock mrock = { NULL, NULL, &bm };
856
857 int r = sqldb_exec(backup->db, backup_index_message_select_guid_sql, bval,
858 _message_row_cb, &mrock);
859 if (r) {
860 syslog(LOG_ERR, "%s: something went wrong: %i %s\n",
861 __func__, r, message_guid_encode(guid));
862 if (bm) backup_message_free(&bm);
863 return NULL;
864 }
865
866 return bm;
867 }
868
backup_message_foreach(struct backup * backup,int chunk_id,const time_t * sincep,backup_message_foreach_cb cb,void * rock)869 EXPORTED int backup_message_foreach(struct backup *backup,
870 int chunk_id, const time_t *sincep,
871 backup_message_foreach_cb cb, void *rock)
872 {
873 const char *sql = NULL;
874
875 struct sqldb_bindval bval[] = {
876 { ":chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
877 { ":since", SQLITE_NULL, { .s = NULL } },
878 { NULL, SQLITE_NULL, { .s = NULL } },
879 };
880
881 struct message_row_rock mrock = { cb, rock, NULL };
882
883 if (chunk_id) {
884 if (sincep) {
885 struct sqldb_bindval *since_bval = &bval[1];
886 assert(strcmp(since_bval->name, ":since") == 0);
887 since_bval->type = SQLITE_INTEGER;
888 since_bval->val.i = *sincep;
889 sql = backup_index_message_select_live_chunkid_sql;
890 }
891 else {
892 sql = backup_index_message_select_chunkid_sql;
893 }
894 }
895 else {
896 sql = backup_index_message_select_all_sql;
897 }
898
899 return sqldb_exec(backup->db, sql, bval, _message_row_cb, &mrock);
900 }
901
backup_chunk_list_add(struct backup_chunk_list * list,struct backup_chunk * chunk)902 EXPORTED void backup_chunk_list_add(struct backup_chunk_list *list,
903 struct backup_chunk *chunk)
904 {
905 chunk->next = NULL;
906
907 if (list->tail)
908 list->tail->next = chunk;
909
910 if (!list->head)
911 list->head = chunk;
912
913 list->tail = chunk;
914 list->count++;
915 }
916
backup_chunk_list_empty(struct backup_chunk_list * list)917 EXPORTED void backup_chunk_list_empty(struct backup_chunk_list *list)
918 {
919 struct backup_chunk *curr, *next;
920 curr = list->head;
921 while (curr) {
922 next = curr->next;
923 backup_chunk_free(&curr);
924 curr = next;
925 }
926
927 list->head = list->tail = NULL;
928 list->count = 0;
929 }
930
backup_chunk_list_free(struct backup_chunk_list ** chunk_listp)931 EXPORTED void backup_chunk_list_free(struct backup_chunk_list **chunk_listp)
932 {
933 struct backup_chunk_list *chunk_list = *chunk_listp;
934 *chunk_listp = NULL;
935
936 backup_chunk_list_empty(chunk_list);
937 free(chunk_list);
938 }
939
940 struct _chunk_row_rock {
941 struct backup_chunk_list *save_list;
942 struct backup_chunk **save_one;
943 };
944
_chunk_row_cb(sqlite3_stmt * stmt,void * rock)945 static int _chunk_row_cb(sqlite3_stmt *stmt, void *rock)
946 {
947 struct _chunk_row_rock *crock = (struct _chunk_row_rock *) rock;
948
949 struct backup_chunk *chunk = xzmalloc(sizeof(*chunk));
950
951 int column = 0;
952 chunk->id = _column_int(stmt, column++);
953 chunk->ts_start = _column_int64(stmt, column++);
954 chunk->ts_end = _column_int64(stmt, column++);
955 chunk->offset = _column_int64(stmt, column++);
956 chunk->length = _column_int64(stmt, column++);
957 chunk->file_sha1 = xstrdupnull(_column_text(stmt, column++));
958 chunk->data_sha1 = xstrdupnull(_column_text(stmt, column++));
959
960 if (crock->save_list) {
961 backup_chunk_list_add(crock->save_list, chunk);
962 }
963 else if (crock->save_one) {
964 *crock->save_one = chunk;
965 }
966 else {
967 syslog(LOG_DEBUG, "%s: useless invocation with nowhere to save to", __func__);
968 backup_chunk_free(&chunk);
969 }
970
971 return 0;
972 }
973
backup_get_chunks(struct backup * backup)974 EXPORTED struct backup_chunk_list *backup_get_chunks(struct backup *backup)
975 {
976 struct backup_chunk_list *chunk_list = xzmalloc(sizeof *chunk_list);
977
978 struct _chunk_row_rock crock = { chunk_list, NULL };
979
980 int r = sqldb_exec(backup->db, backup_index_chunk_select_all_sql,
981 NULL, _chunk_row_cb, &crock);
982
983 if (r) {
984 backup_chunk_list_free(&chunk_list);
985 return NULL;
986 }
987
988 return chunk_list;
989 }
990
backup_get_live_chunks(struct backup * backup,time_t since)991 EXPORTED struct backup_chunk_list *backup_get_live_chunks(struct backup *backup,
992 time_t since)
993 {
994 struct backup_chunk_list *chunk_list = xzmalloc(sizeof *chunk_list);
995
996 struct _chunk_row_rock crock = { chunk_list, NULL };
997
998 struct sqldb_bindval bval[] = {
999 { ":since", SQLITE_INTEGER, { .i = since } },
1000 { NULL, SQLITE_NULL, { .s = NULL } },
1001 };
1002
1003 int r = sqldb_exec(backup->db, backup_index_chunk_select_live_sql,
1004 bval, _chunk_row_cb, &crock);
1005
1006 if (r) {
1007 backup_chunk_list_free(&chunk_list);
1008 return NULL;
1009 }
1010
1011 return chunk_list;
1012 }
1013
backup_get_chunk(struct backup * backup,int chunk_id)1014 EXPORTED struct backup_chunk *backup_get_chunk(struct backup *backup,
1015 int chunk_id)
1016 {
1017 struct backup_chunk *chunk = NULL;
1018 struct _chunk_row_rock crock = { NULL, &chunk };
1019
1020 struct sqldb_bindval bval[] = {
1021 { ":id", SQLITE_INTEGER, { .i = chunk_id } },
1022 { NULL, SQLITE_NULL, { .s = NULL } },
1023 };
1024
1025 int r = sqldb_exec(backup->db, backup_index_chunk_select_id_sql,
1026 bval, _chunk_row_cb, &crock);
1027
1028 if (r) {
1029 if (chunk) backup_chunk_free(&chunk);
1030 return NULL;
1031 }
1032
1033 return chunk;
1034 }
1035
backup_get_latest_chunk(struct backup * backup)1036 EXPORTED struct backup_chunk *backup_get_latest_chunk(struct backup *backup)
1037 {
1038 struct backup_chunk *chunk = NULL;
1039 struct _chunk_row_rock crock = { NULL, &chunk };
1040
1041 int r = sqldb_exec(backup->db, backup_index_chunk_select_latest_sql,
1042 NULL, _chunk_row_cb, &crock);
1043
1044 if (r) {
1045 if (chunk) backup_chunk_free(&chunk);
1046 return NULL;
1047 }
1048
1049 return chunk;
1050 }
1051
backup_chunk_free(struct backup_chunk ** chunkp)1052 EXPORTED void backup_chunk_free(struct backup_chunk **chunkp)
1053 {
1054 struct backup_chunk *chunk = *chunkp;
1055 *chunkp = NULL;
1056
1057 if (chunk->file_sha1) free(chunk->file_sha1);
1058 if (chunk->data_sha1) free(chunk->data_sha1);
1059
1060 free(chunk);
1061 }
1062
1063 struct _seen_row_rock {
1064 backup_seen_foreach_cb proc;
1065 void *rock;
1066 };
1067
_seen_row_cb(sqlite3_stmt * stmt,void * rock)1068 static int _seen_row_cb(sqlite3_stmt *stmt, void *rock)
1069 {
1070 struct _seen_row_rock *seenrock = (struct _seen_row_rock *) rock;
1071 struct backup_seen *seen = xzmalloc(sizeof *seen);
1072 int r = 0;
1073
1074 int column = 0;
1075 seen->id = _column_int(stmt, column++);
1076 seen->last_chunk_id = _column_int(stmt, column++);
1077 seen->uniqueid = xstrdupnull(_column_text(stmt, column++));
1078 seen->lastread = _column_int64(stmt, column++);
1079 seen->lastuid = _column_int(stmt, column++);
1080 seen->lastchange = _column_int64(stmt, column++);
1081 seen->seenuids = xstrdupnull(_column_text(stmt, column++));
1082
1083 if (seenrock->proc)
1084 r = seenrock->proc(seen, seenrock->rock);
1085
1086 backup_seen_free(&seen);
1087
1088 return r;
1089 }
1090
backup_seen_foreach(struct backup * backup,int chunk_id,backup_seen_foreach_cb cb,void * rock)1091 EXPORTED int backup_seen_foreach(struct backup *backup,
1092 int chunk_id,
1093 backup_seen_foreach_cb cb,
1094 void *rock)
1095 {
1096 struct _seen_row_rock seenrock = { cb, rock };
1097
1098 struct sqldb_bindval bval[] = {
1099 { ":last_chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
1100 { NULL, SQLITE_NULL, { .s = NULL } },
1101 };
1102
1103 const char *sql = chunk_id ?
1104 backup_index_seen_select_chunkid_sql :
1105 backup_index_seen_select_all_sql;
1106
1107 int r = sqldb_exec(backup->db, sql, bval, _seen_row_cb, &seenrock);
1108
1109 return r;
1110 }
1111
backup_seen_free(struct backup_seen ** seenp)1112 EXPORTED void backup_seen_free(struct backup_seen **seenp)
1113 {
1114 struct backup_seen *seen = *seenp;
1115 *seenp = NULL;
1116
1117 if (seen->uniqueid) free(seen->uniqueid);
1118 if (seen->seenuids) free(seen->seenuids);
1119
1120 free(seen);
1121 }
1122
1123 struct _subscription_row_rock {
1124 backup_subscription_foreach_cb proc;
1125 void *rock;
1126 };
1127
_subscription_row_cb(sqlite3_stmt * stmt,void * rock)1128 static int _subscription_row_cb(sqlite3_stmt *stmt, void *rock)
1129 {
1130 struct _subscription_row_rock *subrock = (struct _subscription_row_rock *) rock;
1131 struct backup_subscription *sub = xzmalloc(sizeof *sub);
1132 int r = 0;
1133
1134 int column = 0;
1135 sub->id = _column_int(stmt, column++);
1136 sub->last_chunk_id = _column_int(stmt, column++);
1137 sub->mboxname = xstrdupnull(_column_text(stmt, column++));
1138 sub->unsubscribed = _column_int64(stmt, column++);
1139
1140 if (subrock->proc)
1141 r = subrock->proc(sub, subrock->rock);
1142
1143 backup_subscription_free(&sub);
1144
1145 return r;
1146 }
1147
backup_subscription_foreach(struct backup * backup,int chunk_id,backup_subscription_foreach_cb cb,void * rock)1148 EXPORTED int backup_subscription_foreach(struct backup *backup,
1149 int chunk_id,
1150 backup_subscription_foreach_cb cb,
1151 void *rock)
1152 {
1153 struct _subscription_row_rock subrock = { cb, rock };
1154
1155 struct sqldb_bindval bval[] = {
1156 { ":last_chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
1157 { NULL, SQLITE_NULL, { .s = NULL } },
1158 };
1159
1160 const char *sql = chunk_id ?
1161 backup_index_subscription_select_chunkid_sql :
1162 backup_index_subscription_select_all_sql;
1163
1164 int r = sqldb_exec(backup->db, sql, bval, _subscription_row_cb, &subrock);
1165
1166 return r;
1167 }
1168
backup_subscription_free(struct backup_subscription ** subp)1169 EXPORTED void backup_subscription_free(struct backup_subscription **subp)
1170 {
1171 struct backup_subscription *sub = *subp;
1172 *subp = NULL;
1173
1174 if (sub->mboxname) free(sub->mboxname);
1175
1176 free(sub);
1177 }
1178
1179 struct _sieve_row_rock {
1180 backup_sieve_foreach_cb proc;
1181 void *rock;
1182 };
1183
_sieve_row_cb(sqlite3_stmt * stmt,void * rock)1184 static int _sieve_row_cb(sqlite3_stmt *stmt, void *rock)
1185 {
1186 struct _sieve_row_rock *srock = (struct _sieve_row_rock *) rock;
1187 struct backup_sieve *sieve = xzmalloc(sizeof *sieve);
1188 int r = 0;
1189
1190 int column = 0;
1191 sieve->id = _column_int(stmt, column++);
1192 sieve->chunk_id = _column_int(stmt, column++);
1193 sieve->last_update = _column_int64(stmt, column++);
1194 sieve->filename = xstrdupnull(_column_text(stmt, column++));
1195 message_guid_decode(&sieve->guid, _column_text(stmt, column++));
1196 sieve->offset = _column_int64(stmt, column++);
1197 sieve->deleted = _column_int64(stmt, column++);
1198
1199 if (srock->proc)
1200 r = srock->proc(sieve, srock->rock);
1201
1202 backup_sieve_free(&sieve);
1203
1204 return r;
1205 }
1206
backup_sieve_foreach(struct backup * backup,int chunk_id,backup_sieve_foreach_cb cb,void * rock)1207 EXPORTED int backup_sieve_foreach(struct backup *backup,
1208 int chunk_id,
1209 backup_sieve_foreach_cb cb,
1210 void *rock)
1211 {
1212 struct _sieve_row_rock srock = { cb, rock };
1213
1214 struct sqldb_bindval bval[] = {
1215 { ":chunk_id", SQLITE_INTEGER, { .i = chunk_id } },
1216 { NULL, SQLITE_NULL, { .s = NULL } },
1217 };
1218
1219 const char *sql = chunk_id ?
1220 backup_index_sieve_select_chunkid_sql :
1221 backup_index_sieve_select_all_sql;
1222
1223 int r = sqldb_exec(backup->db, sql, bval, _sieve_row_cb, &srock);
1224
1225 return r;
1226 }
1227
backup_sieve_free(struct backup_sieve ** sievep)1228 EXPORTED void backup_sieve_free(struct backup_sieve **sievep)
1229 {
1230 struct backup_sieve *sieve = *sievep;
1231 *sievep = NULL;
1232
1233 if (sieve->filename) free(sieve->filename);
1234
1235 free(sieve);
1236 }
1237