This is an omnibus commit which moves the Citadel Server from crusty old GNU Autotool...
[citadel.git] / citadel / server / modules / imap / imap_metadata.c
1 /*
2  * IMAP METADATA extension
3  *
4  * This is an implementation of the Bynari variant of the METADATA extension.
5  *
6  * Copyright (c) 2007-2017 by the citadel.org team
7  *
8  * This program is open source software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23
24 #include "../../sysdep.h"
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <pwd.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <time.h>
34 #include <sys/wait.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <limits.h>
38 #include <libcitadel.h>
39 #include "../../citadel.h"
40 #include "../../server.h"
41 #include "../../sysdep_decls.h"
42 #include "../../citserver.h"
43 #include "../../support.h"
44 #include "../../config.h"
45 #include "../../user_ops.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 #include "../../ctdl_module.h"
55
56 /*
57  * Implements the SETMETADATA command.
58  *
59  * Again, the only thing we're interested in setting here is the folder type.
60  *
61  * Attempting to set anything else calls a stub which fools the client into
62  * thinking that there is no remaining space available to store annotations.
63  */
64 void imap_setmetadata(int num_parms, ConstStr *Params) {
65         char roomname[ROOMNAMELEN];
66         char savedroom[ROOMNAMELEN];
67         int msgs, new;
68         int ret;
69         int setting_user_value = 0;
70         char set_value[32];
71         int set_view = VIEW_BBS;
72         visit vbuf;
73
74         if (num_parms != 6) {
75                 IReply("BAD usage error");
76                 return;
77         }
78
79         /*
80          * Don't allow other types of metadata to be set
81          */
82         if (strcasecmp(Params[3].Key, "/vendor/kolab/folder-type")) {
83                 IReply("NO [METADATA TOOMANY] SETMETADATA failed");
84                 return;
85         }
86
87         if (!strcasecmp(Params[4].Key, "(value.shared")) {
88                 setting_user_value = 0;                         /* global view */
89         }
90         else if (!strcasecmp(Params[4].Key, "(value.priv")) {
91                 setting_user_value = 1;                         /* per-user view */
92         }
93         else {
94                 IReply("NO [METADATA TOOMANY] SETMETADATA failed");
95                 return;
96         }
97
98         /*
99          * Extract the folder type without any parentheses.  Then learn
100          * the Citadel view type based on the supplied folder type.
101          */
102         extract_token(set_value, Params[5].Key, 0, ')', sizeof set_value);
103         if (!strncasecmp(set_value, "mail", 4)) {
104                 set_view = VIEW_MAILBOX;
105         }
106         else if (!strncasecmp(set_value, "event", 5)) {
107                 set_view = VIEW_CALENDAR;
108         }
109         else if (!strncasecmp(set_value, "contact", 7)) {
110                 set_view = VIEW_ADDRESSBOOK;
111         }
112         else if (!strncasecmp(set_value, "journal", 7)) {
113                 set_view = VIEW_JOURNAL;
114         }
115         else if (!strncasecmp(set_value, "note", 4)) {
116                 set_view = VIEW_NOTES;
117         }
118         else if (!strncasecmp(set_value, "task", 4)) {
119                 set_view = VIEW_TASKS;
120         }
121         else {
122                 set_view = VIEW_MAILBOX;
123         }
124
125         ret = imap_grabroom(roomname, Params[2].Key, 1);
126         if (ret != 0) {
127                 IReply("NO Invalid mailbox name or access denied");
128                 return;
129         }
130
131         /*
132          * CtdlUserGoto() formally takes us to the desired room.  (If another
133          * folder is selected, save its name so we can return there!!!!!)
134          */
135         if (IMAP->selected) {
136                 strcpy(savedroom, CC->room.QRname);
137         }
138         CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
139
140         /*
141          * Always set the per-user view to the requested one.
142          */
143         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
144         vbuf.v_view = set_view;
145         CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
146
147         /* If this is a "value.priv" set operation, we're done. */
148
149         if (setting_user_value)
150         {
151                 IReply("OK SETANNOTATION complete");
152         }
153
154         /* If this is a "value.shared" set operation, we are allowed to perform it
155          * under certain conditions.
156          */
157         else if (       (is_room_aide())                                        /* aide or room aide */
158                 ||      (       (CC->room.QRflags & QR_MAILBOX)
159                         &&      (CC->user.usernum == atol(CC->room.QRname))     /* mailbox owner */
160                         )
161                 ||      (msgs == 0)             /* hack: if room is empty, assume we just created it */
162         ) {
163                 CtdlGetRoomLock(&CC->room, CC->room.QRname);
164                 CC->room.QRdefaultview = set_view;
165                 CtdlPutRoomLock(&CC->room);
166                 IReply("OK SETANNOTATION complete");
167         }
168
169         /* If we got to this point, we don't have permission to set the default view. */
170         else {
171                 IReply("NO [METADATA TOOMANY] SETMETADATA failed");
172         }
173
174         /*
175          * If a different folder was previously selected, return there now.
176          */
177         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
178                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
179         }
180         return;
181 }
182
183
184 /*
185  * Implements the GETMETADATA command.
186  *
187  * Regardless of what the client asked for, we are going to supply them with
188  * the folder type.  It's the only metadata we have anyway.
189  */
190 void imap_getmetadata(int num_parms, ConstStr *Params) {
191         char roomname[ROOMNAMELEN];
192         char savedroom[ROOMNAMELEN];
193         int msgs, new;
194         int ret;
195         int found = 0;
196
197 /* this doesn't work if you have rooms/floors with spaces. 
198    we need this for the bynari connector.
199         if (num_parms > 5) {
200                 IReply("BAD usage error");
201                 return;
202         }
203 */
204         ret = imap_grabroom(roomname, Params[2].Key, 1);
205         if (ret != 0) {
206                 IReply("NO Invalid mailbox name or access denied");
207                 return;
208         }
209
210         /*
211          * CtdlUserGoto() 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         CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);
218
219         IAPuts("* METADATA ");
220         IPutCParamStr(2);
221         IAPuts(" \"/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 (CC->room.QRname[10] == '.')
226         {
227                 if (!strcasecmp(&CC->room.QRname[11], MAILROOM)) {
228                         found = 1;
229                         IAPuts("mail.inbox");
230                 }
231                 else if (!strcasecmp(&CC->room.QRname[11], SENTITEMS)) {
232                         found = 1;
233                         IAPuts("mail.sentitems");
234                 }
235                 else if (!strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) {
236                         found = 1;
237                         IAPuts("mail.drafts");
238                 }
239                 else if (!strcasecmp(&CC->room.QRname[11], USERCALENDARROOM)) {
240                         found = 1;
241                         IAPuts("event.default");
242                 }
243                 else if (!strcasecmp(&CC->room.QRname[11], USERCONTACTSROOM)) {
244                         found = 1;
245                         IAPuts("contact.default");
246                 }
247                 else if (!strcasecmp(&CC->room.QRname[11], USERNOTESROOM)) {
248                         found = 1;
249                         IAPuts("note.default");
250                 }
251                 else if (!strcasecmp(&CC->room.QRname[11], USERTASKSROOM)) {
252                         found = 1;
253                         IAPuts("task.default");
254                 }
255         }
256
257         /* Otherwise, use the view for this room to determine the type of data.
258          * We are going with the default view rather than the user's view, because
259          * the default view almost always defines the actual contents, while the
260          * user's view might only make changes to presentation.  It also saves us
261          * an extra database access because we don't need to load the visit record.
262          */
263         if (!found)
264         {
265                 if (CC->room.QRdefaultview == VIEW_CALENDAR) {
266                         IAPuts("event");
267                 }
268                 else if (CC->room.QRdefaultview == VIEW_ADDRESSBOOK) {
269                         IAPuts("contact");
270                 }
271                 else if (CC->room.QRdefaultview == VIEW_TASKS) {
272                         IAPuts("task");
273                 }
274                 else if (CC->room.QRdefaultview == VIEW_NOTES) {
275                         IAPuts("note");
276                 }
277                 else if (CC->room.QRdefaultview == VIEW_JOURNAL) {
278                         IAPuts("journal");
279                 }
280         }
281         /* If none of the above conditions were met, consider it an ordinary mailbox. */
282
283         if (!found) {
284                 IAPuts("mail");
285         }
286
287         /* "mail.outbox" and "junkemail" are not implemented. */
288
289         IAPuts("\")\r\n");
290
291         /*
292          * If a different folder was previously selected, return there now.
293          */
294         if ( (IMAP->selected) && (strcasecmp(roomname, savedroom)) ) {
295                 CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
296         }
297
298         IReply("OK GETMETADATA complete");
299         return;
300 }
301