1 /* webdav_db.c -- implementation of per-user WebDAV database
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
44 #include <config.h>
45
46 #ifdef WITH_DAV
47
48 #include <syslog.h>
49 #include <string.h>
50
51 #include "webdav_db.h"
52 #include "cyrusdb.h"
53 #include "httpd.h"
54 #include "http_dav.h"
55 #include "libconfig.h"
56 #include "util.h"
57 #include "xstrlcat.h"
58 #include "xmalloc.h"
59
60
61 enum {
62 STMT_SELRSRC,
63 STMT_SELUID,
64 STMT_SELMBOX,
65 STMT_INSERT,
66 STMT_UPDATE,
67 STMT_DELETE,
68 STMT_DELMBOX,
69 STMT_BEGIN,
70 STMT_COMMIT,
71 STMT_ROLLBACK
72 };
73
74 #define NUM_STMT 10
75
76 struct webdav_db {
77 sqldb_t *db; /* DB handle */
78 char *userid;
79 struct buf mailbox; /* buffers for copies of column text */
80 struct buf resource;
81 struct buf lock_token;
82 struct buf lock_owner;
83 struct buf lock_ownerid;
84 struct buf filename;
85 struct buf type;
86 struct buf subtype;
87 struct buf res_uid;
88 unsigned ref_count;
89 };
90
91 static int webdav_initialized = 0;
92
done_cb(void * rock)93 static void done_cb(void *rock __attribute__((unused))) {
94 webdav_done();
95 }
96
init_internal()97 static void init_internal() {
98 if (!webdav_initialized) {
99 webdav_init();
100 cyrus_modules_add(done_cb, NULL);
101 }
102 }
103
webdav_init(void)104 EXPORTED int webdav_init(void)
105 {
106 int r = sqldb_init();
107 if (!r) webdav_initialized = 1;
108 return r;
109 }
110
111
webdav_done(void)112 EXPORTED int webdav_done(void)
113 {
114 int r = sqldb_done();
115 if (!r) webdav_initialized = 0;
116 return r;
117 }
118
119 /* Open DAV DB corresponding to userid */
webdav_open_userid(const char * userid)120 EXPORTED struct webdav_db *webdav_open_userid(const char *userid)
121 {
122 struct webdav_db *webdavdb = NULL;
123
124 init_internal();
125
126 sqldb_t *db = dav_open_userid(userid);
127 if (!db) return NULL;
128
129 webdavdb = xzmalloc(sizeof(struct webdav_db));
130 webdavdb->userid = xstrdup(userid);
131 webdavdb->db = db;
132
133 return webdavdb;
134 }
135
136 /* Open DAV DB corresponding to mailbox */
webdav_open_mailbox(struct mailbox * mailbox)137 EXPORTED struct webdav_db *webdav_open_mailbox(struct mailbox *mailbox)
138 {
139 struct webdav_db *webdavdb = NULL;
140 char *userid = mboxname_to_userid(mailbox->name);
141
142 init_internal();
143
144 if (userid) {
145 webdavdb = webdav_open_userid(userid);
146 free(userid);
147 return webdavdb;
148 }
149
150 sqldb_t *db = dav_open_mailbox(mailbox);
151 if (!db) return NULL;
152
153 webdavdb = xzmalloc(sizeof(struct webdav_db));
154 webdavdb->db = db;
155
156 return webdavdb;
157 }
158
159 /* Close DAV DB */
webdav_close(struct webdav_db * webdavdb)160 EXPORTED int webdav_close(struct webdav_db *webdavdb)
161 {
162 int r = 0;
163
164 if (!webdavdb) return 0;
165
166 buf_free(&webdavdb->mailbox);
167 buf_free(&webdavdb->resource);
168 buf_free(&webdavdb->lock_token);
169 buf_free(&webdavdb->lock_owner);
170 buf_free(&webdavdb->lock_ownerid);
171 buf_free(&webdavdb->filename);
172 buf_free(&webdavdb->type);
173 buf_free(&webdavdb->subtype);
174 buf_free(&webdavdb->res_uid);
175
176 r = sqldb_close(&webdavdb->db);
177
178 free(webdavdb->userid);
179 free(webdavdb);
180
181 return r;
182 }
183
webdav_begin(struct webdav_db * webdavdb)184 EXPORTED int webdav_begin(struct webdav_db *webdavdb)
185 {
186 return sqldb_begin(webdavdb->db, "webdav");
187 }
188
webdav_commit(struct webdav_db * webdavdb)189 EXPORTED int webdav_commit(struct webdav_db *webdavdb)
190 {
191 return sqldb_commit(webdavdb->db, "webdav");
192 }
193
webdav_abort(struct webdav_db * webdavdb)194 EXPORTED int webdav_abort(struct webdav_db *webdavdb)
195 {
196 return sqldb_rollback(webdavdb->db, "webdav");
197 }
198
199
200 struct read_rock {
201 struct webdav_db *db;
202 struct webdav_data *wdata;
203 int tombstones;
204 webdav_cb_t *cb;
205 void *rock;
206 };
207
column_text_to_buf(const char * text,struct buf * buf)208 static const char *column_text_to_buf(const char *text, struct buf *buf)
209 {
210 if (text) {
211 buf_setcstr(buf, text);
212 text = buf_cstring(buf);
213 }
214
215 return text;
216 }
217
read_cb(sqlite3_stmt * stmt,void * rock)218 static int read_cb(sqlite3_stmt *stmt, void *rock)
219 {
220 struct read_rock *rrock = (struct read_rock *) rock;
221 struct webdav_db *db = rrock->db;
222 struct webdav_data *wdata = rrock->wdata;
223 int r = 0;
224
225 memset(wdata, 0, sizeof(struct webdav_data));
226
227 wdata->dav.alive = sqlite3_column_int(stmt, 14);
228 wdata->dav.modseq = sqlite3_column_int64(stmt, 15);
229 wdata->dav.createdmodseq = sqlite3_column_int64(stmt, 16);
230 if (!rrock->tombstones && !wdata->dav.alive)
231 return 0;
232
233 wdata->dav.rowid = sqlite3_column_int(stmt, 0);
234 wdata->dav.creationdate = sqlite3_column_int(stmt, 1);
235 wdata->dav.imap_uid = sqlite3_column_int(stmt, 4);
236 wdata->dav.lock_expire = sqlite3_column_int(stmt, 8);
237 wdata->ref_count = sqlite3_column_int(stmt, 13);
238
239 if (rrock->cb) {
240 /* We can use the column data directly for the callback */
241 wdata->dav.mailbox = (const char *) sqlite3_column_text(stmt, 2);
242 wdata->dav.resource = (const char *) sqlite3_column_text(stmt, 3);
243 wdata->dav.lock_token = (const char *) sqlite3_column_text(stmt, 5);
244 wdata->dav.lock_owner = (const char *) sqlite3_column_text(stmt, 6);
245 wdata->dav.lock_ownerid = (const char *) sqlite3_column_text(stmt, 7);
246 wdata->filename = (const char *) sqlite3_column_text(stmt, 9);
247 wdata->type = (const char *) sqlite3_column_text(stmt, 10);
248 wdata->subtype = (const char *) sqlite3_column_text(stmt, 11);
249 wdata->res_uid = (const char *) sqlite3_column_text(stmt, 12);
250 r = rrock->cb(rrock->rock, wdata);
251 }
252 else {
253 /* For single row SELECTs like webdav_read(),
254 * we need to make a copy of the column data before
255 * it gets flushed by sqlite3_step() or sqlite3_reset() */
256 wdata->dav.mailbox =
257 column_text_to_buf((const char *) sqlite3_column_text(stmt, 2),
258 &db->mailbox);
259 wdata->dav.resource =
260 column_text_to_buf((const char *) sqlite3_column_text(stmt, 3),
261 &db->resource);
262 wdata->dav.lock_token =
263 column_text_to_buf((const char *) sqlite3_column_text(stmt, 5),
264 &db->lock_token);
265 wdata->dav.lock_owner =
266 column_text_to_buf((const char *) sqlite3_column_text(stmt, 6),
267 &db->lock_owner);
268 wdata->dav.lock_ownerid =
269 column_text_to_buf((const char *) sqlite3_column_text(stmt, 7),
270 &db->lock_ownerid);
271 wdata->filename =
272 column_text_to_buf((const char *) sqlite3_column_text(stmt, 9),
273 &db->filename);
274 wdata->type =
275 column_text_to_buf((const char *) sqlite3_column_text(stmt, 10),
276 &db->type);
277 wdata->subtype =
278 column_text_to_buf((const char *) sqlite3_column_text(stmt, 11),
279 &db->subtype);
280 wdata->res_uid =
281 column_text_to_buf((const char *) sqlite3_column_text(stmt, 12),
282 &db->res_uid);
283 }
284
285 return r;
286 }
287
288 #define CMD_GETFIELDS \
289 "SELECT rowid, creationdate, mailbox, resource, imap_uid," \
290 " lock_token, lock_owner, lock_ownerid, lock_expire," \
291 " filename, type, subtype, res_uid, ref_count, alive," \
292 " modseq, createdmodseq" \
293 " FROM dav_objs" \
294
295
296 #define CMD_SELRSRC CMD_GETFIELDS \
297 " WHERE mailbox = :mailbox AND resource = :resource;"
298
webdav_lookup_resource(struct webdav_db * webdavdb,const char * mailbox,const char * resource,struct webdav_data ** result,int tombstones)299 EXPORTED int webdav_lookup_resource(struct webdav_db *webdavdb,
300 const char *mailbox, const char *resource,
301 struct webdav_data **result,
302 int tombstones)
303 {
304 struct sqldb_bindval bval[] = {
305 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
306 { ":resource", SQLITE_TEXT, { .s = resource } },
307 { NULL, SQLITE_NULL, { .s = NULL } } };
308 static struct webdav_data wdata;
309 struct read_rock rrock = { webdavdb, &wdata, tombstones, NULL, NULL };
310 int r;
311
312 *result = memset(&wdata, 0, sizeof(struct webdav_data));
313
314 r = sqldb_exec(webdavdb->db, CMD_SELRSRC, bval, &read_cb, &rrock);
315 if (!r && !wdata.dav.rowid) r = CYRUSDB_NOTFOUND;
316
317 return r;
318 }
319
320
321 #define CMD_SELIMAPUID CMD_GETFIELDS \
322 " WHERE mailbox = :mailbox AND imap_uid = :imap_uid;"
323
webdav_lookup_imapuid(struct webdav_db * webdavdb,const char * mailbox,int imap_uid,struct webdav_data ** result,int tombstones)324 EXPORTED int webdav_lookup_imapuid(struct webdav_db *webdavdb,
325 const char *mailbox, int imap_uid,
326 struct webdav_data **result,
327 int tombstones)
328 {
329 struct sqldb_bindval bval[] = {
330 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
331 { ":imap_uid", SQLITE_INTEGER, { .i = imap_uid } },
332 { NULL, SQLITE_NULL, { .s = NULL } } };
333 static struct webdav_data wdata;
334 struct read_rock rrock = { webdavdb, &wdata, tombstones, NULL, NULL };
335 int r;
336
337 *result = memset(&wdata, 0, sizeof(struct webdav_data));
338
339 r = sqldb_exec(webdavdb->db, CMD_SELIMAPUID, bval, &read_cb, &rrock);
340 if (!r && !wdata.dav.rowid) r = CYRUSDB_NOTFOUND;
341
342 wdata.dav.mailbox = mailbox;
343 wdata.dav.imap_uid = imap_uid;
344
345 return r;
346 }
347
348
349 #define CMD_SELUID CMD_GETFIELDS \
350 " WHERE res_uid = :res_uid AND alive = 1;"
351
webdav_lookup_uid(struct webdav_db * webdavdb,const char * res_uid,struct webdav_data ** result)352 EXPORTED int webdav_lookup_uid(struct webdav_db *webdavdb, const char *res_uid,
353 struct webdav_data **result)
354 {
355 struct sqldb_bindval bval[] = {
356 { ":res_uid", SQLITE_TEXT, { .s = res_uid } },
357 { NULL, SQLITE_NULL, { .s = NULL } } };
358 static struct webdav_data wdata;
359 struct read_rock rrock = { webdavdb, &wdata, 0, NULL, NULL };
360 int r;
361
362 *result = memset(&wdata, 0, sizeof(struct webdav_data));
363
364 r = sqldb_exec(webdavdb->db, CMD_SELUID, bval, &read_cb, &rrock);
365 if (!r && !wdata.dav.rowid) r = CYRUSDB_NOTFOUND;
366
367 return r;
368 }
369
370
371 #define CMD_SELMBOX CMD_GETFIELDS \
372 " WHERE mailbox = :mailbox AND alive = 1;"
373
webdav_foreach(struct webdav_db * webdavdb,const char * mailbox,int (* cb)(void * rock,struct webdav_data * data),void * rock)374 EXPORTED int webdav_foreach(struct webdav_db *webdavdb, const char *mailbox,
375 int (*cb)(void *rock, struct webdav_data *data),
376 void *rock)
377 {
378 struct sqldb_bindval bval[] = {
379 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
380 { NULL, SQLITE_NULL, { .s = NULL } } };
381 struct webdav_data wdata;
382 struct read_rock rrock = { webdavdb, &wdata, 0, cb, rock };
383
384 return sqldb_exec(webdavdb->db, CMD_SELMBOX, bval, &read_cb, &rrock);
385 }
386
387
388 #define CMD_INSERT \
389 "INSERT INTO dav_objs (" \
390 " creationdate, mailbox, resource, imap_uid, modseq," \
391 " createdmodseq," \
392 " lock_token, lock_owner, lock_ownerid, lock_expire," \
393 " filename, type, subtype, res_uid, ref_count, alive )" \
394 " VALUES (" \
395 " :creationdate, :mailbox, :resource, :imap_uid, :modseq," \
396 " :createdmodseq," \
397 " :lock_token, :lock_owner, :lock_ownerid, :lock_expire," \
398 " :filename, :type, :subtype, :res_uid, :ref_count, :alive );"
399
400 #define CMD_UPDATE \
401 "UPDATE dav_objs SET" \
402 " imap_uid = :imap_uid," \
403 " modseq = :modseq," \
404 " createdmodseq = :createdmodseq," \
405 " lock_token = :lock_token," \
406 " lock_owner = :lock_owner," \
407 " lock_ownerid = :lock_ownerid," \
408 " lock_expire = :lock_expire," \
409 " filename = :filename," \
410 " type = :type," \
411 " subtype = :subtype," \
412 " res_uid = :res_uid," \
413 " ref_count = :ref_count," \
414 " alive = :alive" \
415 " WHERE rowid = :rowid;"
416
webdav_write(struct webdav_db * webdavdb,struct webdav_data * wdata)417 EXPORTED int webdav_write(struct webdav_db *webdavdb, struct webdav_data *wdata)
418 {
419 struct sqldb_bindval bval[] = {
420 { ":imap_uid", SQLITE_INTEGER, { .i = wdata->dav.imap_uid } },
421 { ":modseq", SQLITE_INTEGER, { .i = wdata->dav.modseq } },
422 { ":createdmodseq", SQLITE_INTEGER, { .i = wdata->dav.createdmodseq } },
423 { ":lock_token", SQLITE_TEXT, { .s = wdata->dav.lock_token } },
424 { ":lock_owner", SQLITE_TEXT, { .s = wdata->dav.lock_owner } },
425 { ":lock_ownerid", SQLITE_TEXT, { .s = wdata->dav.lock_ownerid } },
426 { ":lock_expire", SQLITE_INTEGER, { .i = wdata->dav.lock_expire } },
427 { ":filename", SQLITE_TEXT, { .s = wdata->filename } },
428 { ":type", SQLITE_TEXT, { .s = wdata->type } },
429 { ":subtype", SQLITE_TEXT, { .s = wdata->subtype } },
430 { ":res_uid", SQLITE_TEXT, { .s = wdata->res_uid } },
431 { ":ref_count", SQLITE_INTEGER, { .i = wdata->ref_count } },
432 { ":alive", SQLITE_INTEGER, { .i = wdata->dav.alive } },
433 { NULL, SQLITE_NULL, { .s = NULL } },
434 { NULL, SQLITE_NULL, { .s = NULL } },
435 { NULL, SQLITE_NULL, { .s = NULL } },
436 { NULL, SQLITE_NULL, { .s = NULL } } };
437 const char *cmd;
438 int r;
439
440 if (wdata->dav.rowid) {
441 cmd = CMD_UPDATE;
442
443 bval[13].name = ":rowid";
444 bval[13].type = SQLITE_INTEGER;
445 bval[13].val.i = wdata->dav.rowid;
446 }
447 else {
448 cmd = CMD_INSERT;
449
450 bval[13].name = ":creationdate";
451 bval[13].type = SQLITE_INTEGER;
452 bval[13].val.i = wdata->dav.creationdate;
453 bval[14].name = ":mailbox";
454 bval[14].type = SQLITE_TEXT;
455 bval[14].val.s = wdata->dav.mailbox;
456 bval[15].name = ":resource";
457 bval[15].type = SQLITE_TEXT;
458 bval[15].val.s = wdata->dav.resource;
459 }
460
461 r = sqldb_exec(webdavdb->db, cmd, bval, NULL, NULL);
462
463 return r;
464 }
465
466
467 #define CMD_DELETE "DELETE FROM dav_objs WHERE rowid = :rowid;"
468
webdav_delete(struct webdav_db * webdavdb,unsigned rowid)469 EXPORTED int webdav_delete(struct webdav_db *webdavdb, unsigned rowid)
470 {
471 struct sqldb_bindval bval[] = {
472 { ":rowid", SQLITE_INTEGER, { .i = rowid } },
473 { NULL, SQLITE_NULL, { .s = NULL } } };
474 int r;
475
476 r = sqldb_exec(webdavdb->db, CMD_DELETE, bval, NULL, NULL);
477
478 return r;
479 }
480
481
482 #define CMD_DELMBOX "DELETE FROM dav_objs WHERE mailbox = :mailbox;"
483
webdav_delmbox(struct webdav_db * webdavdb,const char * mailbox)484 HIDDEN int webdav_delmbox(struct webdav_db *webdavdb, const char *mailbox)
485 {
486 struct sqldb_bindval bval[] = {
487 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
488 { NULL, SQLITE_NULL, { .s = NULL } } };
489 int r;
490
491 r = sqldb_exec(webdavdb->db, CMD_DELMBOX, bval, NULL, NULL);
492
493 return r;
494 }
495
webdav_get_updates(struct webdav_db * webdavdb,modseq_t oldmodseq,const char * mboxname,int kind,int limit,int (* cb)(void * rock,struct webdav_data * wdata),void * rock)496 EXPORTED int webdav_get_updates(struct webdav_db *webdavdb,
497 modseq_t oldmodseq, const char *mboxname,
498 int kind __attribute__((unused)), int limit,
499 int (*cb)(void *rock, struct webdav_data *wdata),
500 void *rock)
501 {
502 struct sqldb_bindval bval[] = {
503 { ":mailbox", SQLITE_TEXT, { .s = mboxname } },
504 { ":modseq", SQLITE_INTEGER, { .i = oldmodseq } },
505 /* SQLite interprets a negative limit as unbounded. */
506 { ":limit", SQLITE_INTEGER, { .i = limit > 0 ? limit : -1 } },
507 { NULL, SQLITE_NULL, { .s = NULL } }
508 };
509 static struct webdav_data wdata;
510 struct read_rock rrock = { webdavdb, &wdata, 1 /* tombstones */, cb, rock };
511 struct buf sqlbuf = BUF_INITIALIZER;
512 int r;
513
514 buf_setcstr(&sqlbuf, CMD_GETFIELDS " WHERE");
515 if (mboxname) buf_appendcstr(&sqlbuf, " mailbox = :mailbox AND");
516 if (!oldmodseq) buf_appendcstr(&sqlbuf, " alive = 1 AND");
517 buf_appendcstr(&sqlbuf, " modseq > :modseq ORDER BY modseq LIMIT :limit;");
518
519 r = sqldb_exec(webdavdb->db, buf_cstring(&sqlbuf), bval, &read_cb, &rrock);
520 buf_free(&sqlbuf);
521
522 if (r) {
523 syslog(LOG_ERR, "webdav error %s", error_message(r));
524 }
525 return r;
526 }
527
528 #else
529
webdav_init(void)530 EXPORTED int webdav_init(void)
531 {
532 return 0;
533 }
534
535
webdav_done(void)536 EXPORTED int webdav_done(void)
537 {
538 return 0;
539 }
540
541 #endif /* WITH_DAV */
542