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