]> code.citadel.org Git - citadel.git/blob - citadel/imap_tools.c
02d5953b62c5d400f93a7a5080afbe19367206d2
[citadel.git] / citadel / imap_tools.c
1 /*
2  * $Id$
3  *
4  * Utility functions for the IMAP module.
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include "citadel.h"
14 #include "sysdep_decls.h"
15 #include "tools.h"
16 #include "room_ops.h"
17 #include "internet_addressing.h"
18 #include "imap_tools.h"
19
20
21 #ifndef HAVE_SNPRINTF
22 #include "snprintf.h"
23 #endif
24
25 /*
26  * Output a string to the IMAP client, either as a literal or quoted.
27  * (We do a literal if it has any double-quotes or backslashes.)
28  */
29 void imap_strout(char *buf)
30 {
31         int i;
32         int is_literal = 0;
33
34         if (buf == NULL) {      /* yeah, we handle this */
35                 cprintf("NIL");
36                 return;
37         }
38         for (i = 0; i < strlen(buf); ++i) {
39                 if ((buf[i] == '\"') || (buf[i] == '\\'))
40                         is_literal = 1;
41         }
42
43         if (is_literal) {
44                 cprintf("{%ld}\r\n%s", (long)strlen(buf), buf);
45         } else {
46                 cprintf("\"%s\"", buf);
47         }
48 }
49
50
51
52
53
54 /*
55  * Break a command down into tokens, taking into consideration the
56  * possibility of escaping spaces using quoted tokens
57  */
58 int imap_parameterize(char **args, char *buf)
59 {
60         int num = 0;
61         int start = 0;
62         int i;
63         int in_quote = 0;
64         int original_len;
65
66         strcat(buf, " ");
67
68         original_len = strlen(buf);
69
70         for (i = 0; i < original_len; ++i) {
71
72                 if ((isspace(buf[i])) && (!in_quote)) {
73                         buf[i] = 0;
74                         args[num] = &buf[start];
75                         start = i + 1;
76                         if (args[num][0] == '\"') {
77                                 ++args[num];
78                                 args[num][strlen(args[num]) - 1] = 0;
79                         }
80                         ++num;
81                 } else if ((buf[i] == '\"') && (!in_quote)) {
82                         in_quote = 1;
83                 } else if ((buf[i] == '\"') && (in_quote)) {
84                         in_quote = 0;
85                 }
86         }
87
88         return (num);
89 }
90
91 /*
92  * Convert a struct quickroom to an IMAP-compatible mailbox name.
93  */
94 void imap_mailboxname(char *buf, int bufsize, struct quickroom *qrbuf)
95 {
96         struct floor *fl;
97         int i;
98
99         /*
100          * For mailboxes, just do it straight...
101          */
102         if (qrbuf->QRflags & QR_MAILBOX) {
103                 safestrncpy(buf, qrbuf->QRname, bufsize);
104                 strcpy(buf, &buf[11]);
105                 if (!strcasecmp(buf, MAILROOM))
106                         strcpy(buf, "INBOX");
107         }
108         /*
109          * Otherwise, prefix the floor name as a "public folders" moniker
110          */
111         else {
112                 fl = cgetfloor(qrbuf->QRfloor);
113                 snprintf(buf, bufsize, "%s|%s",
114                          fl->f_name,
115                          qrbuf->QRname);
116         }
117
118         /*
119          * Replace "/" characters with "|" for pseudo-folder-delimiting
120          */
121         for (i=0; i<strlen(buf); ++i) {
122                 if (buf[i] == '/') buf[i] = '|';
123         }
124 }
125
126
127 /*
128  * Convert an inputted folder name to our best guess as to what an equivalent
129  * room name should be.
130  *
131  * If an error occurs, it returns -1.  Otherwise...
132  *
133  * The lower eight bits of the return value are the floor number on which the
134  * room most likely resides.   The upper eight bits may contain flags,
135  * including IR_MAILBOX if we're dealing with a personal room.
136  *
137  */
138 int imap_roomname(char *rbuf, int bufsize, char *foldername)
139 {
140         int levels;
141         char floorname[SIZ];
142         char roomname[SIZ];
143         int i;
144         struct floor *fl;
145         int ret = (-1);
146
147         if (foldername == NULL) return(-1);
148         levels = num_parms(foldername);
149
150         /*
151          * Convert the crispy idiot's reserved names to our reserved names.
152          */
153         if (!strcasecmp(foldername, "INBOX")) {
154                 safestrncpy(rbuf, MAILROOM, bufsize);
155                 ret = (0 | IR_MAILBOX);
156         }
157         else if (levels > 1) {
158                 extract(floorname, foldername, 0);
159                 strcpy(roomname, &foldername[strlen(floorname)+1]);
160                 for (i = 0; i < MAXFLOORS; ++i) {
161                         fl = cgetfloor(i);
162                         if (fl->f_flags & F_INUSE) {
163                                 if (!strcasecmp(floorname, fl->f_name)) {
164                                         strcpy(rbuf, roomname);
165                                         ret = i;
166                                 }
167                         }
168                 }
169
170                 if (ret < 0) {
171                         /* No subfolderificationalisticism on this one... */
172                         safestrncpy(rbuf, foldername, bufsize);
173                         ret = (0 | IR_MAILBOX);
174                 }
175
176         }
177         else {
178                 safestrncpy(rbuf, foldername, bufsize);
179                 ret = (0 | IR_MAILBOX);
180         }
181
182         /* Undelimiterizationalize the room name (change '|' to '/') */
183         for (i=0; i<strlen(rbuf); ++i) {
184                 if (rbuf[i] == '|') rbuf[i] = '/';
185         }
186
187         lprintf(9, "(That translates to \"%s\")\n", rbuf);
188         return(ret);
189 }
190
191
192
193
194
195 /*
196  * Output a struct internet_address_list in the form an IMAP client wants
197  */
198 void imap_ial_out(struct internet_address_list *ialist)
199 {
200         struct internet_address_list *iptr;
201
202         if (ialist == NULL) {
203                 cprintf("NIL");
204                 return;
205         }
206         cprintf("(");
207
208         for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
209                 cprintf("(");
210                 imap_strout(iptr->ial_name);
211                 cprintf(" NIL ");
212                 imap_strout(iptr->ial_user);
213                 cprintf(" ");
214                 imap_strout(iptr->ial_node);
215                 cprintf(")");
216         }
217
218         cprintf(")");
219 }
220
221
222
223 /*
224  * Determine whether the supplied string is a valid message set.
225  * If the string contains only numbers, colons, commas, and asterisks,
226  * return 1 for a valid message set.  If any other character is found, 
227  * return 0.
228  */
229 int imap_is_message_set(char *buf)
230 {
231         int i;
232
233         if (buf == NULL)
234                 return (0);     /* stupidity checks */
235         if (strlen(buf) == 0)
236                 return (0);
237
238         if (!strcasecmp(buf, "ALL"))
239                 return (1);     /* macro?  why?  */
240
241         for (i = 0; i < strlen(buf); ++i) {     /* now start the scan */
242                 if (
243                            (!isdigit(buf[i]))
244                            && (buf[i] != ':')
245                            && (buf[i] != ',')
246                            && (buf[i] != '*')
247                     )
248                         return (0);
249         }
250
251         return (1);             /* looks like we're good */
252 }
253
254
255 /*
256  * imap_match.c, based on wildmat.c from INN
257  * hacked for Citadel/IMAP by Daniel Malament
258  */
259
260 /* note: not all return statements use these; don't change them */
261 #define WILDMAT_TRUE    1
262 #define WILDMAT_FALSE   0
263 #define WILDMAT_ABORT   -1
264 #define WILDMAT_DELIM   '|'
265
266 /*
267  * Match text and p, return TRUE, FALSE, or ABORT.
268  */
269 static int do_imap_match(const char *supplied_text, const char *supplied_p)
270 {
271         int matched, i;
272         char lcase_text[SIZ], lcase_p[SIZ];
273         char *text = lcase_text;
274         char *p = lcase_p;
275
276         /* Copy both strings and lowercase them, in order to
277          * make this entire operation case-insensitive.
278          */
279         for (i=0; i<=strlen(supplied_text); ++i)
280                 lcase_text[i] = tolower(supplied_text[i]);
281         for (i=0; i<=strlen(supplied_p); ++i)
282                 p[i] = tolower(supplied_p[i]);
283
284         /* Start matching */
285         for (; *p; text++, p++) {
286                 if ((*text == '\0') && (*p != '*') && (*p != '%')) {
287                         return WILDMAT_ABORT;
288                 }
289                 switch (*p) {
290                 default:
291                         if (*text != *p) {
292                                 return WILDMAT_FALSE;
293                         }
294                         continue;
295                 case '*':
296 star:
297                         while (++p, ((*p == '*') || (*p == '%'))) {
298                                 /* Consecutive stars or %'s act
299                                  * just like one star.
300                                  */
301                                 continue;
302                         }
303                         if (*p == '\0') {
304                                 /* Trailing star matches everything. */
305                                 return WILDMAT_TRUE;
306                         }
307                         while (*text) {
308                                 if ((matched = do_imap_match(text++, p))
309                                    != WILDMAT_FALSE) {
310                                         return matched;
311                                 }
312                         }
313                         return WILDMAT_ABORT;
314                 case '%':
315                         while (++p, ((*p == '*') || (*p == '%'))) {
316                                 /* Consecutive %'s act just like one, but even
317                                  * a single star makes the sequence act like
318                                  * one star, instead.
319                                  */
320                                 if (*p == '*') {
321                                         goto star;
322                                 }
323                                 continue;
324                         }
325                         if (*p == '\0') {
326                                 /*
327                                  * Trailing % matches everything
328                                  * without a delimiter.
329                                  */
330                                 while (*text) {
331                                         if (*text == WILDMAT_DELIM) {
332                                                 return WILDMAT_FALSE;
333                                         }
334                                         text++;
335                                 }
336                                 return WILDMAT_TRUE;
337                         }
338                         while (*text && (*(text - 1) != WILDMAT_DELIM)) {
339                                 if ((matched = do_imap_match(text++, p))
340                                    != WILDMAT_FALSE) {
341                                         return matched;
342                                 }
343                         }
344                         return WILDMAT_ABORT;
345                 }
346         }
347
348         return (*text == '\0');
349 }
350
351
352
353 /*
354  * Support function for mailbox pattern name matching in LIST and LSUB
355  * Returns nonzero if the supplied mailbox name matches the supplied pattern.
356  */
357 int imap_mailbox_matches_pattern(char *pattern, char *mailboxname)
358 {
359         /* handle just-star case quickly */
360         if ((pattern[0] == '*') && (pattern[1] == '\0')) {
361                 return WILDMAT_TRUE;
362         }
363         return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
364 }