warning fixes and cleanups for 64-bit machines
[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("{%d}\r\n%s", 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
98         /*
99          * For mailboxes, just do it straight...
100          */
101         if (qrbuf->QRflags & QR_MAILBOX) {
102                 safestrncpy(buf, qrbuf->QRname, bufsize);
103                 strcpy(buf, &buf[11]);
104                 if (!strcasecmp(buf, MAILROOM))
105                         strcpy(buf, "INBOX");
106         }
107         /*
108          * Otherwise, prefix the floor name as a "public folders" moniker
109          */
110         else {
111                 fl = cgetfloor(qrbuf->QRfloor);
112                 snprintf(buf, bufsize, "%s|%s",
113                          fl->f_name,
114                          qrbuf->QRname);
115         }
116 }
117
118
119 /*
120  * Convert an inputted folder name to our best guess as to what an equivalent
121  * room name should be.
122  *
123  * If an error occurs, it returns -1.  Otherwise...
124  *
125  * The lower eight bits of the return value are the floor number on which the
126  * room most likely resides.   The upper eight bits may contain flags,
127  * including IR_MAILBOX if we're dealing with a personal room.
128  *
129  */
130 int imap_roomname(char *rbuf, int bufsize, char *foldername)
131 {
132         int levels;
133         char buf[SIZ];
134         int i;
135         struct floor *fl;
136
137         if (foldername == NULL)
138                 return (-1);
139         levels = num_parms(foldername);
140
141         /* When we can support hierarchial mailboxes, take this out. */
142         if (levels > 2)
143                 return (-1);
144
145         /*
146          * Convert the crispy idiot's reserved names to our reserved names.
147          */
148         if (!strcasecmp(foldername, "INBOX")) {
149                 safestrncpy(rbuf, MAILROOM, bufsize);
150                 return (0 | IR_MAILBOX);
151         }
152         if (levels > 1) {
153                 extract(buf, foldername, 0);
154                 for (i = 0; i < MAXFLOORS; ++i) {
155                         fl = cgetfloor(i);
156                         if (fl->f_flags & F_INUSE) {
157                                 if (!strcasecmp(buf, fl->f_name)) {
158                                         extract(rbuf, foldername, 1);
159                                         return (i);
160                                 }
161                         }
162                 }
163
164                 /* since we don't allow multi-level yet, fail.
165                    extract(rbuf, buf, 1);
166                    return(0);
167                  */
168                 return (-1);
169         }
170         safestrncpy(rbuf, foldername, bufsize);
171         return (0 | IR_MAILBOX);
172 }
173
174
175
176
177
178 /*
179  * Output a struct internet_address_list in the form an IMAP client wants
180  */
181 void imap_ial_out(struct internet_address_list *ialist)
182 {
183         struct internet_address_list *iptr;
184
185         if (ialist == NULL) {
186                 cprintf("NIL");
187                 return;
188         }
189         cprintf("(");
190
191         for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
192                 cprintf("(");
193                 imap_strout(iptr->ial_name);
194                 cprintf(" NIL ");
195                 imap_strout(iptr->ial_user);
196                 cprintf(" ");
197                 imap_strout(iptr->ial_node);
198                 cprintf(")");
199         }
200
201         cprintf(")");
202 }
203
204
205
206 /*
207  * Determine whether the supplied string is a valid message set.
208  * If the string contains only numbers, colons, commas, and asterisks,
209  * return 1 for a valid message set.  If any other character is found, 
210  * return 0.
211  */
212 int imap_is_message_set(char *buf)
213 {
214         int i;
215
216         if (buf == NULL)
217                 return (0);     /* stupidity checks */
218         if (strlen(buf) == 0)
219                 return (0);
220
221         if (!strcasecmp(buf, "ALL"))
222                 return (1);     /* macro?  why?  */
223
224         for (i = 0; i < strlen(buf); ++i) {     /* now start the scan */
225                 if (
226                            (!isdigit(buf[i]))
227                            && (buf[i] != ':')
228                            && (buf[i] != ',')
229                            && (buf[i] != '*')
230                     )
231                         return (0);
232         }
233
234         return (1);             /* looks like we're good */
235 }
236
237
238 /*
239  * imap_match.c, based on wildmat.c from INN
240  * hacked for Citadel/IMAP by Daniel Malament
241  */
242
243 /* note: not all return statements use these; don't change them */
244 #define WILDMAT_TRUE    1
245 #define WILDMAT_FALSE   0
246 #define WILDMAT_ABORT   -1
247 #define WILDMAT_DELIM   '|'
248
249 /*
250  * Match text and p, return TRUE, FALSE, or ABORT.
251  */
252 static int do_imap_match(const char *supplied_text, const char *supplied_p)
253 {
254         int matched, i;
255         char lcase_text[SIZ], lcase_p[SIZ];
256         char *text = lcase_text;
257         char *p = lcase_p;
258
259         /* Copy both strings and lowercase them, in order to
260          * make this entire operation case-insensitive.
261          */
262         for (i=0; i<=strlen(supplied_text); ++i)
263                 lcase_text[i] = tolower(supplied_text[i]);
264         for (i=0; i<=strlen(supplied_p); ++i)
265                 p[i] = tolower(supplied_p[i]);
266
267         /* Start matching */
268         for (; *p; text++, p++) {
269                 if ((*text == '\0') && (*p != '*') && (*p != '%')) {
270                         return WILDMAT_ABORT;
271                 }
272                 switch (*p) {
273                 default:
274                         if (*text != *p) {
275                                 return WILDMAT_FALSE;
276                         }
277                         continue;
278                 case '*':
279 star:
280                         while (++p, ((*p == '*') || (*p == '%'))) {
281                                 /* Consecutive stars or %'s act
282                                  * just like one star.
283                                  */
284                                 continue;
285                         }
286                         if (*p == '\0') {
287                                 /* Trailing star matches everything. */
288                                 return WILDMAT_TRUE;
289                         }
290                         while (*text) {
291                                 if ((matched = do_imap_match(text++, p))
292                                    != WILDMAT_FALSE) {
293                                         return matched;
294                                 }
295                         }
296                         return WILDMAT_ABORT;
297                 case '%':
298                         while (++p, ((*p == '*') || (*p == '%'))) {
299                                 /* Consecutive %'s act just like one, but even
300                                  * a single star makes the sequence act like
301                                  * one star, instead.
302                                  */
303                                 if (*p == '*') {
304                                         goto star;
305                                 }
306                                 continue;
307                         }
308                         if (*p == '\0') {
309                                 /*
310                                  * Trailing % matches everything
311                                  * without a delimiter.
312                                  */
313                                 while (*text) {
314                                         if (*text == WILDMAT_DELIM) {
315                                                 return WILDMAT_FALSE;
316                                         }
317                                         text++;
318                                 }
319                                 return WILDMAT_TRUE;
320                         }
321                         while (*text && (*(text - 1) != WILDMAT_DELIM)) {
322                                 if ((matched = do_imap_match(text++, p))
323                                    != WILDMAT_FALSE) {
324                                         return matched;
325                                 }
326                         }
327                         return WILDMAT_ABORT;
328                 }
329         }
330
331         return (*text == '\0');
332 }
333
334
335
336 /*
337  * Support function for mailbox pattern name matching in LIST and LSUB
338  * Returns nonzero if the supplied mailbox name matches the supplied pattern.
339  */
340 int imap_mailbox_matches_pattern(char *pattern, char *mailboxname)
341 {
342         /* handle just-star case quickly */
343         if ((pattern[0] == '*') && (pattern[1] == '\0')) {
344                 return WILDMAT_TRUE;
345         }
346         return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
347 }