* do typedef the visit struct, remove all those 'struct' statements from all over...
[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         if (num_parms > 5) {
212                 cprintf("%s BAD usage error\r\n", Params[0].Key);
213                 return;
214         }
215
216         ret = imap_grabroom(roomname, Params[2].Key, 1);
217         if (ret != 0) {
218                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
219                         Params[0].Key);
220                 return;
221         }
222
223         /*
224          * CtdlUserGoto() formally takes us to the desired room.  (If another
225          * folder is selected, save its name so we can return there!!!!!)
226          */
227         if (IMAP->selected) {
228                 strcpy(savedroom, CC->room.QRname);
229         }
230         CtdlUserGoto(roomname, 0, 0, &msgs, &new);
231
232         cprintf("* METADATA ");
233         imap_strout(&Params[2]);
234         cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
235
236         /* If it's one of our hard-coded default rooms, we know what to do... */
237
238         if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
239                 cprintf("mail.inbox");
240         }
241         else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
242                 cprintf("mail.sentitems");
243         }
244         else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
245                 cprintf("mail.drafts");
246         }
247         else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
248                 cprintf("event.default");
249         }
250         else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
251                 cprintf("contact.default");
252         }
253         else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
254                 cprintf("note.default");
255         }
256         else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
257                 cprintf("task.default");
258         }
259
260         /* Otherwise, use the view for this room to determine the type of data.
261          * We are going with the default view rather than the user's view, because
262          * the default view almost always defines the actual contents, while the
263          * user's view might only make changes to presentation.  It also saves us
264          * an extra database access because we don't need to load the visit record.
265          */
266
267         else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
268                 cprintf("event");
269         }
270         else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
271                 cprintf("contact");
272         }
273         else if (CC->room.QRdefaultview == VIEW_TASKS) {
274                 cprintf("task");
275         }
276         else if (CC->room.QRdefaultview == VIEW_NOTES) {
277                 cprintf("note");
278         }
279         else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
280                 cprintf("journal");
281         }
282
283         /* If none of the above conditions were met, consider it an ordinary mailbox. */
284         else {
285                 cprintf("mail");
286         }
287
288         /* "mail.outbox" and "junkemail" are not implemented. */
289
290         cprintf("\")\r\n");
291
292         /*
293          * If a different folder was previously selected, return there now.
294          */
295         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
296                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new);
297         }
298
299         cprintf("%s OK GETMETADATA complete\r\n", Params[0].Key);
300         return;
301 }
302