* More license declarations
[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 "room_ops.h"
59 #include "user_ops.h"
60 #include "policy.h"
61 #include "database.h"
62 #include "msgbase.h"
63 #include "internet_addressing.h"
64 #include "serv_imap.h"
65 #include "imap_tools.h"
66 #include "imap_fetch.h"
67 #include "imap_misc.h"
68 #include "genstamp.h"
69
70
71
72 /*
73  * Implements the SETMETADATA command.
74  *
75  * Again, the only thing we're interested in setting here is the folder type.
76  *
77  * Attempting to set anything else calls a stub which fools the client into
78  * thinking that there is no remaining space available to store annotations.
79  */
80 void imap_setmetadata(int num_parms, char *parms[]) {
81         char roomname[ROOMNAMELEN];
82         char savedroom[ROOMNAMELEN];
83         int msgs, new;
84         int ret;
85         int setting_user_value = 0;
86         char set_value[32];
87         int set_view = VIEW_BBS;
88         struct visit vbuf;
89
90         if (num_parms != 6) {
91                 cprintf("%s BAD usage error\r\n", parms[0]);
92                 return;
93         }
94
95         /*
96          * Don't allow other types of metadata to be set
97          */
98         if (strcasecmp(parms[3], "/vendor/kolab/folder-type")) {
99                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
100                 return;
101         }
102
103         if (!strcasecmp(parms[4], "(value.shared")) {
104                 setting_user_value = 0;                         /* global view */
105         }
106         else if (!strcasecmp(parms[4], "(value.priv")) {
107                 setting_user_value = 1;                         /* per-user view */
108         }
109         else {
110                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
111                 return;
112         }
113
114         /*
115          * Extract the folder type without any parentheses.  Then learn
116          * the Citadel view type based on the supplied folder type.
117          */
118         extract_token(set_value, parms[5], 0, ')', sizeof set_value);
119         if (!strncasecmp(set_value, "mail", 4)) {
120                 set_view = VIEW_MAILBOX;
121         }
122         else if (!strncasecmp(set_value, "event", 5)) {
123                 set_view = VIEW_CALENDAR;
124         }
125         else if (!strncasecmp(set_value, "contact", 7)) {
126                 set_view = VIEW_ADDRESSBOOK;
127         }
128         else if (!strncasecmp(set_value, "journal", 7)) {
129                 set_view = VIEW_JOURNAL;
130         }
131         else if (!strncasecmp(set_value, "note", 4)) {
132                 set_view = VIEW_NOTES;
133         }
134         else if (!strncasecmp(set_value, "task", 4)) {
135                 set_view = VIEW_TASKS;
136         }
137         else {
138                 set_view = VIEW_MAILBOX;
139         }
140
141         ret = imap_grabroom(roomname, parms[2], 1);
142         if (ret != 0) {
143                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
144                         parms[0]);
145                 return;
146         }
147
148         /*
149          * usergoto() formally takes us to the desired room.  (If another
150          * folder is selected, save its name so we can return there!!!!!)
151          */
152         if (IMAP->selected) {
153                 strcpy(savedroom, CC->room.QRname);
154         }
155         usergoto(roomname, 0, 0, &msgs, &new);
156
157         /*
158          * Always set the per-user view to the requested one.
159          */
160         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
161         vbuf.v_view = set_view;
162         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
163
164         /* If this is a "value.priv" set operation, we're done. */
165
166         if (setting_user_value)
167         {
168                 cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
169         }
170
171         /* If this is a "value.shared" set operation, we are allowed to perform it
172          * under certain conditions.
173          */
174         else if (       (is_room_aide())                                        /* aide or room aide */
175                 ||      (       (CC->room.QRflags & QR_MAILBOX)
176                         &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
177                         )
178                 ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
179         ) {
180                 lgetroom(&CC->room, CC->room.QRname);
181                 CC->room.QRdefaultview = set_view;
182                 lputroom(&CC->room);
183                 cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
184         }
185
186         /* If we got to this point, we don't have permission to set the default view. */
187         else {
188                 cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
189         }
190
191         /*
192          * If a different folder was previously selected, return there now.
193          */
194         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
195                 usergoto(savedroom, 0, 0, &msgs, &new);
196         }
197         return;
198 }
199
200
201 /*
202  * Implements the GETMETADATA command.
203  *
204  * Regardless of what the client asked for, we are going to supply them with
205  * the folder type.  It's the only metadata we have anyway.
206  */
207 void imap_getmetadata(int num_parms, char *parms[]) {
208         char roomname[ROOMNAMELEN];
209         char savedroom[ROOMNAMELEN];
210         int msgs, new;
211         int ret;
212
213         if (num_parms > 5) {
214                 cprintf("%s BAD usage error\r\n", parms[0]);
215                 return;
216         }
217
218         ret = imap_grabroom(roomname, parms[2], 1);
219         if (ret != 0) {
220                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
221                         parms[0]);
222                 return;
223         }
224
225         /*
226          * usergoto() 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         usergoto(roomname, 0, 0, &msgs, &new);
233
234         cprintf("* METADATA ");
235         imap_strout(parms[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                 usergoto(savedroom, 0, 0, &msgs, &new);
299         }
300
301         cprintf("%s OK GETMETADATA complete\r\n", parms[0]);
302         return;
303 }
304