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