]> code.citadel.org Git - citadel.git/blob - citadel/imap_list.c
648ff5c8ad40e21693aedcb9347e7b5d345f62de
[citadel.git] / citadel / imap_list.c
1 /*
2  * $Id:$
3  *
4  * Implements the LIST and LSUB commands.
5  *
6  * Copyright (C) 2000-2007 by Art Cancro and others.
7  * This code is released under the terms of the GNU General Public License.
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 "citadel.h"
37 #include "server.h"
38 #include "sysdep_decls.h"
39 #include "citserver.h"
40 #include "support.h"
41 #include "config.h"
42 #include "serv_extensions.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 "tools.h"
49 #include "internet_addressing.h"
50 #include "serv_imap.h"
51 #include "imap_tools.h"
52 #include "imap_fetch.h"
53 #include "imap_search.h"
54 #include "imap_store.h"
55 #include "imap_acl.h"
56 #include "imap_misc.h"
57
58 #ifdef HAVE_OPENSSL
59 #include "serv_crypto.h"
60 #endif
61
62 /*
63  * Used by LIST and LSUB to show the floors in the listing
64  */
65 void imap_list_floors(char *verb, int num_patterns, char **patterns)
66 {
67         int i;
68         struct floor *fl;
69         int j = 0;
70         int match = 0;
71
72         for (i = 0; i < MAXFLOORS; ++i) {
73                 fl = cgetfloor(i);
74                 if (fl->f_flags & F_INUSE) {
75                         match = 0;
76                         for (j=0; j<num_patterns; ++j) {
77                                 if (imap_mailbox_matches_pattern (patterns[j], fl->f_name)) {
78                                         match = 1;
79                                 }
80                         }
81                         if (match) {
82                                 cprintf("* %s (\\NoSelect) \"/\" ", verb);
83                                 imap_strout(fl->f_name);
84                                 cprintf("\r\n");
85                         }
86                 }
87         }
88 }
89
90
91 /*
92  * Back end for imap_list()
93  *
94  * Implementation note: IMAP "subscribed folder" is equivalent to Citadel "known room"
95  *
96  * The "user data" field is actually an array of pointers; see below for the breakdown
97  *
98  */
99 void imap_listroom(struct ctdlroom *qrbuf, void *data)
100 {
101         char buf[SIZ];
102         char return_options[256];
103         int ra;
104         int yes_output_this_room;
105
106         char **data_for_callback;
107         char *verb;
108         int subscribed_rooms_only;
109         int num_patterns;
110         char **patterns;
111         int return_subscribed;
112         int i = 0;
113         int match = 0;
114
115         /* Here's how we break down the array of pointers passed to us */
116         data_for_callback = data;
117         verb = data_for_callback[0];
118         subscribed_rooms_only = (int) data_for_callback[1];
119         num_patterns = (int) data_for_callback[2];
120         patterns = (char **) data_for_callback[3];
121         return_subscribed = (int) data_for_callback[4];
122
123         /* Only list rooms to which the user has access!! */
124         yes_output_this_room = 0;
125         strcpy(return_options, "");
126         CtdlRoomAccess(qrbuf, &CC->user, &ra, NULL);
127
128         if (return_subscribed) {
129                 if (ra & UA_KNOWN) {
130                         strcat(return_options, "\\Subscribed");
131                 }
132         }
133
134         if (subscribed_rooms_only) {
135                 if (ra & UA_KNOWN) {
136                         yes_output_this_room = 1;
137                 }
138         }
139         else {
140                 if ((ra & UA_KNOWN) || ((ra & UA_GOTOALLOWED) && (ra & UA_ZAPPED))) {
141                         yes_output_this_room = 1;
142                 }
143         }
144
145         if (yes_output_this_room) {
146                 imap_mailboxname(buf, sizeof buf, qrbuf);
147                 match = 0;
148                 for (i=0; i<num_patterns; ++i) {
149                         if (imap_mailbox_matches_pattern(patterns[i], buf)) {
150                                 match = 1;
151                         }
152                 }
153                 if (match) {
154                         cprintf("* %s (%s) \"/\" ", verb, return_options);
155                         imap_strout(buf);
156                         cprintf("\r\n");
157                 }
158         }
159 }
160
161 #define MAX_PATTERNS 20
162
163 /*
164  * Implements the LIST and LSUB commands
165  */
166 void imap_list(int num_parms, char *parms[])
167 {
168         int subscribed_rooms_only = 0;
169         char verb[16];
170         int i, j, paren_nest;
171         char *data_for_callback[5];
172         int num_patterns = 1;
173         char *patterns[MAX_PATTERNS];
174         int selection_left = (-1);
175         int selection_right = (-1);
176         int return_left = (-1);
177         int return_right = (-1);
178         int root_pos = 2;
179         int patterns_left = 3;
180         int patterns_right = 3;
181         int extended_list_in_use = 0;
182         int return_subscribed = 0;
183
184         if (num_parms < 4) {
185                 cprintf("%s BAD arguments invalid\r\n", parms[0]);
186                 return;
187         }
188
189         /* parms[1] is the IMAP verb being used (e.g. LIST or LSUB)
190          * This tells us how to behave, and what verb to return back to the caller
191          */
192         safestrncpy(verb, parms[1], sizeof verb);
193         j = strlen(verb);
194         for (i=0; i<j; ++i) {
195                 verb[i] = toupper(verb[i]);
196         }
197
198         if (!strcasecmp(verb, "LSUB")) {
199                 subscribed_rooms_only = 1;
200         }
201
202         /*
203          * In order to implement draft-ietf-imapext-list-extensions-18
204          * ("LIST Command Extensions") we need to:
205          * 1. Extract "selection options" (DONE, but we don't do anything with it yet.  We
206          *                                 need to implement SUBSCRIBED, and RECURSIVEMATCH,
207          *                                 and silently ignore REMOTE)
208          * 2. Extract "return options" (DONE, but so far we only implement the SUBSCRIBED
209          *                              option; we also need to implement the CHILDREN
210          *                              return option.)
211          * 3. Determine whether there is more than one match pattern (DONE)
212          */
213
214         /*
215          * If parameter 2 begins with a '(' character, the client is specifying
216          * selection options.  Extract their exact position, and then modify our
217          * expectation of where the root folder will be specified.
218          */
219         if (parms[2][0] == '(') {
220                 extended_list_in_use = 1;
221                 selection_left = 2;
222                 paren_nest = 0;
223                 for (i=2; i<num_parms; ++i) {
224                         for (j=0; j<strlen(parms[i]); ++j) {
225                                 if (parms[i][j] == '(') ++paren_nest;
226                                 if (parms[i][j] == ')') --paren_nest;
227                         }
228                         if (paren_nest == 0) {
229                                 selection_right = i;    /* found end of selection options */
230                                 root_pos = i+1;         /* folder root appears after selection options */
231                                 i = num_parms + 1;      /* break out of the loop */
232                         }
233                 }
234         }
235
236         /* The folder root appears immediately after the selection options,
237          * or in position 2 if no selection options were specified.
238          */
239         patterns_left = root_pos + 1;
240         patterns_right = root_pos + 1;
241
242         if (parms[patterns_left][0] == '(') {
243                 extended_list_in_use = 1;
244                 paren_nest = 0;
245                 for (i=patterns_left; i<num_parms; ++i) {
246                         for (j=0; j<strlen(parms[i]); ++j) {
247                                 if (parms[i][j] == '(') ++paren_nest;
248                                 if (parms[i][j] == ')') --paren_nest;
249                         }
250                         if (paren_nest == 0) {
251                                 patterns_right = i;     /* found end of patterns */
252                                 i = num_parms + 1;      /* break out of the loop */
253                         }
254                 }
255                 num_patterns = patterns_right - patterns_left + 1;
256                 for (i=0; i<num_patterns; ++i) {
257                         if (i < MAX_PATTERNS) {
258                                 patterns[i] = malloc(512);
259                                 snprintf(patterns[i], 512, "%s%s", parms[root_pos], parms[patterns_left+i]);
260                                 if (i == 0) {
261                                         strcpy(patterns[i], &patterns[i][1]);
262                                 }
263                                 if (i == num_patterns-1) {
264                                         patterns[i][strlen(patterns[i])-1] = 0;
265                                 }
266                         }
267                 }
268         }
269         else {
270                 num_patterns = 1;
271                 patterns[0] = malloc(512);
272                 snprintf(patterns[0], 512, "%s%s", parms[root_pos], parms[patterns_left]);
273         }
274
275         /* If the word "RETURN" appears after the folder pattern list, then the client
276          * is specifying return options.
277          */
278         if (num_parms - patterns_right > 2) if (!strcasecmp(parms[patterns_right+1], "RETURN")) {
279                 return_left = patterns_right + 2;
280                 extended_list_in_use = 1;
281                 paren_nest = 0;
282                 for (i=return_left; i<num_parms; ++i) {
283                         for (j=0; j<strlen(parms[i]); ++j) {
284                                 if (parms[i][j] == '(') ++paren_nest;
285                                 if (parms[i][j] == ')') --paren_nest;
286                         }
287
288                         /* Might as well look for these while we're in here... */
289                         if (parms[i][0] == '(') strcpy(parms[i], &parms[i][1]);
290                         if (parms[i][strlen(parms[i])-1] == ')') parms[i][strlen(parms[i])-1] = 0;
291                         lprintf(9, "evaluating <%s>\n", parms[i]);
292                         if (!strcasecmp(parms[i], "SUBSCRIBED")) {
293                                 return_subscribed = 1;
294                         }
295
296                         if (paren_nest == 0) {
297                                 return_right = i;       /* found end of patterns */
298                                 i = num_parms + 1;      /* break out of the loop */
299                         }
300                 }
301         }
302
303         /* Now start setting up the data we're going to send to the ForEachRoom() callback.
304          */
305         data_for_callback[0] = (char *) verb;
306         data_for_callback[1] = (char *) subscribed_rooms_only;
307         data_for_callback[2] = (char *) num_patterns;
308         data_for_callback[3] = (char *) patterns;
309         data_for_callback[4] = (char *) return_subscribed;
310
311         /* The non-extended LIST command is required to treat an empty
312          * ("" string) mailbox name argument as a special request to return the
313          * hierarchy delimiter and the root name of the name given in the
314          * reference parameter.
315          */
316         if ( (strlen(patterns[0]) == 0) && (extended_list_in_use == 0) ) {
317                 cprintf("* %s (\\Noselect) \"/\" \"\"\r\n", verb);
318         }
319
320         /* Non-empty mailbox names, and any form of the extended LIST command,
321          * is handled by this loop.
322          */
323         else {
324                 imap_list_floors(verb, num_patterns, patterns);
325                 ForEachRoom(imap_listroom, data_for_callback);
326         }
327
328         /* 
329          * Free the pattern buffers we allocated above.
330          */
331         for (i=0; i<num_patterns; ++i) {
332                 free(patterns[i]);
333         }
334
335         cprintf("%s OK %s completed\r\n", parms[0], verb);
336 }