Citadel API clean up.
[citadel.git] / citadel / modules / imap / imap_metadata.c
1 /*
2  * $Id$
3  *
4  * IMAP METADATA extension
5  *
6  * This is an implementation of the Bynari variant of the METADATA extension.
7  *
8  * Copyright (c) 2007-2009 by the citadel.org team
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25
26 #include "sysdep.h"
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <pwd.h>
33 #include <errno.h>
34 #include <sys/types.h>
35
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 #  include <sys/time.h>
42 # else
43 #  include <time.h>
44 # endif
45 #endif
46
47 #include <sys/wait.h>
48 #include <ctype.h>
49 #include <string.h>
50 #include <limits.h>
51 #include <libcitadel.h>
52 #include "citadel.h"
53 #include "server.h"
54 #include "sysdep_decls.h"
55 #include "citserver.h"
56 #include "support.h"
57 #include "config.h"
58 #include "user_ops.h"
59 #include "policy.h"
60 #include "database.h"
61 #include "msgbase.h"
62 #include "internet_addressing.h"
63 #include "serv_imap.h"
64 #include "imap_tools.h"
65 #include "imap_fetch.h"
66 #include "imap_misc.h"
67 #include "genstamp.h"
68
69 #include "ctdl_module.h"
70
71 /*
72  * Implements the SETMETADATA command.
73  *
74  * Again, the only thing we're interested in setting here is the folder type.
75  *
76  * Attempting to set anything else calls a stub which fools the client into
77  * thinking that there is no remaining space available to store annotations.
78  */
79 void imap_setmetadata(int num_parms, char *parms[]) {
80         char roomname[ROOMNAMELEN];
81         char savedroom[ROOMNAMELEN];
82         int msgs, new;
83         int ret;
84         int setting_user_value = 0;
85         char set_value[32];
86         int set_view = VIEW_BBS;
87         struct visit vbuf;
88
89         if (num_parms != 6) {
90                 cprintf("%s BAD usage error\r\n", parms[0]);
91                 return;
92         }
93
94         /*
95          * Don't allow other types of metadata to be set
96          */
97         if (strcasecmp(parms[3], "/vendor/kolab/folder-type")) {
98                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
99                 return;
100         }
101
102         if (!strcasecmp(parms[4], "(value.shared")) {
103                 setting_user_value = 0;                         /* global view */
104         }
105         else if (!strcasecmp(parms[4], "(value.priv")) {
106                 setting_user_value = 1;                         /* per-user view */
107         }
108         else {
109                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
110                 return;
111         }
112
113         /*
114          * Extract the folder type without any parentheses.  Then learn
115          * the Citadel view type based on the supplied folder type.
116          */
117         extract_token(set_value, parms[5], 0, ')', sizeof set_value);
118         if (!strncasecmp(set_value, "mail", 4)) {
119                 set_view = VIEW_MAILBOX;
120         }
121         else if (!strncasecmp(set_value, "event", 5)) {
122                 set_view = VIEW_CALENDAR;
123         }
124         else if (!strncasecmp(set_value, "contact", 7)) {
125                 set_view = VIEW_ADDRESSBOOK;
126         }
127         else if (!strncasecmp(set_value, "journal", 7)) {
128                 set_view = VIEW_JOURNAL;
129         }
130         else if (!strncasecmp(set_value, "note", 4)) {
131                 set_view = VIEW_NOTES;
132         }
133         else if (!strncasecmp(set_value, "task", 4)) {
134                 set_view = VIEW_TASKS;
135         }
136         else {
137                 set_view = VIEW_MAILBOX;
138         }
139
140         ret = imap_grabroom(roomname, parms[2], 1);
141         if (ret != 0) {
142                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
143                         parms[0]);
144                 return;
145         }
146
147         /*
148          * CtdlUserGoto() formally takes us to the desired room.  (If another
149          * folder is selected, save its name so we can return there!!!!!)
150          */
151         if (IMAP->selected) {
152                 strcpy(savedroom, CC->room.QRname);
153         }
154         CtdlUserGoto(roomname, 0, 0, &msgs, &new);
155
156         /*
157          * Always set the per-user view to the requested one.
158          */
159         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
160         vbuf.v_view = set_view;
161         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
162
163         /* If this is a "value.priv" set operation, we're done. */
164
165         if (setting_user_value)
166         {
167                 cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
168         }
169
170         /* If this is a "value.shared" set operation, we are allowed to perform it
171          * under certain conditions.
172          */
173         else if (       (is_room_aide())                                        /* aide or room aide */
174                 ||      (       (CC->room.QRflags & QR_MAILBOX)
175                         &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
176                         )
177                 ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
178         ) {
179                 CtdlGetRoomLock(&CC->room, CC->room.QRname);
180                 CC->room.QRdefaultview = set_view;
181                 CtdlPutRoomLock(&CC->room);
182                 cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
183         }
184
185         /* If we got to this point, we don't have permission to set the default view. */
186         else {
187                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
188         }
189
190         /*
191          * If a different folder was previously selected, return there now.
192          */
193         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
194                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
195         }
196         return;
197 }
198
199
200 /*
201  * Implements the GETMETADATA command.
202  *
203  * Regardless of what the client asked for, we are going to supply them with
204  * the folder type.  It's the only metadata we have anyway.
205  */
206 void imap_getmetadata(int num_parms, char *parms[]) {
207         char roomname[ROOMNAMELEN];
208         char savedroom[ROOMNAMELEN];
209         int msgs, new;
210         int ret;
211
212         if (num_parms > 5) {
213                 cprintf("%s BAD usage error\r\n", parms[0]);
214                 return;
215         }
216
217         ret = imap_grabroom(roomname, parms[2], 1);
218         if (ret != 0) {
219                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
220                         parms[0]);
221                 return;
222         }
223
224         /*
225          * CtdlUserGoto() formally takes us to the desired room.  (If another
226          * folder is selected, save its name so we can return there!!!!!)
227          */
228         if (IMAP->selected) {
229                 strcpy(savedroom, CC->room.QRname);
230         }
231         CtdlUserGoto(roomname, 0, 0, &msgs, &new);
232
233         cprintf("* METADATA ");
234         imap_strout(parms[2]);
235         cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
236
237         /* If it's one of our hard-coded default rooms, we know what to do... */
238
239         if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
240                 cprintf("mail.inbox");
241         }
242         else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
243                 cprintf("mail.sentitems");
244         }
245         else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
246                 cprintf("mail.drafts");
247         }
248         else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
249                 cprintf("event.default");
250         }
251         else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
252                 cprintf("contact.default");
253         }
254         else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
255                 cprintf("note.default");
256         }
257         else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
258                 cprintf("task.default");
259         }
260
261         /* Otherwise, use the view for this room to determine the type of data.
262          * We are going with the default view rather than the user's view, because
263          * the default view almost always defines the actual contents, while the
264          * user's view might only make changes to presentation.  It also saves us
265          * an extra database access because we don't need to load the visit record.
266          */
267
268         else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
269                 cprintf("event");
270         }
271         else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
272                 cprintf("contact");
273         }
274         else if (CC->room.QRdefaultview == VIEW_TASKS) {
275                 cprintf("task");
276         }
277         else if (CC->room.QRdefaultview == VIEW_NOTES) {
278                 cprintf("note");
279         }
280         else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
281                 cprintf("journal");
282         }
283
284         /* If none of the above conditions were met, consider it an ordinary mailbox. */
285         else {
286                 cprintf("mail");
287         }
288
289         /* "mail.outbox" and "junkemail" are not implemented. */
290
291         cprintf("\")\r\n");
292
293         /*
294          * If a different folder was previously selected, return there now.
295          */
296         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
297                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
298         }
299
300         cprintf("%s OK GETMETADATA complete\r\n", parms[0]);
301         return;
302 }
303