2b0b5fc2843098200e6f2aa0e3cfec708fce2d63
[citadel.git] / citadel / imap_metadata.c
1 /*
2  * $Id:  $
3  *
4  * IMAP METADATA extension
5  *
6  * This is a partial implementation of draft-daboo-imap-annotatemore-11
7  * intended to help a specific connector product work with Citadel.
8  *
9  */
10
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/types.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <sys/wait.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <limits.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 "serv_extensions.h"
44 #include "room_ops.h"
45 #include "user_ops.h"
46 #include "policy.h"
47 #include "database.h"
48 #include "msgbase.h"
49 #include "tools.h"
50 #include "internet_addressing.h"
51 #include "serv_imap.h"
52 #include "imap_tools.h"
53 #include "imap_fetch.h"
54 #include "imap_misc.h"
55 #include "genstamp.h"
56
57
58
59 /*
60  * Implements the SETMETADATA command.
61  *
62  * Again, the only thing we're interested in setting here is the folder type.
63  *
64  * Attempting to set anything else calls a stub which fools the client into
65  * thinking that there is no remaining space available to store annotations.
66  */
67 void imap_setmetadata(int num_parms, char *parms[]) {
68         char roomname[ROOMNAMELEN];
69         char savedroom[ROOMNAMELEN];
70         int msgs, new;
71         int ret;
72         int setting_user_value = 0;
73         char set_value[32];
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.
101          */
102         extract_token(set_value, parms[5], 0, ')', sizeof set_value);
103
104         ret = imap_grabroom(roomname, parms[2], 0);
105         if (ret != 0) {
106                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
107                         parms[0]);
108                 return;
109         }
110
111         /*
112          * usergoto() formally takes us to the desired room.  (If another
113          * folder is selected, save its name so we can return there!!!!!)
114          */
115         if (IMAP->selected) {
116                 strcpy(savedroom, CC->room.QRname);
117         }
118         usergoto(roomname, 0, 0, &msgs, &new);
119
120         /*
121          * FIXME ... NOW DO SOMETHING
122         roomname
123         set_value
124         setting_user_value
125          * on success: cprintf("%s OK SETANNOTATION complete\r\n", parms[0]);
126          */
127
128         cprintf("%s NO [METADATA TOOMANY] SETMETADATA failed\r\n", parms[0]);
129
130         /*
131          * If a different folder was previously selected, return there now.
132          */
133         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
134                 usergoto(savedroom, 0, 0, &msgs, &new);
135         }
136         return;
137 }
138
139
140 /*
141  * Implements the GETMETADATA command.
142  *
143  * Regardless of what the client asked for, we are going to supply them with
144  * the folder type.  It's the only metadata we have anyway.
145  */
146 void imap_getmetadata(int num_parms, char *parms[]) {
147         char roomname[ROOMNAMELEN];
148         char savedroom[ROOMNAMELEN];
149         int msgs, new;
150         int ret;
151
152         if (num_parms > 5) {
153                 cprintf("%s BAD usage error\r\n", parms[0]);
154                 return;
155         }
156
157         ret = imap_grabroom(roomname, parms[2], 0);
158         if (ret != 0) {
159                 cprintf("%s NO Invalid mailbox name or access denied\r\n",
160                         parms[0]);
161                 return;
162         }
163
164         /*
165          * usergoto() formally takes us to the desired room.  (If another
166          * folder is selected, save its name so we can return there!!!!!)
167          */
168         if (IMAP->selected) {
169                 strcpy(savedroom, CC->room.QRname);
170         }
171         usergoto(roomname, 0, 0, &msgs, &new);
172
173         cprintf("* METADATA ");
174         imap_strout(parms[2]);
175         cprintf(" \"/vendor/kolab/folder-type\" (\"value.shared\" \"");
176
177         /* If it's one of our hard-coded default rooms, we know what to do... */
178
179         if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
180                 cprintf("mail.inbox");
181         }
182         else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
183                 cprintf("mail.sentitems");
184         }
185         else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
186                 cprintf("event.default");
187         }
188         else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
189                 cprintf("contact.default");
190         }
191         else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
192                 cprintf("note.default");
193         }
194         else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
195                 cprintf("task.default");
196         }
197
198         /* Otherwise, use the view for this room to determine the type of data.
199          * We are going with the default view rather than the user's view, because
200          * the default view almost always defines the actual contents, while the
201          * user's view might only make changes to presentation.  It also saves us
202          * an extra database access because we don't need to load the visit record.
203          */
204
205         else if (CC->room.QRdefaultview == VIEW_CALENDAR) {
206                 cprintf("event");
207         }
208         else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
209                 cprintf("contact");
210         }
211         else if (CC->room.QRdefaultview == VIEW_TASKS) {
212                 cprintf("task");
213         }
214         else if (CC->room.QRdefaultview == VIEW_NOTES) {
215                 cprintf("note");
216         }
217         else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
218                 cprintf("journal");
219         }
220
221         /* If none of the above conditions were met, consider it an ordinary mailbox. */
222         else {
223                 cprintf("mail");
224         }
225
226         /* "mail.outbox" and "junkemail" are not implemented. */
227
228         cprintf("\")\r\n");
229
230         /*
231          * If a different folder was previously selected, return there now.
232          */
233         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
234                 usergoto(savedroom, 0, 0, &msgs, &new);
235         }
236
237         cprintf("%s OK GETMETADATA complete\r\n", parms[0]);
238         return;
239 }
240