citadel
About: Citadel is an advanced messaging and collaboration system for groupware and BBS applications (preferred OS: Linux).
  Fossies Dox: citadel.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

imap_tools.c
Go to the documentation of this file.
1/*
2 * Utility functions for the IMAP module.
3 *
4 * Copyright (c) 2001-2017 by the citadel.org team and others, except for
5 * most of the UTF7 and UTF8 handling code which was lifted from Evolution.
6 *
7 * This program is open source software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#define SHOW_ME_VAPPEND_PRINTF
23#include <stdlib.h>
24#include <unistd.h>
25#include <stdio.h>
26#include <ctype.h>
27#include <string.h>
28#include <stdarg.h>
29#include <libcitadel.h>
30#include "citadel.h"
31#include "sysdep_decls.h"
32#include "internet_addressing.h"
33#include "serv_imap.h"
34#include "imap_tools.h"
35#include "ctdl_module.h"
36
37/* String handling helpers */
38
39/* This code uses some pretty nasty string manipulation. To make everything
40 * manageable, we use this semi-high-level string manipulation API. Strings are
41 * always \0-terminated, despite the fact that we keep track of the size.
42 */
43struct string {
44 char* buffer;
46 int size;
47};
48
49static void string_init(struct string* s, char* buf, int bufsize)
50{
51 s->buffer = buf;
52 s->maxsize = bufsize-1;
53 s->size = strlen(buf);
54}
55
56static char* string_end(struct string* s)
57{
58 return s->buffer + s->size;
59}
60
61/* Append a UTF8 string of a particular length (in bytes). -1 to autocalculate. */
62
63static void string_append_sn(struct string* s, char* p, int len)
64{
65 if (len == -1)
66 len = strlen(p);
67 if ((s->size+len) > s->maxsize)
68 len = s->maxsize - s->size;
69 memcpy(s->buffer + s->size, p, len);
70 s->size += len;
71 s->buffer[s->size] = '\0';
72}
73
74/* As above, always autocalculate. */
75
76#define string_append_s(s, p) string_append_sn((s), (p), -1)
77
78/* Appends a UTF8 character --- which may make the size change by more than 1!
79 * If the string overflows, the last character may become invalid. */
80
81static void string_append_c(struct string* s, int c)
82{
83 char UmlChar[5];
84 int len = 0;
85
86 /* Don't do anything if there's no room. */
87
88 if (s->size == s->maxsize)
89 return;
90
91 if (c <= 0x7F)
92 {
93 /* This is the most common case, so we optimise it. */
94
95 s->buffer[s->size++] = c;
96 s->buffer[s->size] = 0;
97 return;
98 }
99 else if (c <= 0x7FF)
100 {
101 UmlChar[0] = 0xC0 | (c >> 6);
102 UmlChar[1] = 0x80 | (c & 0x3F);
103 len = 2;
104 }
105 else if (c <= 0xFFFF)
106 {
107 UmlChar[0] = 0xE0 | (c >> 12);
108 UmlChar[1] = 0x80 | ((c >> 6) & 0x3f);
109 UmlChar[2] = 0x80 | (c & 0x3f);
110 len = 3;
111 }
112 else
113 {
114 UmlChar[0] = 0xf0 | c >> 18;
115 UmlChar[1] = 0x80 | ((c >> 12) & 0x3f);
116 UmlChar[2] = 0x80 | ((c >> 6) & 0x3f);
117 UmlChar[3] = 0x80 | (c & 0x3f);
118 len = 4;
119 }
120
121 string_append_sn(s, UmlChar, len);
122}
123
124/* Reads a UTF8 character from a char*, advancing the pointer. */
125
126int utf8_getc(char** ptr)
127{
128 unsigned char* p = (unsigned char*) *ptr;
129 unsigned char c, r;
130 int v, m;
131
132 for (;;)
133 {
134 r = *p++;
135 loop:
136 if (r < 0x80)
137 {
138 *ptr = (char*) p;
139 v = r;
140 break;
141 }
142 else if (r < 0xf8)
143 {
144 /* valid start char? (max 4 octets) */
145 v = r;
146 m = 0x7f80; /* used to mask out the length bits */
147 do {
148 c = *p++;
149 if ((c & 0xc0) != 0x80)
150 {
151 r = c;
152 goto loop;
153 }
154 v = (v<<6) | (c & 0x3f);
155 r<<=1;
156 m<<=5;
157 } while (r & 0x40);
158
159 *ptr = (char*)p;
160
161 v &= ~m;
162 break;
163 }
164 }
165
166 return v;
167}
168
169/* IMAP name safety */
170
171/* IMAP has certain special requirements in its character set, which means we
172 * have to do a fair bit of work to convert Citadel's UTF8 strings to IMAP
173 * strings. The next two routines (and their data tables) do that.
174 */
175
176static char *utf7_alphabet =
177 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
178
179static unsigned char utf7_rank[256] = {
180 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
181 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
182 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0x3F,0xFF,0xFF,0xFF,
183 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
184 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
185 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
186 0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
187 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,
188 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
189 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
190 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
191 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
192 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
193 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
194 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
195 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
196};
197
198/* Base64 helpers. */
199
200static void utf7_closeb64(struct string* out, int v, int i)
201{
202 int x;
203
204 if (i > 0)
205 {
206 x = (v << (6-i)) & 0x3F;
208 }
209 string_append_c(out, '-');
210}
211
212/* Convert from a Citadel name to an IMAP-safe name. Returns the end
213 * of the destination.
214 */
215static char* toimap(char* destp, char* destend, char* src)
216{
217 struct string dest;
218 int state = 0;
219 int v = 0;
220 int i = 0;
221
222 *destp = 0;
223 string_init(&dest, destp, destend-destp);
224 /* syslog(LOG_DEBUG, "toimap %s", src); */
225
226 for (;;)
227 {
228 int c = utf8_getc(&src);
229 if (c == '\0')
230 break;
231
232 if (c >= 0x20 && c <= 0x7e)
233 {
234 if (state == 1)
235 {
236 utf7_closeb64(&dest, v, i);
237 state = 0;
238 i = 0;
239 }
240
241 switch (c)
242 {
243 case '&':
244 string_append_sn(&dest, "&-", 2);
245 break;
246
247 case '/':
248 /* Citadel extension: / becomes |, because /
249 * isn't valid as part of an IMAP name. */
250
251 c = '|';
252 goto defaultcase;
253
254 case '\\':
255 /* Citadel extension: backslashes mark folder
256 * seperators in the IMAP subfolder emulation
257 * hack. We turn them into / characters,
258 * *except* if it's the last character in the
259 * string. */
260
261 if (*src != '\0')
262 c = '/';
263 /* fall through */
264
265 default:
266 defaultcase:
267 string_append_c(&dest, c);
268 }
269 }
270 else
271 {
272 if (state == 0)
273 {
274 string_append_c(&dest, '&');
275 state = 1;
276 }
277 v = (v << 16) | c;
278 i += 16;
279 while (i >= 6)
280 {
281 int x = (v >> (i-6)) & 0x3f;
283 i -= 6;
284 }
285 }
286 }
287
288 if (state == 1)
289 utf7_closeb64(&dest, v, i);
290 /* syslog(LOG_DEBUG, " -> %s", destp); */
291 return string_end(&dest);
292}
293
294/* Convert from an IMAP-safe name back into a Citadel name. Returns the end of the destination. */
295
296static int cfrommap(int c);
297static char* fromimap(char* destp, char* destend, const char* src)
298{
299 struct string dest;
300 unsigned const char *p = (unsigned const char*) src;
301 int v = 0;
302 int i = 0;
303 int state = 0;
304 int c;
305
306 *destp = 0;
307 string_init(&dest, destp, destend-destp);
308 /* syslog(LOG_DEBUG, "fromimap %s", src); */
309
310 do {
311 c = *p++;
312 switch (state)
313 {
314 case 0:
315 /* US-ASCII characters. */
316
317 if (c == '&')
318 state = 1;
319 else
320 string_append_c(&dest, cfrommap(c));
321 break;
322
323 case 1:
324 if (c == '-')
325 {
326 string_append_c(&dest, '&');
327 state = 0;
328 }
329 else if (utf7_rank[c] != 0xff)
330 {
331 v = utf7_rank[c];
332 i = 6;
333 state = 2;
334 }
335 else
336 {
337 /* invalid char */
338 string_append_sn(&dest, "&-", 2);
339 state = 0;
340 }
341 break;
342
343 case 2:
344 if (c == '-')
345 state = 0;
346 else if (utf7_rank[c] != 0xFF)
347 {
348 v = (v<<6) | utf7_rank[c];
349 i += 6;
350 if (i >= 16)
351 {
352 int x = (v >> (i-16)) & 0xFFFF;
353 string_append_c(&dest, cfrommap(x));
354 i -= 16;
355 }
356 }
357 else
358 {
359 string_append_c(&dest, cfrommap(c));
360 state = 0;
361 }
362 break;
363 }
364 } while (c != '\0');
365
366 /* syslog(LOG_DEBUG, " -> %s", destp); */
367 return string_end(&dest);
368}
369
370/* Undoes the special character conversion. */
371static int cfrommap(int c)
372{
373 switch (c)
374 {
375 case '|': return '/';
376 case '/': return '\\';
377 }
378 return c;
379}
380
381
382/* Break a command down into tokens, unquoting any escaped characters. */
383void MakeStringOf(StrBuf *Buf, int skip)
384{
385 int i;
386 citimap_command *Cmd = &IMAP->Cmd;
387
388 for (i=skip; i<Cmd->num_parms; ++i) {
389 StrBufAppendBufPlain(Buf, Cmd->Params[i].Key, Cmd->Params[i].len, 0);
390 if (i < (Cmd->num_parms-1)) StrBufAppendBufPlain(Buf, HKEY(" "), 0);
391 }
392}
393
394
396 ConstStr *CutMe,
397 int n)
398{
399 const char *CutAt;
400
401 if (CutMe->len < n) {
402 CutAt = CutMe->Key;
403 CutMe->len = 0;
404 }
405 else {
406 CutAt = CutMe->Key + CutMe->len - n;
407 CutMe->len -= n;
408 }
409 StrBufPeek(Cmd->CmdBuf, CutAt, -1, '\0');
410}
411
413 ConstStr *CutMe,
414 int n)
415{
416 if (CutMe->len < n) {
417 CutMe->Key += CutMe->len;
418 CutMe->len = 0;
419 }
420 else {
421 CutMe->Key += n;
422 CutMe->len -= n;
423 }
424}
425
426
427
429 int nArgs,
430 int Realloc)
431{
432 ConstStr *Params;
433 if (nArgs > Cmd->avail_parms) {
434 Params = (ConstStr*) malloc(sizeof(ConstStr) * nArgs);
435 if (Realloc) {
436 memcpy(Params,
437 Cmd->Params,
438 sizeof(ConstStr) * Cmd->avail_parms);
439
440 memset(Cmd->Params +
441 sizeof(ConstStr) * Cmd->avail_parms,
442 0,
443 sizeof(ConstStr) * nArgs -
444 sizeof(ConstStr) * Cmd->avail_parms
445 );
446 }
447 else {
448 Cmd->num_parms = 0;
449 memset(Params, 0,
450 sizeof(ConstStr) * nArgs);
451 }
452 Cmd->avail_parms = nArgs;
453 if (Cmd->Params != NULL)
454 free (Cmd->Params);
455 Cmd->Params = Params;
456 }
457 else {
458 if (!Realloc) {
459 memset(Cmd->Params,
460 0,
461 sizeof(ConstStr) * Cmd->avail_parms);
462 Cmd->num_parms = 0;
463 }
464 }
465 return Cmd->avail_parms;
466}
467
469{
470 int nArgs;
471 const char *In, *End;
472
473 In = ChrPtr(Cmd->CmdBuf);
474 End = In + StrLength(Cmd->CmdBuf);
475
476 /* we start with 10 chars per arg, maybe we need to realloc later. */
477 nArgs = StrLength(Cmd->CmdBuf) / 10 + 10;
478 nArgs = CmdAdjust(Cmd, nArgs, 0);
479 while (In < End)
480 {
481 /* Skip whitespace. */
482 while (isspace(*In))
483 In++;
484 if (*In == '\0')
485 break;
486
487 /* Found the start of a token. */
488
489 Cmd->Params[Cmd->num_parms].Key = In;
490
491 /* Read in the token. */
492
493 for (;;)
494 {
495 if (isspace(*In))
496 break;
497
498 if (*In == '\"')
499 {
500 /* Found a quoted section. */
501
502 Cmd->Params[Cmd->num_parms].Key++;
503 //In++;
504 for (;;)
505 {
506 In++;
507 if (*In == '\"') {
508 StrBufPeek(Cmd->CmdBuf, In, -1, '\0');
509 break;
510 }
511 else if (*In == '\\')
512 In++;
513
514 if (*In == '\0') {
515 Cmd->Params[Cmd->num_parms].len =
516 In - Cmd->Params[Cmd->num_parms].Key;
517 Cmd->num_parms++;
518 return Cmd->num_parms;
519 }
520 }
521 break;
522 }
523 else if (*In == '\\')
524 {
525 In++;
526 }
527
528 if (*In == '\0') {
529 Cmd->Params[Cmd->num_parms].len =
530 In - Cmd->Params[Cmd->num_parms].Key;
531 Cmd->num_parms++;
532 return Cmd->num_parms;
533 }
534 In++;
535 }
536 StrBufPeek(Cmd->CmdBuf, In, -1, '\0');
537 Cmd->Params[Cmd->num_parms].len =
538 In - Cmd->Params[Cmd->num_parms].Key;
539 if (Cmd->num_parms + 1 >= Cmd->avail_parms) {
540 nArgs = CmdAdjust(Cmd, nArgs * 2, 1);
541 }
542 Cmd->num_parms ++;
543 In++;
544 }
545 return Cmd->num_parms;
546}
547
548
549/* Convert a struct ctdlroom to an IMAP-compatible mailbox name. */
550long imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
551{
552 char* bufend = buf+bufsize;
553 struct floor *fl;
554 char* p = buf;
555 const char *pend;
556
557 /* For mailboxes, just do it straight.
558 * Do the Cyrus-compatible thing: all private folders are
559 * subfolders of INBOX. */
560
561 if (qrbuf->QRflags & QR_MAILBOX)
562 {
563 if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0)
564 {
565 pend = toimap(p, bufend, "INBOX");
566 return pend - buf;
567 }
568 else
569 {
570 p = toimap(p, bufend, "INBOX");
571 if (p < bufend)
572 *p++ = '/';
573 pend = toimap(p, bufend, qrbuf->QRname+11);
574 return pend - buf;
575 }
576 }
577 else
578 {
579 /* Otherwise, prefix the floor name as a "public folders" moniker. */
580
582 p = toimap(p, bufend, fl->f_name);
583 if (p < bufend)
584 *p++ = '/';
585 pend = toimap(p, bufend, qrbuf->QRname);
586 return pend - buf;
587 }
588}
589
590/*
591 * Convert an inputted folder name to our best guess as to what an equivalent
592 * room name should be.
593 *
594 * If an error occurs, it returns -1. Otherwise...
595 *
596 * The lower eight bits of the return value are the floor number on which the
597 * room most likely resides. The upper eight bits may contain flags,
598 * including IR_MAILBOX if we're dealing with a personal room.
599 *
600 */
601int imap_roomname(char *rbuf, int bufsize, const char *foldername)
602{
603 int levels;
604 char floorname[ROOMNAMELEN*2];
605 char roomname[ROOMNAMELEN];
606 int i;
607 struct floor *fl;
608 int ret = (-1);
609
610 if (foldername == NULL)
611 return(-1);
612
613 /* Unmunge the entire string into the output buffer. */
614
615 fromimap(rbuf, rbuf+bufsize, foldername);
616
617 /* Is this an IMAP inbox? */
618
619 if (strncasecmp(rbuf, "INBOX", 5) == 0)
620 {
621 if (rbuf[5] == 0)
622 {
623 /* It's the system inbox. */
624
625 safestrncpy(rbuf, MAILROOM, bufsize);
626 ret = (0 | IR_MAILBOX);
627 goto exit;
628 }
629 else if (rbuf[5] == FDELIM)
630 {
631 /* It's another personal mail folder. */
632
633 safestrncpy(rbuf, rbuf+6, bufsize);
634 ret = (0 | IR_MAILBOX);
635 goto exit;
636 }
637
638 /* If we get here, the folder just happens to start with INBOX
639 * --- fall through. */
640 }
641
642 /* Is this a multi-level room name? */
643
644 levels = num_tokens(rbuf, FDELIM);
645 if (levels > 1)
646 {
647 long len;
648 /* Extract the main room name. */
649
650 len = extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname);
651 if (len < 0) len = 0;
652 safestrncpy(roomname, &rbuf[len + 1], sizeof(roomname));
653
654 /* Try and find it on any floor. */
655
656 for (i = 0; i < MAXFLOORS; ++i)
657 {
658 fl = CtdlGetCachedFloor(i);
659 if (fl->f_flags & F_INUSE)
660 {
661 if (strcasecmp(floorname, fl->f_name) == 0)
662 {
663 /* Got it! */
664
665 safestrncpy(rbuf, roomname, bufsize);
666 ret = i;
667 goto exit;
668 }
669 }
670 }
671 }
672
673 /* Meh. It's either not a multi-level room name, or else we
674 * couldn't find it.
675 */
676 ret = (0 | IR_MAILBOX);
677
678exit:
679 syslog(LOG_DEBUG, "(That translates to \"%s\")", rbuf);
680 return(ret);
681}
682
683
684/*
685 * Determine whether the supplied string is a valid message set.
686 * If the string contains only numbers, colons, commas, and asterisks,
687 * return 1 for a valid message set. If any other character is found,
688 * return 0.
689 */
690int imap_is_message_set(const char *buf)
691{
692 int i;
693
694 if (buf == NULL)
695 return (0); /* stupidity checks */
696 if (IsEmptyStr(buf))
697 return (0);
698
699 if (!strcasecmp(buf, "ALL"))
700 return (1); /* macro? why? */
701
702 for (i = 0; buf[i]; ++i) { /* now start the scan */
703 if (
704 (!isdigit(buf[i]))
705 && (buf[i] != ':')
706 && (buf[i] != ',')
707 && (buf[i] != '*')
708 )
709 return (0);
710 }
711
712 return (1); /* looks like we're good */
713}
714
715
716/*
717 * imap_match.c, based on wildmat.c from INN
718 * hacked for Citadel/IMAP by Daniel Malament
719 */
720
721/* note: not all return statements use these; don't change them */
722#define WILDMAT_TRUE 1
723#define WILDMAT_FALSE 0
724#define WILDMAT_ABORT -1
725#define WILDMAT_DELIM '/'
726
727/*
728 * Match text and p, return TRUE, FALSE, or ABORT.
729 */
730static int do_imap_match(const char *supplied_text, const char *supplied_p)
731{
732 int matched, i;
733 char lcase_text[SIZ], lcase_p[SIZ];
734 char *text;
735 char *p;
736
737 /* Copy both strings and lowercase them, in order to
738 * make this entire operation case-insensitive.
739 */
740 for (i=0;
741 ((supplied_text[i] != '\0') &&
742 (i < sizeof(lcase_text)));
743 ++i)
744 lcase_text[i] = tolower(supplied_text[i]);
745 lcase_text[i] = '\0';
746
747 for (i=0;
748 ((supplied_p[i] != '\0') &&
749 (i < sizeof(lcase_p)));
750 ++i)
751 lcase_p[i] = tolower(supplied_p[i]);
752 lcase_p[i] = '\0';
753
754 /* Start matching */
755 for (p = lcase_p, text = lcase_text;
756 !IsEmptyStr(p) && !IsEmptyStr(text);
757 text++, p++) {
758 if ((*text == '\0') && (*p != '*') && (*p != '%')) {
759 return WILDMAT_ABORT;
760 }
761 switch (*p) {
762 default:
763 if (*text != *p) {
764 return WILDMAT_FALSE;
765 }
766 continue;
767 case '*':
768star:
769 while (++p, ((*p == '*') || (*p == '%'))) {
770 /* Consecutive stars or %'s act
771 * just like one star.
772 */
773 continue;
774 }
775 if (*p == '\0') {
776 /* Trailing star matches everything. */
777 return WILDMAT_TRUE;
778 }
779 while (*text) {
780 if ((matched = do_imap_match(text++, p))
781 != WILDMAT_FALSE) {
782 return matched;
783 }
784 }
785 return WILDMAT_ABORT;
786 case '%':
787 while (++p, (!IsEmptyStr(p) && ((*p == '*') || (*p == '%'))))
788 {
789 /* Consecutive %'s act just like one, but even
790 * a single star makes the sequence act like
791 * one star, instead.
792 */
793 if (*p == '*') {
794 goto star;
795 }
796 continue;
797 }
798 if (*p == '\0') {
799 /*
800 * Trailing % matches everything
801 * without a delimiter.
802 */
803 while (!IsEmptyStr(text)) {
804 if (*text == WILDMAT_DELIM) {
805 return WILDMAT_FALSE;
806 }
807 text++;
808 }
809 return WILDMAT_TRUE;
810 }
811 while (!IsEmptyStr(text) &&
812 /* make sure text - 1 isn't before lcase_p */
813 ((text == lcase_text) || (*(text - 1) != WILDMAT_DELIM)))
814 {
815 if ((matched = do_imap_match(text++, p))
816 != WILDMAT_FALSE) {
817 return matched;
818 }
819 }
820 return WILDMAT_ABORT;
821 }
822 }
823
824 if ((*text == '\0') && (*p == '\0')) return WILDMAT_TRUE;
825 else return WILDMAT_FALSE;
826}
827
828
829/*
830 * Support function for mailbox pattern name matching in LIST and LSUB
831 * Returns nonzero if the supplied mailbox name matches the supplied pattern.
832 */
833int imap_mailbox_matches_pattern(const char *pattern, char *mailboxname)
834{
835 /* handle just-star case quickly */
836 if ((pattern[0] == '*') && (pattern[1] == '\0')) {
837 return WILDMAT_TRUE;
838 }
839 return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
840}
841
842
843
844/*
845 * Compare an IMAP date string (date only, no time) to the date found in
846 * a Unix timestamp.
847 */
848int imap_datecmp(const char *datestr, time_t msgtime) {
849 char daystr[256];
850 char monthstr[256];
851 char yearstr[256];
852 int i;
853 int day, month, year;
854 int msgday, msgmonth, msgyear;
855 struct tm msgtm;
856
857 char *imap_datecmp_ascmonths[12] = {
858 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
859 };
860
861 if (datestr == NULL) return(0);
862
863 /* Expecting a date in the form dd-Mmm-yyyy */
864 extract_token(daystr, datestr, 0, '-', sizeof daystr);
865 extract_token(monthstr, datestr, 1, '-', sizeof monthstr);
866 extract_token(yearstr, datestr, 2, '-', sizeof yearstr);
867
868 day = atoi(daystr);
869 year = atoi(yearstr);
870 month = 0;
871 for (i=0; i<12; ++i) {
872 if (!strcasecmp(monthstr, imap_datecmp_ascmonths[i])) {
873 month = i;
874 }
875 }
876
877 /* Extract day/month/year from message timestamp */
878 localtime_r(&msgtime, &msgtm);
879 msgday = msgtm.tm_mday;
880 msgmonth = msgtm.tm_mon;
881 msgyear = msgtm.tm_year + 1900;
882
883 /* Now start comparing */
884
885 if (year < msgyear) return(+1);
886 if (year > msgyear) return(-1);
887
888 if (month < msgmonth) return(+1);
889 if (month > msgmonth) return(-1);
890
891 if (day < msgday) return(+1);
892 if (day > msgday) return(-1);
893
894 return(0);
895}
896
897
898void IAPrintf(const char *Format, ...)
899{
900 va_list arg_ptr;
901
902 va_start(arg_ptr, Format);
903 StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr);
904 va_end(arg_ptr);
905}
906
907
908void iaputs(const char *Str, long Len)
909{
910 StrBufAppendBufPlain(IMAP->Reply, Str, Len, 0);
911}
912
913
914void ireply(const char *Msg, long len)
915{
916 citimap *Imap = IMAP;
917
918 StrBufAppendBufPlain(Imap->Reply,
919 CKEY(Imap->Cmd.Params[0]), 0);
920 StrBufAppendBufPlain(Imap->Reply,
921 HKEY(" "), 0);
922 StrBufAppendBufPlain(Imap->Reply,
923 Msg, len, 0);
924
925 StrBufAppendBufPlain(Imap->Reply,
926 HKEY("\r\n"), 0);
927
928}
929
930
931void IReplyPrintf(const char *Format, ...)
932{
933 citimap *Imap = IMAP;
934 va_list arg_ptr;
935
936
937 StrBufAppendBufPlain(Imap->Reply,
938 CKEY(Imap->Cmd.Params[0]), 0);
939
940 StrBufAppendBufPlain(Imap->Reply,
941 HKEY(" "), 0);
942
943 va_start(arg_ptr, Format);
944 StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr);
945 va_end(arg_ptr);
946
947 StrBufAppendBufPlain(Imap->Reply,
948 HKEY("\r\n"), 0);
949
950}
951
952
953/* Output a string to the IMAP client, either as a literal or quoted.
954 * (We do a literal if it has any double-quotes or backslashes.) */
955void IPutStr(const char *Msg, long Len)
956{
957 int i;
958 int is_literal = 0;
959 citimap *Imap = IMAP;
960
961
962 if ((Msg == NULL) || (Len == 0))
963 { /* yeah, we handle this */
964 StrBufAppendBufPlain(Imap->Reply, HKEY("NIL"), 0);
965 return;
966 }
967
968 for (i = 0; i < Len; ++i) {
969 if ((Msg[i] == '\"') || (Msg[i] == '\\'))
970 is_literal = 1;
971 }
972
973 if (is_literal) {
974 StrBufAppendPrintf(Imap->Reply, "{%ld}\r\n", Len);
975 StrBufAppendBufPlain(Imap->Reply, Msg, Len, 0);
976 } else {
977 StrBufAppendBufPlain(Imap->Reply,
978 HKEY("\""), 0);
979 StrBufAppendBufPlain(Imap->Reply,
980 Msg, Len, 0);
981 StrBufAppendBufPlain(Imap->Reply,
982 HKEY("\""), 0);
983 }
984}
985
986void IUnbuffer (void)
987{
988 citimap *Imap = IMAP;
989
990 cputbuf(Imap->Reply);
991 FlushStrBuf(Imap->Reply);
992}
#define ROOMNAMELEN
Definition: citadel.h:56
#define F_INUSE
Definition: citadel.h:152
struct floor * CtdlGetCachedFloor(int floor_num)
Definition: room_ops.c:496
#define WILDMAT_FALSE
Definition: imap_tools.c:723
#define WILDMAT_DELIM
Definition: imap_tools.c:725
void iaputs(const char *Str, long Len)
Definition: imap_tools.c:908
int imap_is_message_set(const char *buf)
Definition: imap_tools.c:690
static char * string_end(struct string *s)
Definition: imap_tools.c:56
static void string_append_c(struct string *s, int c)
Definition: imap_tools.c:81
long imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
Definition: imap_tools.c:550
static char * fromimap(char *destp, char *destend, const char *src)
Definition: imap_tools.c:297
void MakeStringOf(StrBuf *Buf, int skip)
Definition: imap_tools.c:383
static void string_append_sn(struct string *s, char *p, int len)
Definition: imap_tools.c:63
int imap_parameterize(citimap_command *Cmd)
Definition: imap_tools.c:468
void ireply(const char *Msg, long len)
Definition: imap_tools.c:914
void TokenCutLeft(citimap_command *Cmd, ConstStr *CutMe, int n)
Definition: imap_tools.c:412
static int cfrommap(int c)
Definition: imap_tools.c:371
#define WILDMAT_ABORT
Definition: imap_tools.c:724
void IReplyPrintf(const char *Format,...)
Definition: imap_tools.c:931
void IUnbuffer(void)
Definition: imap_tools.c:986
int imap_mailbox_matches_pattern(const char *pattern, char *mailboxname)
Definition: imap_tools.c:833
void IAPrintf(const char *Format,...)
Definition: imap_tools.c:898
int CmdAdjust(citimap_command *Cmd, int nArgs, int Realloc)
Definition: imap_tools.c:428
int utf8_getc(char **ptr)
Definition: imap_tools.c:126
static int do_imap_match(const char *supplied_text, const char *supplied_p)
Definition: imap_tools.c:730
void TokenCutRight(citimap_command *Cmd, ConstStr *CutMe, int n)
Definition: imap_tools.c:395
static void string_init(struct string *s, char *buf, int bufsize)
Definition: imap_tools.c:49
int imap_datecmp(const char *datestr, time_t msgtime)
Definition: imap_tools.c:848
static char * utf7_alphabet
Definition: imap_tools.c:176
static void utf7_closeb64(struct string *out, int v, int i)
Definition: imap_tools.c:200
static char * toimap(char *destp, char *destend, char *src)
Definition: imap_tools.c:215
static unsigned char utf7_rank[256]
Definition: imap_tools.c:179
#define WILDMAT_TRUE
Definition: imap_tools.c:722
void IPutStr(const char *Msg, long Len)
Definition: imap_tools.c:955
int imap_roomname(char *rbuf, int bufsize, const char *foldername)
Definition: imap_tools.c:601
#define QR_MAILBOX
Definition: ipcdef.h:53
void * malloc(size_t)
void free(void *)
#define IR_MAILBOX
Definition: serv_imap.h:98
#define IMAP
Definition: serv_imap.h:104
#define FDELIM
Definition: serv_imap.h:102
struct ctdlroom qrbuf
Definition: serv_migrate.c:497
ConstStr * Params
Definition: serv_imap.h:30
StrBuf * CmdBuf
Definition: serv_imap.h:29
citimap_command Cmd
Definition: serv_imap.h:51
StrBuf * Reply
Definition: serv_imap.h:38
char QRfloor
Definition: citadel.h:116
char QRname[128]
Definition: citadel.h:108
unsigned QRflags
Definition: citadel.h:113
Definition: citadel.h:145
char f_name[256]
Definition: citadel.h:147
unsigned short f_flags
Definition: citadel.h:146
char * buffer
Definition: imap_tools.c:44
int maxsize
Definition: imap_tools.c:45
int size
Definition: imap_tools.c:46
#define MAILROOM
Definition: sysconfig.h:61
#define SIZ
Definition: sysconfig.h:33
#define MAXFLOORS
Definition: sysconfig.h:25
void cputbuf(const StrBuf *Buf)
Definition: sysdep.c:374