4 * Utility functions for the IMAP module.
14 #include "sysdep_decls.h"
17 #include "internet_addressing.h"
18 #include "imap_tools.h"
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.)
29 void imap_strout(char *buf)
34 if (buf == NULL) { /* yeah, we handle this */
38 for (i = 0; i < strlen(buf); ++i) {
39 if ((buf[i] == '\"') || (buf[i] == '\\'))
44 cprintf("{%ld}\r\n%s", (long)strlen(buf), buf);
46 cprintf("\"%s\"", buf);
55 * Break a command down into tokens, taking into consideration the
56 * possibility of escaping spaces using quoted tokens
58 int imap_parameterize(char **args, char *buf)
68 original_len = strlen(buf);
70 for (i = 0; i < original_len; ++i) {
72 if ((isspace(buf[i])) && (!in_quote)) {
74 args[num] = &buf[start];
76 if (args[num][0] == '\"') {
78 args[num][strlen(args[num]) - 1] = 0;
81 } else if ((buf[i] == '\"') && (!in_quote)) {
83 } else if ((buf[i] == '\"') && (in_quote)) {
92 * Convert a struct ctdlroom to an IMAP-compatible mailbox name.
94 void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
100 * For mailboxes, just do it straight.
101 * Also handle Kolab-compatible groupware folder names.
103 if (qrbuf->QRflags & QR_MAILBOX) {
104 safestrncpy(buf, qrbuf->QRname, bufsize);
105 strcpy(buf, &buf[11]);
106 if (!strcasecmp(buf, MAILROOM)) {
107 strcpy(buf, "INBOX");
109 if (!strcasecmp(buf, USERCALENDARROOM)) {
110 sprintf(buf, "INBOX|%s", USERCALENDARROOM);
112 if (!strcasecmp(buf, USERCONTACTSROOM)) {
113 sprintf(buf, "INBOX|%s", USERCONTACTSROOM);
115 if (!strcasecmp(buf, USERTASKSROOM)) {
116 sprintf(buf, "INBOX|%s", USERTASKSROOM);
118 if (!strcasecmp(buf, USERNOTESROOM)) {
119 sprintf(buf, "INBOX|%s", USERNOTESROOM);
123 * Otherwise, prefix the floor name as a "public folders" moniker
126 fl = cgetfloor(qrbuf->QRfloor);
127 snprintf(buf, bufsize, "%s|%s",
133 * Replace delimiter characters with "|" for pseudo-folder-delimiting
135 for (i=0; i<strlen(buf); ++i) {
136 if (buf[i] == FDELIM) buf[i] = '|';
142 * Convert an inputted folder name to our best guess as to what an equivalent
143 * room name should be.
145 * If an error occurs, it returns -1. Otherwise...
147 * The lower eight bits of the return value are the floor number on which the
148 * room most likely resides. The upper eight bits may contain flags,
149 * including IR_MAILBOX if we're dealing with a personal room.
152 int imap_roomname(char *rbuf, int bufsize, char *foldername)
161 if (foldername == NULL) return(-1);
162 levels = num_parms(foldername);
165 * Convert the crispy idiot's reserved names to our reserved names.
166 * Also handle Kolab-compatible groupware folder names.
168 if (!strcasecmp(foldername, "INBOX")) {
169 safestrncpy(rbuf, MAILROOM, bufsize);
170 ret = (0 | IR_MAILBOX);
172 else if ( (!strncasecmp(foldername, "INBOX", 5))
173 && (!strcasecmp(&foldername[6], USERCALENDARROOM)) ) {
174 safestrncpy(rbuf, USERCALENDARROOM, bufsize);
175 ret = (0 | IR_MAILBOX);
177 else if ( (!strncasecmp(foldername, "INBOX", 5))
178 && (!strcasecmp(&foldername[6], USERCONTACTSROOM)) ) {
179 safestrncpy(rbuf, USERCONTACTSROOM, bufsize);
180 ret = (0 | IR_MAILBOX);
182 else if ( (!strncasecmp(foldername, "INBOX", 5))
183 && (!strcasecmp(&foldername[6], USERTASKSROOM)) ) {
184 safestrncpy(rbuf, USERTASKSROOM, bufsize);
185 ret = (0 | IR_MAILBOX);
187 else if ( (!strncasecmp(foldername, "INBOX", 5))
188 && (!strcasecmp(&foldername[6], USERNOTESROOM)) ) {
189 safestrncpy(rbuf, USERNOTESROOM, bufsize);
190 ret = (0 | IR_MAILBOX);
192 else if (levels > 1) {
193 extract(floorname, foldername, 0);
194 strcpy(roomname, &foldername[strlen(floorname)+1]);
195 for (i = 0; i < MAXFLOORS; ++i) {
197 if (fl->f_flags & F_INUSE) {
198 if (!strcasecmp(floorname, fl->f_name)) {
199 strcpy(rbuf, roomname);
206 /* No subfolderificationalisticism on this one... */
207 safestrncpy(rbuf, foldername, bufsize);
208 ret = (0 | IR_MAILBOX);
213 safestrncpy(rbuf, foldername, bufsize);
214 ret = (0 | IR_MAILBOX);
217 /* Undelimiterizationalize the room name (change '|') */
218 for (i=0; i<strlen(rbuf); ++i) {
219 if (rbuf[i] == '|') rbuf[i] = FDELIM;
223 /*** This doesn't work.
225 if (ret & IR_MAILBOX) {
226 if (atol(rbuf) == 0L) {
228 sprintf(rbuf, "%010ld.%s", CC->user.usernum, buf);
233 lprintf(9, "(That translates to \"%s\")\n", rbuf);
242 * Output a struct internet_address_list in the form an IMAP client wants
244 void imap_ial_out(struct internet_address_list *ialist)
246 struct internet_address_list *iptr;
248 if (ialist == NULL) {
254 for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
256 imap_strout(iptr->ial_name);
258 imap_strout(iptr->ial_user);
260 imap_strout(iptr->ial_node);
270 * Determine whether the supplied string is a valid message set.
271 * If the string contains only numbers, colons, commas, and asterisks,
272 * return 1 for a valid message set. If any other character is found,
275 int imap_is_message_set(char *buf)
280 return (0); /* stupidity checks */
281 if (strlen(buf) == 0)
284 if (!strcasecmp(buf, "ALL"))
285 return (1); /* macro? why? */
287 for (i = 0; i < strlen(buf); ++i) { /* now start the scan */
297 return (1); /* looks like we're good */
302 * imap_match.c, based on wildmat.c from INN
303 * hacked for Citadel/IMAP by Daniel Malament
306 /* note: not all return statements use these; don't change them */
307 #define WILDMAT_TRUE 1
308 #define WILDMAT_FALSE 0
309 #define WILDMAT_ABORT -1
310 #define WILDMAT_DELIM '|'
313 * Match text and p, return TRUE, FALSE, or ABORT.
315 static int do_imap_match(const char *supplied_text, const char *supplied_p)
318 char lcase_text[SIZ], lcase_p[SIZ];
319 char *text = lcase_text;
322 /* Copy both strings and lowercase them, in order to
323 * make this entire operation case-insensitive.
325 for (i=0; i<=strlen(supplied_text); ++i)
326 lcase_text[i] = tolower(supplied_text[i]);
327 for (i=0; i<=strlen(supplied_p); ++i)
328 p[i] = tolower(supplied_p[i]);
331 for (; *p; text++, p++) {
332 if ((*text == '\0') && (*p != '*') && (*p != '%')) {
333 return WILDMAT_ABORT;
338 return WILDMAT_FALSE;
343 while (++p, ((*p == '*') || (*p == '%'))) {
344 /* Consecutive stars or %'s act
345 * just like one star.
350 /* Trailing star matches everything. */
354 if ((matched = do_imap_match(text++, p))
359 return WILDMAT_ABORT;
361 while (++p, ((*p == '*') || (*p == '%'))) {
362 /* Consecutive %'s act just like one, but even
363 * a single star makes the sequence act like
373 * Trailing % matches everything
374 * without a delimiter.
377 if (*text == WILDMAT_DELIM) {
378 return WILDMAT_FALSE;
384 while (*text && (*(text - 1) != WILDMAT_DELIM)) {
385 if ((matched = do_imap_match(text++, p))
390 return WILDMAT_ABORT;
394 return (*text == '\0');
400 * Support function for mailbox pattern name matching in LIST and LSUB
401 * Returns nonzero if the supplied mailbox name matches the supplied pattern.
403 int imap_mailbox_matches_pattern(char *pattern, char *mailboxname)
405 /* handle just-star case quickly */
406 if ((pattern[0] == '*') && (pattern[1] == '\0')) {
409 return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
415 * Compare an IMAP date string (date only, no time) to the date found in
418 int imap_datecmp(char *datestr, time_t msgtime) {
423 int day, month, year;
424 int msgday, msgmonth, msgyear;
427 if (datestr == NULL) return(0);
429 /* Expecting a date in the form dd-Mmm-yyyy */
430 extract_token(daystr, datestr, 0, '-');
431 extract_token(monthstr, datestr, 1, '-');
432 extract_token(yearstr, datestr, 2, '-');
435 year = atoi(yearstr);
437 for (i=0; i<12; ++i) {
438 if (!strcasecmp(monthstr, ascmonths[i])) {
443 /* Extract day/month/year from message timestamp */
444 memcpy(&msgtm, localtime(&msgtime), sizeof(struct tm));
445 msgday = msgtm.tm_mday;
446 msgmonth = msgtm.tm_mon;
447 msgyear = msgtm.tm_year + 1900;
449 /* Now start comparing */
451 if (year < msgyear) return(+1);
452 if (year > msgyear) return(-1);
454 if (month < msgmonth) return(+1);
455 if (month > msgmonth) return(-1);
457 if (day < msgday) return(+1);
458 if (day > msgday) return(-1);