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
webdav_init(void)92 EXPORTED int webdav_init(void)
93 {
94 return sqldb_init();
95 }
96
97
webdav_done(void)98 EXPORTED int webdav_done(void)
99 {
100 return sqldb_done();
101 }
102
103 /* Open DAV DB corresponding to userid */
webdav_open_userid(const char * userid)104 EXPORTED struct webdav_db *webdav_open_userid(const char *userid)
105 {
106 struct webdav_db *webdavdb = NULL;
107
108 sqldb_t *db = dav_open_userid(userid);
109 if (!db) return NULL;
110
111 webdavdb = xzmalloc(sizeof(struct webdav_db));
112 webdavdb->userid = xstrdup(userid);
113 webdavdb->db = db;
114
115 return webdavdb;
116 }
117
118 /* Open DAV DB corresponding to mailbox */
webdav_open_mailbox(struct mailbox * mailbox)119 EXPORTED struct webdav_db *webdav_open_mailbox(struct mailbox *mailbox)
120 {
121 struct webdav_db *webdavdb = NULL;
122 char *userid = mboxname_to_userid(mailbox->name);
123
124 if (userid) {
125 webdavdb = webdav_open_userid(userid);
126 free(userid);
127 return webdavdb;
128 }
129
130 sqldb_t *db = dav_open_mailbox(mailbox);
131 if (!db) return NULL;
132
133 webdavdb = xzmalloc(sizeof(struct webdav_db));
134 webdavdb->db = db;
135
136 return webdavdb;
137 }
138
139 /* Close DAV DB */
webdav_close(struct webdav_db * webdavdb)140 EXPORTED int webdav_close(struct webdav_db *webdavdb)
141 {
142 int r = 0;
143
144 if (!webdavdb) return 0;
145
146 buf_free(&webdavdb->mailbox);
147 buf_free(&webdavdb->resource);
148 buf_free(&webdavdb->lock_token);
149 buf_free(&webdavdb->lock_owner);
150 buf_free(&webdavdb->lock_ownerid);
151 buf_free(&webdavdb->filename);
152 buf_free(&webdavdb->type);
153 buf_free(&webdavdb->subtype);
154 buf_free(&webdavdb->res_uid);
155
156 r = sqldb_close(&webdavdb->db);
157
158 free(webdavdb->userid);
159 free(webdavdb);
160
161 return r;
162 }
163
webdav_begin(struct webdav_db * webdavdb)164 EXPORTED int webdav_begin(struct webdav_db *webdavdb)
165 {
166 return sqldb_begin(webdavdb->db, "webdav");
167 }
168
webdav_commit(struct webdav_db * webdavdb)169 EXPORTED int webdav_commit(struct webdav_db *webdavdb)
170 {
171 return sqldb_commit(webdavdb->db, "webdav");
172 }
173
webdav_abort(struct webdav_db * webdavdb)174 EXPORTED int webdav_abort(struct webdav_db *webdavdb)
175 {
176 return sqldb_rollback(webdavdb->db, "webdav");
177 }
178
179
180 struct read_rock {
181 struct webdav_db *db;
182 struct webdav_data *wdata;
183 int tombstones;
184 int (*cb)(void *rock, void *data);
185 void *rock;
186 };
187
column_text_to_buf(const char * text,struct buf * buf)188 static const char *column_text_to_buf(const char *text, struct buf *buf)
189 {
190 if (text) {
191 buf_setcstr(buf, text);
192 text = buf_cstring(buf);
193 }
194
195 return text;
196 }
197
read_cb(sqlite3_stmt * stmt,void * rock)198 static int read_cb(sqlite3_stmt *stmt, void *rock)
199 {
200 struct read_rock *rrock = (struct read_rock *) rock;
201 struct webdav_db *db = rrock->db;
202 struct webdav_data *wdata = rrock->wdata;
203 int r = 0;
204
205 memset(wdata, 0, sizeof(struct webdav_data));
206
207 wdata->dav.alive = sqlite3_column_int(stmt, 14);
208 if (!rrock->tombstones && !wdata->dav.alive)
209 return 0;
210
211 wdata->dav.rowid = sqlite3_column_int(stmt, 0);
212 wdata->dav.creationdate = sqlite3_column_int(stmt, 1);
213 wdata->dav.imap_uid = sqlite3_column_int(stmt, 4);
214 wdata->dav.lock_expire = sqlite3_column_int(stmt, 8);
215 wdata->ref_count = sqlite3_column_int(stmt, 13);
216
217 if (rrock->cb) {
218 /* We can use the column data directly for the callback */
219 wdata->dav.mailbox = (const char *) sqlite3_column_text(stmt, 2);
220 wdata->dav.resource = (const char *) sqlite3_column_text(stmt, 3);
221 wdata->dav.lock_token = (const char *) sqlite3_column_text(stmt, 5);
222 wdata->dav.lock_owner = (const char *) sqlite3_column_text(stmt, 6);
223 wdata->dav.lock_ownerid = (const char *) sqlite3_column_text(stmt, 7);
224 wdata->filename = (const char *) sqlite3_column_text(stmt, 9);
225 wdata->type = (const char *) sqlite3_column_text(stmt, 10);
226 wdata->subtype = (const char *) sqlite3_column_text(stmt, 11);
227 wdata->res_uid = (const char *) sqlite3_column_text(stmt, 12);
228 r = rrock->cb(rrock->rock, wdata);
229 }
230 else {
231 /* For single row SELECTs like webdav_read(),
232 * we need to make a copy of the column data before
233 * it gets flushed by sqlite3_step() or sqlite3_reset() */
234 wdata->dav.mailbox =
235 column_text_to_buf((const char *) sqlite3_column_text(stmt, 2),
236 &db->mailbox);
237 wdata->dav.resource =
238 column_text_to_buf((const char *) sqlite3_column_text(stmt, 3),
239 &db->resource);
240 wdata->dav.lock_token =
241 column_text_to_buf((const char *) sqlite3_column_text(stmt, 5),
242 &db->lock_token);
243 wdata->dav.lock_owner =
244 column_text_to_buf((const char *) sqlite3_column_text(stmt, 6),
245 &db->lock_owner);
246 wdata->dav.lock_ownerid =
247 column_text_to_buf((const char *) sqlite3_column_text(stmt, 7),
248 &db->lock_ownerid);
249 wdata->filename =
250 column_text_to_buf((const char *) sqlite3_column_text(stmt, 9),
251 &db->filename);
252 wdata->type =
253 column_text_to_buf((const char *) sqlite3_column_text(stmt, 10),
254 &db->type);
255 wdata->subtype =
256 column_text_to_buf((const char *) sqlite3_column_text(stmt, 11),
257 &db->subtype);
258 wdata->res_uid =
259 column_text_to_buf((const char *) sqlite3_column_text(stmt, 12),
260 &db->res_uid);
261 }
262
263 return r;
264 }
265
266 #define CMD_GETFIELDS \
267 "SELECT rowid, creationdate, mailbox, resource, imap_uid," \
268 " lock_token, lock_owner, lock_ownerid, lock_expire," \
269 " filename, type, subtype, res_uid, ref_count, alive" \
270 " FROM dav_objs" \
271
272
273 #define CMD_SELRSRC CMD_GETFIELDS \
274 " WHERE mailbox = :mailbox AND resource = :resource;"
275
webdav_lookup_resource(struct webdav_db * webdavdb,const char * mailbox,const char * resource,struct webdav_data ** result,int tombstones)276 EXPORTED int webdav_lookup_resource(struct webdav_db *webdavdb,
277 const char *mailbox, const char *resource,
278 struct webdav_data **result,
279 int tombstones)
280 {
281 struct sqldb_bindval bval[] = {
282 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
283 { ":resource", SQLITE_TEXT, { .s = resource } },
284 { NULL, SQLITE_NULL, { .s = NULL } } };
285 static struct webdav_data wdata;
286 struct read_rock rrock = { webdavdb, &wdata, tombstones, NULL, NULL };
287 int r;
288
289 *result = memset(&wdata, 0, sizeof(struct webdav_data));
290
291 r = sqldb_exec(webdavdb->db, CMD_SELRSRC, bval, &read_cb, &rrock);
292 if (!r && !wdata.dav.rowid) r = CYRUSDB_NOTFOUND;
293
294 return r;
295 }
296
297
298 #define CMD_SELUID CMD_GETFIELDS \
299 " WHERE res_uid = :res_uid AND alive = 1;"
300
webdav_lookup_uid(struct webdav_db * webdavdb,const char * res_uid,struct webdav_data ** result)301 EXPORTED int webdav_lookup_uid(struct webdav_db *webdavdb, const char *res_uid,
302 struct webdav_data **result)
303 {
304 struct sqldb_bindval bval[] = {
305 { ":res_uid", SQLITE_TEXT, { .s = res_uid } },
306 { NULL, SQLITE_NULL, { .s = NULL } } };
307 static struct webdav_data wdata;
308 struct read_rock rrock = { webdavdb, &wdata, 0, NULL, NULL };
309 int r;
310
311 *result = memset(&wdata, 0, sizeof(struct webdav_data));
312
313 r = sqldb_exec(webdavdb->db, CMD_SELUID, bval, &read_cb, &rrock);
314 if (!r && !wdata.dav.rowid) r = CYRUSDB_NOTFOUND;
315
316 return r;
317 }
318
319
320 #define CMD_SELMBOX CMD_GETFIELDS \
321 " WHERE mailbox = :mailbox AND alive = 1;"
322
webdav_foreach(struct webdav_db * webdavdb,const char * mailbox,int (* cb)(void * rock,void * data),void * rock)323 EXPORTED int webdav_foreach(struct webdav_db *webdavdb, const char *mailbox,
324 int (*cb)(void *rock, void *data),
325 void *rock)
326 {
327 struct sqldb_bindval bval[] = {
328 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
329 { NULL, SQLITE_NULL, { .s = NULL } } };
330 struct webdav_data wdata;
331 struct read_rock rrock = { webdavdb, &wdata, 0, cb, rock };
332
333 return sqldb_exec(webdavdb->db, CMD_SELMBOX, bval, &read_cb, &rrock);
334 }
335
336
337 #define CMD_INSERT \
338 "INSERT INTO dav_objs (" \
339 " creationdate, mailbox, resource, imap_uid, modseq," \
340 " lock_token, lock_owner, lock_ownerid, lock_expire," \
341 " filename, type, subtype, res_uid, ref_count, alive )" \
342 " VALUES (" \
343 " :creationdate, :mailbox, :resource, :imap_uid, :modseq," \
344 " :lock_token, :lock_owner, :lock_ownerid, :lock_expire," \
345 " :filename, :type, :subtype, :res_uid, :ref_count, :alive );"
346
347 #define CMD_UPDATE \
348 "UPDATE dav_objs SET" \
349 " imap_uid = :imap_uid," \
350 " modseq = :modseq," \
351 " lock_token = :lock_token," \
352 " lock_owner = :lock_owner," \
353 " lock_ownerid = :lock_ownerid," \
354 " lock_expire = :lock_expire," \
355 " filename = :filename," \
356 " type = :type," \
357 " subtype = :subtype," \
358 " res_uid = :res_uid," \
359 " ref_count = :ref_count," \
360 " alive = :alive" \
361 " WHERE rowid = :rowid;"
362
webdav_write(struct webdav_db * webdavdb,struct webdav_data * wdata)363 EXPORTED int webdav_write(struct webdav_db *webdavdb, struct webdav_data *wdata)
364 {
365 struct sqldb_bindval bval[] = {
366 { ":imap_uid", SQLITE_INTEGER, { .i = wdata->dav.imap_uid } },
367 { ":modseq", SQLITE_INTEGER, { .i = wdata->dav.modseq } },
368 { ":lock_token", SQLITE_TEXT, { .s = wdata->dav.lock_token } },
369 { ":lock_owner", SQLITE_TEXT, { .s = wdata->dav.lock_owner } },
370 { ":lock_ownerid", SQLITE_TEXT, { .s = wdata->dav.lock_ownerid } },
371 { ":lock_expire", SQLITE_INTEGER, { .i = wdata->dav.lock_expire } },
372 { ":filename", SQLITE_TEXT, { .s = wdata->filename } },
373 { ":type", SQLITE_TEXT, { .s = wdata->type } },
374 { ":subtype", SQLITE_TEXT, { .s = wdata->subtype } },
375 { ":res_uid", SQLITE_TEXT, { .s = wdata->res_uid } },
376 { ":ref_count", SQLITE_INTEGER, { .i = wdata->ref_count } },
377 { ":alive", SQLITE_INTEGER, { .i = wdata->dav.alive } },
378 { NULL, SQLITE_NULL, { .s = NULL } },
379 { NULL, SQLITE_NULL, { .s = NULL } },
380 { NULL, SQLITE_NULL, { .s = NULL } },
381 { NULL, SQLITE_NULL, { .s = NULL } } };
382 const char *cmd;
383 int r;
384
385 if (wdata->dav.rowid) {
386 cmd = CMD_UPDATE;
387
388 bval[12].name = ":rowid";
389 bval[12].type = SQLITE_INTEGER;
390 bval[12].val.i = wdata->dav.rowid;
391 }
392 else {
393 cmd = CMD_INSERT;
394
395 bval[12].name = ":creationdate";
396 bval[12].type = SQLITE_INTEGER;
397 bval[12].val.i = wdata->dav.creationdate;
398 bval[13].name = ":mailbox";
399 bval[13].type = SQLITE_TEXT;
400 bval[13].val.s = wdata->dav.mailbox;
401 bval[14].name = ":resource";
402 bval[14].type = SQLITE_TEXT;
403 bval[14].val.s = wdata->dav.resource;
404 }
405
406 r = sqldb_exec(webdavdb->db, cmd, bval, NULL, NULL);
407
408 return r;
409 }
410
411
412 #define CMD_DELETE "DELETE FROM dav_objs WHERE rowid = :rowid;"
413
webdav_delete(struct webdav_db * webdavdb,unsigned rowid)414 EXPORTED int webdav_delete(struct webdav_db *webdavdb, unsigned rowid)
415 {
416 struct sqldb_bindval bval[] = {
417 { ":rowid", SQLITE_INTEGER, { .i = rowid } },
418 { NULL, SQLITE_NULL, { .s = NULL } } };
419 int r;
420
421 r = sqldb_exec(webdavdb->db, CMD_DELETE, bval, NULL, NULL);
422
423 return r;
424 }
425
426
427 #define CMD_DELMBOX "DELETE FROM dav_objs WHERE mailbox = :mailbox;"
428
webdav_delmbox(struct webdav_db * webdavdb,const char * mailbox)429 EXPORTED int webdav_delmbox(struct webdav_db *webdavdb, const char *mailbox)
430 {
431 struct sqldb_bindval bval[] = {
432 { ":mailbox", SQLITE_TEXT, { .s = mailbox } },
433 { NULL, SQLITE_NULL, { .s = NULL } } };
434 int r;
435
436 r = sqldb_exec(webdavdb->db, CMD_DELMBOX, bval, NULL, NULL);
437
438 return r;
439 }
440
441 #else
442
webdav_init(void)443 EXPORTED int webdav_init(void)
444 {
445 return 0;
446 }
447
448
webdav_done(void)449 EXPORTED int webdav_done(void)
450 {
451 return 0;
452 }
453
454 #endif /* WITH_DAV */
455