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