Back to index

citadel  8.12
imap_tools.c
Go to the documentation of this file.
00001 /*
00002  * Utility functions for the IMAP module.
00003  *
00004  * Copyright (c) 2001-2009 by the citadel.org team and others, except for
00005  * most of the UTF7 and UTF8 handling code which was lifted from Evolution.
00006  *
00007  * This program is open source software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 3 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 #define SHOW_ME_VAPPEND_PRINTF
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <stdio.h>
00026 #include <ctype.h>
00027 #include <string.h>
00028 #include <stdarg.h>
00029 #include <libcitadel.h>
00030 #include "citadel.h"
00031 #include "sysdep_decls.h"
00032 #include "internet_addressing.h"
00033 #include "serv_imap.h"
00034 #include "imap_tools.h"
00035 #include "ctdl_module.h"
00036 
00037 #ifndef HAVE_SNPRINTF
00038 #include "snprintf.h"
00039 #endif
00040 
00041 /* String handling helpers */
00042 
00043 /* This code uses some pretty nasty string manipulation. To make everything
00044  * manageable, we use this semi-high-level string manipulation API. Strings are
00045  * always \0-terminated, despite the fact that we keep track of the size.
00046  */
00047 struct string {
00048        char* buffer;
00049        int maxsize;
00050        int size;
00051 };
00052 
00053 static void string_init(struct string* s, char* buf, int bufsize)
00054 {
00055        s->buffer = buf;
00056        s->maxsize = bufsize-1;
00057        s->size = strlen(buf);
00058 }
00059 
00060 static char* string_end(struct string* s)
00061 {
00062        return s->buffer + s->size;
00063 }
00064 
00065 /* Append a UTF8 string of a particular length (in bytes). -1 to autocalculate. */
00066 
00067 static void string_append_sn(struct string* s, char* p, int len)
00068 {
00069        if (len == -1)
00070               len = strlen(p);
00071        if ((s->size+len) > s->maxsize)
00072               len = s->maxsize - s->size;
00073        memcpy(s->buffer + s->size, p, len);
00074        s->size += len;
00075        s->buffer[s->size] = '\0';
00076 }
00077 
00078 /* As above, always autocalculate. */
00079 
00080 #define string_append_s(s, p) string_append_sn((s), (p), -1)
00081 
00082 /* Appends a UTF8 character --- which may make the size change by more than 1!
00083  * If the string overflows, the last character may become invalid. */
00084 
00085 static void string_append_c(struct string* s, int c)
00086 {
00087        char UmlChar[5];
00088        int len = 0;
00089 
00090        /* Don't do anything if there's no room. */
00091 
00092        if (s->size == s->maxsize)
00093               return;
00094 
00095        if (c <= 0x7F)
00096        {
00097               /* This is the most common case, so we optimise it. */
00098 
00099               s->buffer[s->size++] = c;
00100               s->buffer[s->size] = 0;
00101               return;
00102        }
00103        else if (c <= 0x7FF)
00104        {
00105               UmlChar[0] = 0xC0 | (c >> 6);
00106               UmlChar[1] = 0x80 | (c & 0x3F);
00107               len = 2;
00108        }
00109        else if (c <= 0xFFFF)
00110        {
00111               UmlChar[0] = 0xE0 | (c >> 12);
00112               UmlChar[1] = 0x80 | ((c >> 6) & 0x3f);
00113               UmlChar[2] = 0x80 | (c & 0x3f);
00114               len = 3;
00115        }
00116        else
00117        {
00118               UmlChar[0] = 0xf0 | c >> 18;
00119               UmlChar[1] = 0x80 | ((c >> 12) & 0x3f);
00120               UmlChar[2] = 0x80 | ((c >> 6) & 0x3f);
00121               UmlChar[3] = 0x80 | (c & 0x3f);
00122               len = 4;
00123        }
00124 
00125        string_append_sn(s, UmlChar, len);
00126 }      
00127 
00128 /* Reads a UTF8 character from a char*, advancing the pointer. */
00129 
00130 int utf8_getc(char** ptr)
00131 {
00132        unsigned char* p = (unsigned char*) *ptr;
00133        unsigned char c, r;
00134        int v, m;
00135 
00136        for (;;)
00137        {
00138               r = *p++;
00139        loop:
00140               if (r < 0x80)
00141               {
00142                      *ptr = (char*) p;
00143                      v = r;
00144                      break;
00145               }
00146               else if (r < 0xf8)
00147               {
00148                      /* valid start char? (max 4 octets) */
00149                      v = r;
00150                      m = 0x7f80;   /* used to mask out the length bits */
00151                      do {
00152                             c = *p++;
00153                             if ((c & 0xc0) != 0x80)
00154                             {
00155                                    r = c;
00156                                    goto loop;
00157                             }
00158                             v = (v<<6) | (c & 0x3f);
00159                             r<<=1;
00160                             m<<=5;
00161                      } while (r & 0x40);
00162                      
00163                      *ptr = (char*)p;
00164 
00165                      v &= ~m;
00166                      break;
00167               }
00168        }
00169 
00170        return v;
00171 }
00172 
00173 /* IMAP name safety */
00174 
00175 /* IMAP has certain special requirements in its character set, which means we
00176  * have to do a fair bit of work to convert Citadel's UTF8 strings to IMAP
00177  * strings. The next two routines (and their data tables) do that.
00178  */
00179 
00180 static char *utf7_alphabet =
00181        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
00182 
00183 static unsigned char utf7_rank[256] = {
00184        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00185        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00186        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0x3F,0xFF,0xFF,0xFF,
00187        0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00188        0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
00189        0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
00190        0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
00191        0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,
00192        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00193        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00194        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00195        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00196        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00197        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00198        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00199        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
00200 };
00201 
00202 /* Base64 helpers. */
00203 
00204 static void utf7_closeb64(struct string* out, int v, int i)
00205 {
00206        int x;
00207 
00208        if (i > 0)
00209        {
00210               x = (v << (6-i)) & 0x3F;
00211               string_append_c(out, utf7_alphabet[x]);
00212        }
00213        string_append_c(out, '-');
00214 }
00215 
00216 /* Convert from a Citadel name to an IMAP-safe name. Returns the end
00217  * of the destination.
00218  */
00219 static char* toimap(char* destp, char* destend, char* src)
00220 {
00221        struct string dest;
00222        int state = 0;
00223        int v = 0;
00224        int i = 0;
00225 
00226        *destp = 0;
00227        string_init(&dest, destp, destend-destp);
00228        /* IMAP_syslog(LOG_DEBUG, "toimap %s", src); */
00229 
00230        for (;;)
00231        {
00232               int c = utf8_getc(&src);
00233               if (c == '\0')
00234                      break;
00235 
00236               if (c >= 0x20 && c <= 0x7e)
00237               {
00238                      if (state == 1)
00239                      {
00240                             utf7_closeb64(&dest, v, i);
00241                             state = 0;
00242                             i = 0;
00243                      }
00244 
00245                      switch (c)
00246                      {
00247                             case '&':
00248                                    string_append_sn(&dest, "&-", 2);
00249                                    break;
00250 
00251                             case '/':
00252                                    /* Citadel extension: / becomes |, because /
00253                                     * isn't valid as part of an IMAP name. */
00254 
00255                                    c = '|';
00256                                    goto defaultcase;
00257 
00258                             case '\\':
00259                                    /* Citadel extension: backslashes mark folder
00260                                     * seperators in the IMAP subfolder emulation
00261                                     * hack. We turn them into / characters,
00262                                     * *except* if it's the last character in the
00263                                     * string. */
00264 
00265                                    if (*src != '\0')
00266                                           c = '/';
00267                                    /* fall through */
00268 
00269                             default:
00270                             defaultcase:
00271                                    string_append_c(&dest, c);
00272                      }
00273               }
00274               else
00275               {
00276                      if (state == 0)
00277                      {
00278                             string_append_c(&dest, '&');
00279                             state = 1;
00280                      }
00281                      v = (v << 16) | c;
00282                      i += 16;
00283                      while (i >= 6)
00284                      {
00285                             int x = (v >> (i-6)) & 0x3f;
00286                             string_append_c(&dest, utf7_alphabet[x]);
00287                             i -= 6;
00288                      }
00289               }
00290        }
00291 
00292        if (state == 1)
00293               utf7_closeb64(&dest, v, i);
00294        /* IMAP_syslog(LOG_DEBUG, "    -> %s", destp); */
00295        return string_end(&dest);
00296 }
00297 
00298 /* Convert from an IMAP-safe name back into a Citadel name. Returns the end of the destination. */
00299 
00300 static int cfrommap(int c);
00301 static char* fromimap(char* destp, char* destend, const char* src)
00302 {
00303        struct string dest;
00304        unsigned const char *p = (unsigned const char*) src;
00305        int v = 0;
00306        int i = 0;
00307        int state = 0;
00308        int c;
00309 
00310        *destp = 0;
00311        string_init(&dest, destp, destend-destp);
00312        /* IMAP_syslog(LOG_DEBUG, "fromimap %s", src); */
00313 
00314        do {
00315               c = *p++;
00316               switch (state)
00317               {
00318                      case 0:
00319                             /* US-ASCII characters. */
00320                             
00321                             if (c == '&')
00322                                    state = 1;
00323                             else
00324                                    string_append_c(&dest, cfrommap(c));
00325                             break;
00326 
00327                      case 1:
00328                             if (c == '-')
00329                             {
00330                                    string_append_c(&dest, '&');
00331                                    state = 0;
00332                             }
00333                             else if (utf7_rank[c] != 0xff)
00334                             {
00335                                    v = utf7_rank[c];
00336                                    i = 6;
00337                                    state = 2;
00338                             }
00339                             else
00340                             {
00341                                    /* invalid char */
00342                                    string_append_sn(&dest, "&-", 2);
00343                                    state = 0;
00344                             }
00345                             break;
00346                             
00347                      case 2:
00348                             if (c == '-')
00349                                    state = 0;
00350                             else if (utf7_rank[c] != 0xFF)
00351                             {
00352                                    v = (v<<6) | utf7_rank[c];
00353                                    i += 6;
00354                                    if (i >= 16)
00355                                    {
00356                                           int x = (v >> (i-16)) & 0xFFFF;
00357                                           string_append_c(&dest, cfrommap(x));
00358                                           i -= 16;
00359                                    }
00360                             }
00361                             else
00362                             {
00363                                    string_append_c(&dest, cfrommap(c));
00364                                    state = 0;
00365                             }
00366                             break;
00367                      }
00368        } while (c != '\0');
00369 
00370        /* IMAP_syslog(LOG_DEBUG, "      -> %s", destp); */
00371        return string_end(&dest);
00372 }
00373 
00374 /* Undoes the special character conversion. */
00375 
00376 static int cfrommap(int c)
00377 {
00378        switch (c)
00379        {
00380               case '|':     return '/';
00381               case '/':     return '\\';
00382        }
00383        return c;            
00384 }
00385 
00386 
00387 
00388 
00389 /* Break a command down into tokens, unquoting any escaped characters. */
00390 
00391 void MakeStringOf(StrBuf *Buf, int skip)
00392 {
00393        int i;
00394        citimap_command *Cmd = &IMAP->Cmd;
00395 
00396        for (i=skip; i<Cmd->num_parms; ++i) {
00397               StrBufAppendBufPlain(Buf, Cmd->Params[i].Key, Cmd->Params[i].len, 0);
00398               if (i < (Cmd->num_parms-1)) StrBufAppendBufPlain(Buf, HKEY(" "), 0);
00399        }
00400 }
00401 
00402 
00403 void TokenCutRight(citimap_command *Cmd, 
00404                  ConstStr *CutMe,
00405                  int n)
00406 {
00407        const char *CutAt;
00408 
00409        if (CutMe->len < n) {
00410               CutAt = CutMe->Key;
00411               CutMe->len = 0;
00412        }
00413        else {
00414               CutAt = CutMe->Key + CutMe->len - n;
00415               CutMe->len -= n;
00416        }
00417        StrBufPeek(Cmd->CmdBuf, CutAt, -1, '\0');
00418 }
00419 
00420 void TokenCutLeft(citimap_command *Cmd, 
00421                 ConstStr *CutMe,
00422                 int n)
00423 {
00424        if (CutMe->len < n) {
00425               CutMe->Key += CutMe->len;
00426               CutMe->len = 0;
00427        }
00428        else {
00429               CutMe->Key += n;
00430               CutMe->len -= n;
00431        }
00432 }
00433 
00434 
00435 
00436 int CmdAdjust(citimap_command *Cmd, 
00437              int nArgs,
00438              int Realloc)
00439 {
00440        ConstStr *Params;
00441        if (nArgs > Cmd->avail_parms) {
00442               Params = (ConstStr*) malloc(sizeof(ConstStr) * nArgs);
00443               if (Realloc) {
00444                      memcpy(Params, 
00445                             Cmd->Params, 
00446                             sizeof(ConstStr) * Cmd->avail_parms);
00447 
00448                      memset(Cmd->Params + 
00449                             sizeof(ConstStr) * Cmd->avail_parms,
00450                             0, 
00451                             sizeof(ConstStr) * nArgs - 
00452                             sizeof(ConstStr) * Cmd->avail_parms 
00453                             );
00454               }
00455               else {
00456                      Cmd->num_parms = 0;
00457                      memset(Params, 0, 
00458                             sizeof(ConstStr) * nArgs);
00459               }
00460               Cmd->avail_parms = nArgs;
00461               if (Cmd->Params != NULL)
00462                      free (Cmd->Params);
00463               Cmd->Params = Params;
00464        }
00465        else {
00466               if (!Realloc) {
00467                      memset(Cmd->Params, 
00468                             0,
00469                             sizeof(ConstStr) * Cmd->avail_parms);
00470                      Cmd->num_parms = 0;
00471               }
00472        }
00473        return Cmd->avail_parms;
00474 }
00475 
00476 int imap_parameterize(citimap_command *Cmd)
00477 {
00478        int nArgs;
00479        const char *In, *End;
00480 
00481        In = ChrPtr(Cmd->CmdBuf);
00482        End = In + StrLength(Cmd->CmdBuf);
00483 
00484        /* we start with 10 chars per arg, maybe we need to realloc later. */
00485        nArgs = StrLength(Cmd->CmdBuf) / 10 + 10;
00486        nArgs = CmdAdjust(Cmd, nArgs, 0);
00487        while (In < End)
00488        {
00489               /* Skip whitespace. */
00490               while (isspace(*In))
00491                      In++;
00492               if (*In == '\0')
00493                      break;
00494 
00495               /* Found the start of a token. */
00496               
00497               Cmd->Params[Cmd->num_parms].Key = In;
00498 
00499               /* Read in the token. */
00500 
00501               for (;;)
00502               {
00503                      if (isspace(*In))
00504                             break;
00505                      
00506                      if (*In == '\"')
00507                      {
00508                             /* Found a quoted section. */
00509 
00510                             Cmd->Params[Cmd->num_parms].Key++; 
00511                             //In++;
00512                             for (;;)
00513                             {
00514                                    In++;
00515                                    if (*In == '\"') {
00516                                           StrBufPeek(Cmd->CmdBuf, In, -1, '\0');
00517                                           break;
00518                                    }
00519                                    else if (*In == '\\')
00520                                           In++;
00521 
00522                                    if (*In == '\0') {
00523                                           Cmd->Params[Cmd->num_parms].len = 
00524                                                  In - Cmd->Params[Cmd->num_parms].Key;
00525                                           Cmd->num_parms++;
00526                                           return Cmd->num_parms;
00527                                    }
00528                             }
00529                             break;
00530                      }
00531                      else if (*In == '\\')
00532                      {
00533                             In++;
00534                      }
00535 
00536                      if (*In == '\0') {
00537                             Cmd->Params[Cmd->num_parms].len = 
00538                                    In - Cmd->Params[Cmd->num_parms].Key;
00539                             Cmd->num_parms++;
00540                             return Cmd->num_parms;
00541                      }
00542                      In++;
00543               }
00544               StrBufPeek(Cmd->CmdBuf, In, -1, '\0');
00545               Cmd->Params[Cmd->num_parms].len = 
00546                      In - Cmd->Params[Cmd->num_parms].Key;
00547               if (Cmd->num_parms + 1 >= Cmd->avail_parms) {
00548                      nArgs = CmdAdjust(Cmd, nArgs * 2, 1);
00549               }
00550               Cmd->num_parms ++;
00551               In++;
00552        }
00553        return Cmd->num_parms;
00554 }
00555 
00556 
00557 /* Convert a struct ctdlroom to an IMAP-compatible mailbox name. */
00558 
00559 void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf)
00560 {
00561        char* bufend = buf+bufsize;
00562        struct floor *fl;
00563        char* p = buf;
00564 
00565        /* For mailboxes, just do it straight.
00566         * Do the Cyrus-compatible thing: all private folders are
00567         * subfolders of INBOX. */
00568 
00569        if (qrbuf->QRflags & QR_MAILBOX)
00570        {
00571               if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0)
00572                      toimap(p, bufend, "INBOX");
00573               else
00574               {
00575                      p = toimap(p, bufend, "INBOX");
00576                      if (p < bufend)
00577                             *p++ = '/';
00578                      toimap(p, bufend, qrbuf->QRname+11);
00579               }
00580        }
00581        else
00582        {
00583               /* Otherwise, prefix the floor name as a "public folders" moniker. */
00584 
00585               fl = CtdlGetCachedFloor(qrbuf->QRfloor);
00586               p = toimap(p, bufend, fl->f_name);
00587               if (p < bufend)
00588                      *p++ = '/';
00589               toimap(p, bufend, qrbuf->QRname);
00590        }
00591 }
00592 
00593 /*
00594  * Convert an inputted folder name to our best guess as to what an equivalent
00595  * room name should be.
00596  *
00597  * If an error occurs, it returns -1.  Otherwise...
00598  *
00599  * The lower eight bits of the return value are the floor number on which the
00600  * room most likely resides.   The upper eight bits may contain flags,
00601  * including IR_MAILBOX if we're dealing with a personal room.
00602  *
00603  */
00604 
00605 int imap_roomname(char *rbuf, int bufsize, const char *foldername)
00606 {
00607        struct CitContext *CCC = CC;
00608        int levels;
00609        char floorname[ROOMNAMELEN*2];
00610        char roomname[ROOMNAMELEN];
00611        int i;
00612        struct floor *fl;
00613        int ret = (-1);
00614 
00615        if (foldername == NULL)
00616               return(-1);
00617 
00618        /* Unmunge the entire string into the output buffer. */
00619 
00620        fromimap(rbuf, rbuf+bufsize, foldername);
00621 
00622        /* Is this an IMAP inbox? */
00623 
00624        if (strncasecmp(rbuf, "INBOX", 5) == 0)
00625        {
00626               if (rbuf[5] == 0)
00627               {
00628                      /* It's the system inbox. */
00629 
00630                      safestrncpy(rbuf, MAILROOM, bufsize);
00631                      ret = (0 | IR_MAILBOX);
00632                      goto exit;
00633               }
00634               else if (rbuf[5] == FDELIM)
00635               {
00636                      /* It's another personal mail folder. */
00637 
00638                      safestrncpy(rbuf, rbuf+6, bufsize);
00639                      ret = (0 | IR_MAILBOX);
00640                      goto exit;
00641               }
00642 
00643               /* If we get here, the folder just happens to start with INBOX
00644                * --- fall through. */
00645        }
00646 
00647        /* Is this a multi-level room name? */
00648 
00649        levels = num_tokens(rbuf, FDELIM);
00650        if (levels > 1)
00651        {
00652               long len;
00653               /* Extract the main room name. */
00654               
00655               len = extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname);
00656               if (len < 0) len = 0;
00657               safestrncpy(roomname, &rbuf[len  + 1], sizeof(roomname));
00658 
00659               /* Try and find it on any floor. */
00660               
00661               for (i = 0; i < MAXFLOORS; ++i)
00662               {
00663                      fl = CtdlGetCachedFloor(i);
00664                      if (fl->f_flags & F_INUSE)
00665                      {
00666                             if (strcasecmp(floorname, fl->f_name) == 0)
00667                             {
00668                                    /* Got it! */
00669 
00670                                    safestrncpy(rbuf, roomname, bufsize);
00671                                    ret = i;
00672                                    goto exit;
00673                             }
00674                      }
00675               }
00676        }
00677 
00678        /* Meh. It's either not a multi-level room name, or else we
00679         * couldn't find it.
00680         */
00681        ret = (0 | IR_MAILBOX);
00682 
00683 exit:
00684        IMAP_syslog(LOG_DEBUG, "(That translates to \"%s\")", rbuf);
00685        return(ret);
00686 }
00687 
00688 /*
00689  * Output a struct internet_address_list in the form an IMAP client wants
00690  */
00691 void imap_ial_out(struct internet_address_list *ialist)
00692 {
00693        struct internet_address_list *iptr;
00694 
00695        if (ialist == NULL) {
00696               IAPuts("NIL");
00697               return;
00698        }
00699        IAPuts("(");
00700 
00701        for (iptr = ialist; iptr != NULL; iptr = iptr->next) {
00702               IAPuts("(");
00703               plain_imap_strout(iptr->ial_name);
00704               IAPuts(" NIL ");
00705               plain_imap_strout(iptr->ial_user);
00706               IAPuts(" ");
00707               plain_imap_strout(iptr->ial_node);
00708               IAPuts(")");
00709        }
00710 
00711        IAPuts(")");
00712 }
00713 
00714 
00715 
00716 /*
00717  * Determine whether the supplied string is a valid message set.
00718  * If the string contains only numbers, colons, commas, and asterisks,
00719  * return 1 for a valid message set.  If any other character is found, 
00720  * return 0.
00721  */
00722 int imap_is_message_set(const char *buf)
00723 {
00724        int i;
00725 
00726        if (buf == NULL)
00727               return (0);   /* stupidity checks */
00728        if (IsEmptyStr(buf))
00729               return (0);
00730 
00731        if (!strcasecmp(buf, "ALL"))
00732               return (1);   /* macro?  why?  */
00733 
00734        for (i = 0; buf[i]; ++i) {  /* now start the scan */
00735               if (
00736                         (!isdigit(buf[i]))
00737                         && (buf[i] != ':')
00738                         && (buf[i] != ',')
00739                         && (buf[i] != '*')
00740                   )
00741                      return (0);
00742        }
00743 
00744        return (1);          /* looks like we're good */
00745 }
00746 
00747 
00748 /*
00749  * imap_match.c, based on wildmat.c from INN
00750  * hacked for Citadel/IMAP by Daniel Malament
00751  */
00752 
00753 /* note: not all return statements use these; don't change them */
00754 #define WILDMAT_TRUE 1
00755 #define WILDMAT_FALSE       0
00756 #define WILDMAT_ABORT       -1
00757 #define WILDMAT_DELIM       '/'
00758 
00759 /*
00760  * Match text and p, return TRUE, FALSE, or ABORT.
00761  */
00762 static int do_imap_match(const char *supplied_text, const char *supplied_p)
00763 {
00764        int matched, i;
00765        char lcase_text[SIZ], lcase_p[SIZ];
00766        char *text;
00767        char *p;
00768        
00769        /* Copy both strings and lowercase them, in order to
00770         * make this entire operation case-insensitive.
00771         */
00772        for (i=0; 
00773             ((supplied_text[i] != '\0') && 
00774              (i < sizeof(lcase_text)));
00775             ++i)
00776               lcase_text[i] = tolower(supplied_text[i]);
00777        lcase_text[i] = '\0';
00778 
00779        for (i=0; 
00780             ((supplied_p[i] != '\0') && 
00781              (i < sizeof(lcase_p))); 
00782             ++i)
00783               lcase_p[i] = tolower(supplied_p[i]);
00784        lcase_p[i] = '\0';
00785 
00786        /* Start matching */
00787        for (p = lcase_p, text = lcase_text; 
00788             !IsEmptyStr(p) && !IsEmptyStr(text); 
00789             text++, p++) {
00790               if ((*text == '\0') && (*p != '*') && (*p != '%')) {
00791                      return WILDMAT_ABORT;
00792               }
00793               switch (*p) {
00794               default:
00795                      if (*text != *p) {
00796                             return WILDMAT_FALSE;
00797                      }
00798                      continue;
00799               case '*':
00800 star:
00801                      while (++p, ((*p == '*') || (*p == '%'))) {
00802                             /* Consecutive stars or %'s act
00803                              * just like one star.
00804                              */
00805                             continue;
00806                      }
00807                      if (*p == '\0') {
00808                             /* Trailing star matches everything. */
00809                             return WILDMAT_TRUE;
00810                      }
00811                      while (*text) {
00812                             if ((matched = do_imap_match(text++, p))
00813                                != WILDMAT_FALSE) {
00814                                    return matched;
00815                             }
00816                      }
00817                      return WILDMAT_ABORT;
00818               case '%':
00819                      while (++p, (!IsEmptyStr(p) && ((*p == '*') || (*p == '%'))))
00820                      {
00821                             /* Consecutive %'s act just like one, but even
00822                              * a single star makes the sequence act like
00823                              * one star, instead.
00824                              */
00825                             if (*p == '*') {
00826                                    goto star;
00827                             }
00828                             continue;
00829                      }
00830                      if (*p == '\0') {
00831                             /*
00832                              * Trailing % matches everything
00833                              * without a delimiter.
00834                              */
00835                             while (!IsEmptyStr(text)) {
00836                                    if (*text == WILDMAT_DELIM) {
00837                                           return WILDMAT_FALSE;
00838                                    }
00839                                    text++;
00840                             }
00841                             return WILDMAT_TRUE;
00842                      }
00843                      while (!IsEmptyStr(text) &&
00844                             /* make shure texst - 1 isn't before lcase_p */
00845                             ((text == lcase_text) || (*(text - 1) != WILDMAT_DELIM)))
00846                      {
00847                             if ((matched = do_imap_match(text++, p))
00848                                != WILDMAT_FALSE) {
00849                                    return matched;
00850                             }
00851                      }
00852                      return WILDMAT_ABORT;
00853               }
00854        }
00855 
00856        if ((*text == '\0') && (*p == '\0')) return WILDMAT_TRUE;
00857        else return WILDMAT_FALSE;
00858 }
00859 
00860 
00861 
00862 /*
00863  * Support function for mailbox pattern name matching in LIST and LSUB
00864  * Returns nonzero if the supplied mailbox name matches the supplied pattern.
00865  */
00866 int imap_mailbox_matches_pattern(const char *pattern, char *mailboxname)
00867 {
00868        /* handle just-star case quickly */
00869        if ((pattern[0] == '*') && (pattern[1] == '\0')) {
00870               return WILDMAT_TRUE;
00871        }
00872        return (do_imap_match(mailboxname, pattern) == WILDMAT_TRUE);
00873 }
00874 
00875 
00876 
00877 /*
00878  * Compare an IMAP date string (date only, no time) to the date found in
00879  * a Unix timestamp.
00880  */
00881 int imap_datecmp(const char *datestr, time_t msgtime) {
00882        char daystr[256];
00883        char monthstr[256];
00884        char yearstr[256];
00885        int i;
00886        int day, month, year;
00887        int msgday, msgmonth, msgyear;
00888        struct tm msgtm;
00889 
00890        char *imap_datecmp_ascmonths[12] = {
00891               "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00892        };
00893 
00894        if (datestr == NULL) return(0);
00895 
00896        /* Expecting a date in the form dd-Mmm-yyyy */
00897        extract_token(daystr, datestr, 0, '-', sizeof daystr);
00898        extract_token(monthstr, datestr, 1, '-', sizeof monthstr);
00899        extract_token(yearstr, datestr, 2, '-', sizeof yearstr);
00900 
00901        day = atoi(daystr);
00902        year = atoi(yearstr);
00903        month = 0;
00904        for (i=0; i<12; ++i) {
00905               if (!strcasecmp(monthstr, imap_datecmp_ascmonths[i])) {
00906                      month = i;
00907               }
00908        }
00909 
00910        /* Extract day/month/year from message timestamp */
00911        localtime_r(&msgtime, &msgtm);
00912        msgday = msgtm.tm_mday;
00913        msgmonth = msgtm.tm_mon;
00914        msgyear = msgtm.tm_year + 1900;
00915 
00916        /* Now start comparing */
00917 
00918        if (year < msgyear) return(+1);
00919        if (year > msgyear) return(-1);
00920 
00921        if (month < msgmonth) return(+1);
00922        if (month > msgmonth) return(-1);
00923 
00924        if (day < msgday) return(+1);
00925        if (day > msgday) return(-1);
00926 
00927        return(0);
00928 }
00929 
00930 
00931 
00932 
00933 
00934 void IAPrintf(const char *Format, ...)
00935 {
00936        va_list arg_ptr;
00937        
00938        va_start(arg_ptr, Format);
00939        StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr);
00940        va_end(arg_ptr);
00941 }
00942 
00943 void iaputs(const char *Str, long Len)
00944 {
00945        StrBufAppendBufPlain(IMAP->Reply, Str, Len, 0);
00946 }
00947 
00948 void ireply(const char *Msg, long len)
00949 {
00950        citimap *Imap = IMAP;
00951 
00952        StrBufAppendBufPlain(Imap->Reply, 
00953                           CKEY(Imap->Cmd.Params[0]), 0);
00954        StrBufAppendBufPlain(Imap->Reply, 
00955                           HKEY(" "), 0);
00956        StrBufAppendBufPlain(Imap->Reply, 
00957                           Msg, len, 0);
00958        
00959        StrBufAppendBufPlain(Imap->Reply, 
00960                           HKEY("\r\n"), 0);
00961        
00962 }
00963 
00964 void IReplyPrintf(const char *Format, ...)
00965 {
00966        citimap *Imap = IMAP;
00967        va_list arg_ptr;
00968        
00969 
00970        StrBufAppendBufPlain(Imap->Reply, 
00971                           CKEY(Imap->Cmd.Params[0]), 0);
00972 
00973        StrBufAppendBufPlain(Imap->Reply, 
00974                           HKEY(" "), 0);
00975 
00976        va_start(arg_ptr, Format);
00977        StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr);
00978        va_end(arg_ptr);
00979        
00980        StrBufAppendBufPlain(Imap->Reply, 
00981                           HKEY("\r\n"), 0);
00982        
00983 }
00984 
00985 
00986 
00987 /* Output a string to the IMAP client, either as a literal or quoted.
00988  * (We do a literal if it has any double-quotes or backslashes.) */
00989 
00990 void plain_imap_strout(char *buf)
00991 {
00992        int i;
00993        int is_literal = 0;
00994        long Len;
00995        citimap *Imap = IMAP;
00996 
00997        if (buf == NULL) {   /* yeah, we handle this */
00998               IAPuts("NIL");
00999               return;
01000        }
01001 
01002        Len = strlen(buf);
01003        for (i = 0; i < Len; ++i) {
01004               if ((buf[i] == '\"') || (buf[i] == '\\'))
01005                      is_literal = 1;
01006        }
01007 
01008        if (is_literal) {
01009               StrBufAppendPrintf(Imap->Reply, "{%ld}\r\n", Len);
01010               StrBufAppendBufPlain(Imap->Reply, buf, Len, 0);
01011        } else {
01012               StrBufAppendBufPlain(Imap->Reply, 
01013                                  HKEY("\""), 0);
01014               StrBufAppendBufPlain(Imap->Reply, 
01015                                  buf, Len, 0);
01016               StrBufAppendBufPlain(Imap->Reply, 
01017                                  HKEY("\""), 0);
01018        }
01019 }
01020 
01021 
01022 /* Output a string to the IMAP client, either as a literal or quoted.
01023  * (We do a literal if it has any double-quotes or backslashes.) */
01024 
01025 
01026 void IPutStr(const char *Msg, long Len)
01027 {
01028        int i;
01029        int is_literal = 0;
01030        citimap *Imap = IMAP;
01031 
01032        
01033        if ((Msg == NULL) || (Len == 0))
01034        {      /* yeah, we handle this */
01035               StrBufAppendBufPlain(Imap->Reply, HKEY("NIL"), 0);
01036               return;
01037        }
01038 
01039        for (i = 0; i < Len; ++i) {
01040               if ((Msg[i] == '\"') || (Msg[i] == '\\'))
01041                      is_literal = 1;
01042        }
01043 
01044        if (is_literal) {
01045               StrBufAppendPrintf(Imap->Reply, "{%ld}\r\n", Len);
01046               StrBufAppendBufPlain(Imap->Reply, Msg, Len, 0);
01047        } else {
01048               StrBufAppendBufPlain(Imap->Reply, 
01049                                  HKEY("\""), 0);
01050               StrBufAppendBufPlain(Imap->Reply, 
01051                                  Msg, Len, 0);
01052               StrBufAppendBufPlain(Imap->Reply, 
01053                                  HKEY("\""), 0);
01054        }
01055 }
01056 
01057 void IUnbuffer (void)
01058 {
01059        citimap *Imap = IMAP;
01060 
01061        cputbuf(Imap->Reply);
01062        FlushStrBuf(Imap->Reply);
01063 }