fix the GETMETADATA command; we mustn't count our naive split of the comand, it will...
[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 "database.h"
60 #include "msgbase.h"
61 #include "internet_addressing.h"
62 #include "imap_tools.h"
63 #include "serv_imap.h"
64 #include "imap_fetch.h"
65 #include "imap_misc.h"
66 #include "genstamp.h"
67
68 #include "ctdl_module.h"
69
70 /*
71  * Implements the SETMETADATA command.
72  *
73  * Again, the only thing we're interested in setting here is the folder type.
74  *
75  * Attempting to set anything else calls a stub which fools the client into
76  * thinking that there is no remaining space available to store annotations.
77  */
78 void imap_setmetadata(int num_parms, ConstStr *Params) {
79         char roomname[ROOMNAMELEN];
80         char savedroom[ROOMNAMELEN];
81         int msgs, new;
82         int ret;
83         int setting_user_value = 0;
84         char set_value[32];
85         int set_view = VIEW_BBS;
86         visit vbuf;
87
88         if (num_parms != 6) {
89                 cprintf("%s BAD usage error\r\n", Params[0].Key);
90                 return;
91         }
92
93         /*
94          * Don't allow other types of metadata to be set
95          */
96         if (strcasecmp(Params[3].Key, "/vendor/kolab/folder-type")) {
97                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
98                 return;
99         }
100
101         if (!strcasecmp(Params[4].Key, "(value.shared")) {
102                 setting_user_value = 0;                         /* global view */
103         }
104         else if (!strcasecmp(Params[4].Key, "(value.priv")) {
105                 setting_user_value = 1;                         /* per-user view */
106         }
107         else {
108                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
109                 return;
110         }
111
112         /*
113          * Extract the folder type without any parentheses.  Then learn
114          * the Citadel view type based on the supplied folder type.
115          */
116         extract_token(set_value, Params[5].Key, 0, ')', sizeof set_value);
117         if (!strncasecmp(set_value, "mail", 4)) {
118                 set_view = VIEW_MAILBOX;
119         }
120         else if (!strncasecmp(set_value, "event", 5)) {
121                 set_view = VIEW_CALENDAR;
122         }
123         else if (!strncasecmp(set_value, "contact", 7)) {
124                 set_view = VIEW_ADDRESSBOOK;
125         }
126         else if (!strncasecmp(set_value, "journal", 7)) {
127                 set_view = VIEW_JOURNAL;
128         }
129         else if (!strncasecmp(set_value, "note", 4)) {
130                 set_view = VIEW_NOTES;
131         }
132         else if (!strncasecmp(set_value, "task", 4)) {
133                 set_view = VIEW_TASKS;
134         }
135         else {
136                 set_view = VIEW_MAILBOX;
137         }
138
139         ret = imap_grabroom(roomname, Params[2].Key, 1);
140         if (ret != 0) {
141                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
142                         Params[0].Key);
143                 return;
144         }
145
146         /*
147          * CtdlUserGoto() formally takes us to the desired room.  (If another
148          * folder is selected, save its name so we can return there!!!!!)
149          */
150         if (IMAP->selected) {
151                 strcpy(savedroom, CC->room.QRname);
152         }
153         CtdlUserGoto(roomname, 0, 0, &msgs, &new);
154
155         /*
156          * Always set the per-user view to the requested one.
157          */
158         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
159         vbuf.v_view = set_view;
160         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
161
162         /* If this is a "value.priv" set operation, we're done. */
163
164         if (setting_user_value)
165         {
166                 cprintf("%s OK SETANNOTATION complete\r\n", Params[0].Key);
167         }
168
169         /* If this is a "value.shared" set operation, we are allowed to perform it
170          * under certain conditions.
171          */
172         else if (       (is_room_aide())                                        /* aide or room aide */
173                 ||      (       (CC->room.QRflags & QR_MAILBOX)
174                         &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
175                         )
176                 ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
177         ) {
178                 CtdlGetRoomLock(&CC->room, CC->room.QRname);
179                 CC->room.QRdefaultview = set_view;
180                 CtdlPutRoomLock(&CC->room);
181                 cprintf("%s OK SETANNOTATION complete\r\n", Params[0].Key);
182         }
183
184         /* If we got to this point, we don't have permission to set the default view. */
185         else {
186                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", Params[0].Key);
187         }
188
189         /*
190          * If a different folder was previously selected, return there now.
191          */
192         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
193                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
194         }
195         return;
196 }
197
198
199 /*
200  * Implements the GETMETADATA command.
201  *
202  * Regardless of what the client asked for, we are going to supply them with
203  * the folder type.  It's the only metadata we have anyway.
204  */
205 void imap_getmetadata(int num_parms, ConstStr *Params) {
206         char roomname[ROOMNAMELEN];
207         char savedroom[ROOMNAMELEN];
208         int msgs, new;
209         int ret;
210
211 /* this doesn't work if you have rooms/floors with spaces. 
212    we need this for the bynari connector.
213         if (num_parms > 5) {
214                 cprintf("%s BAD usage error\r\n", Params[0].Key);
215                 return;
216         }
217 */
218         ret = imap_grabroom(roomname, Params[2].Key, 1);
219         if (ret != 0) {
220                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
221                         Params[0].Key);
222                 return;
223         }
224
225         /*
226          * CtdlUserGoto() formally takes us to the desired room.  (If another
227          * folder is selected, save its name so we can return there!!!!!)
228          */
229         if (IMAP->selected) {
230                 strcpy(savedroom, CC->room.QRname);
231         }
232         CtdlUserGoto(roomname, 0, 0, &msgs, &new);
233
234         cprintf("* METADATA ");
235         imap_strout(&Params[2]);
236         cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
237
238         /* If it's one of our hard-coded default rooms, we know what to do... */
239
240         if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
241                 cprintf("mail.inbox");
242         }
243         else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
244                 cprintf("mail.sentitems");
245         }
246         else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
247                 cprintf("mail.drafts");
248         }
249         else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
250                 cprintf("event.default");
251         }
252         else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
253                 cprintf("contact.default");
254         }
255         else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
256                 cprintf("note.default");
257         }
258         else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
259                 cprintf("task.default");
260         }
261
262         /* Otherwise, use the view for this room to determine the type of data.
263          * We are going with the default view rather than the user's view, because
264          * the default view almost always defines the actual contents, while the
265          * user's view might only make changes to presentation.  It also saves us
266          * an extra database access because we don't need to load the visit record.
267          */
268
269         else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
270                 cprintf("event");
271         }
272         else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
273                 cprintf("contact");
274         }
275         else if (CC->room.QRdefaultview == VIEW_TASKS) {
276                 cprintf("task");
277         }
278         else if (CC->room.QRdefaultview == VIEW_NOTES) {
279                 cprintf("note");
280         }
281         else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
282                 cprintf("journal");
283         }
284
285         /* If none of the above conditions were met, consider it an ordinary mailbox. */
286         else {
287                 cprintf("mail");
288         }
289
290         /* "mail.outbox" and "junkemail" are not implemented. */
291
292         cprintf("\")\r\n");
293
294         /*
295          * If a different folder was previously selected, return there now.
296          */
297         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
298                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
299         }
300
301         cprintf("%s OK GETMETADATA complete\r\n", Params[0].Key);
302         return;
303 }
304