Back to index

courier  0.68.2
imaptoken.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2005 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 
00010 #include      "imaptoken.h"
00011 #include      "imapwrite.h"
00012 #include      <stdio.h>
00013 #include      <ctype.h>
00014 #include      <stdlib.h>
00015 #include      <string.h>
00016 #include      <sys/types.h>
00017 #include      <sys/time.h>
00018 #include      "numlib/numlib.h"
00019 #if    HAVE_UNISTD_H
00020 #include      <unistd.h>
00021 #endif
00022 
00023 #ifndef       BUFSIZ
00024 #define       BUFSIZ 8192
00025 #endif
00026 
00027 
00028 static struct imaptoken curtoken;
00029 static char readbuf[BUFSIZ];
00030 
00031 char *imap_readptr=0;
00032 size_t imap_readptrleft=0;
00033 time_t start_time;
00034 
00035 static time_t readtimeout;
00036 
00037 extern FILE *debugfile;
00038 
00039 extern unsigned long header_count, body_count;
00040 
00041 unsigned long bytes_received_count = 0; /* counter for received bytes */
00042 unsigned long bytes_sent_count; /* counter for sent bytes (imapwrite.c) */
00043 
00044 extern void bye();
00045 
00046 void bye_msg(const char *type)
00047 {
00048        const char *a=getenv("AUTHENTICATED");
00049        char buf[NUMBUFSIZE];
00050        const char *tls=getenv("IMAP_TLS");
00051 
00052        libmail_str_time_t(time(NULL)-start_time, buf);
00053 
00054        if (tls && atoi(tls))
00055               tls=", starttls=1";
00056        else
00057               tls="";
00058 
00059        if (a && *a)
00060               fprintf(stderr, "%s, user=%s, "
00061                      "ip=[%s], headers=%lu, body=%lu, rcvd=%lu, sent=%lu, time=%s%s\n",
00062                      type,
00063                      a, getenv("TCPREMOTEIP"), header_count, body_count, bytes_received_count, bytes_sent_count,
00064                      buf, tls);
00065        else
00066               fprintf(stderr, "DEBUG: Disconnected, ip=[%s], time=%s%s\n",
00067                      getenv("TCPREMOTEIP"),
00068                      buf, tls);
00069 }
00070 
00071 void disconnected()
00072 {
00073        bye_msg("INFO: DISCONNECTED");
00074        bye();
00075 }
00076 
00077 static void disconnected_timeout(void)
00078 {
00079        writes("* BYE Disconnected for inactivity.\r\n");
00080        writeflush();
00081        bye_msg("INFO: TIMEOUT");
00082        bye();
00083 }
00084 
00085 int doidle(time_t idletimeout, int extraFd)
00086 {
00087 fd_set fds;
00088 struct timeval tv;
00089 time_t t;
00090 
00091        time(&t);
00092        if (t >= readtimeout)   disconnected_timeout();
00093        if (imap_readptrleft > 0) return 1;
00094 
00095        FD_ZERO(&fds);
00096        FD_SET(0, &fds);
00097 
00098        if (extraFd > 0)
00099        {
00100               FD_SET(extraFd, &fds);
00101        }
00102        else
00103        {
00104               extraFd=0;
00105        }
00106 
00107        tv.tv_sec=idletimeout;
00108        tv.tv_usec=0;
00109 
00110        select(extraFd + 1, &fds, 0, 0, &tv);
00111        return (FD_ISSET(0, &fds));
00112 }
00113 
00114 size_t doread(char *buf, size_t bufsiz)
00115 {
00116 fd_set fds;
00117 struct timeval       tv;
00118 time_t t;
00119 int n = 0;
00120 
00121        time(&t);
00122        if (t >= readtimeout)       disconnected_timeout();
00123 
00124        FD_ZERO(&fds);
00125        FD_SET(0, &fds);
00126        tv.tv_sec=readtimeout - t;
00127        tv.tv_usec=0;
00128 
00129        if (select(1, &fds, 0, 0, &tv) <= 0)
00130        {
00131               disconnected_timeout();
00132               return (0);
00133        }
00134        if (!FD_ISSET(0, &fds) || (n=read(0, buf, bufsiz)) <= 0)
00135        {
00136               if ( n > 0 )
00137                      bytes_received_count += n; /* count received bytes */
00138               disconnected();
00139               return (0);
00140        }
00141        if ( n > 0 )
00142               bytes_received_count += n; /* count received bytes */
00143        return (n);
00144 }
00145 
00146 void readfill()
00147 {
00148        imap_readptrleft=doread(readbuf, sizeof(readbuf));
00149        imap_readptr=readbuf;
00150 }
00151 
00152 #define       UNREAD(c) (*--imap_readptr=(c), ++imap_readptrleft)
00153 
00154 void unread(int c)
00155 {
00156        UNREAD(c);
00157 }
00158 
00159 void read_eol()
00160 {
00161 int    c;
00162 
00163        while ( (c=READ()) != '\n')
00164               ;
00165        curtoken.tokentype=IT_EOL;
00166 }
00167 
00168 void read_timeout(time_t t)
00169 {
00170 time_t tt;
00171 
00172        time(&tt);
00173        readtimeout=tt+t;
00174 }
00175 
00176 static void alloc_tokenbuf(unsigned l)
00177 {
00178        if (l >= curtoken.tokenbuf_size)
00179        {
00180        char   *p=curtoken.tokenbuf ? realloc(curtoken.tokenbuf, l + 256):
00181                      malloc(l + 256);
00182 
00183               if (!p)
00184                      write_error_exit("malloc");
00185 
00186               curtoken.tokenbuf_size = l+256;
00187               curtoken.tokenbuf=p;
00188        }
00189 }
00190 
00191 static char LPAREN_CHAR='(';
00192 static char RPAREN_CHAR=')';
00193 static char LBRACKET_CHAR='[';
00194 static char RBRACKET_CHAR=']';
00195 
00196 void ignorepunct()
00197 {
00198        LPAREN_CHAR=RPAREN_CHAR=LBRACKET_CHAR=RBRACKET_CHAR='\n';
00199 }
00200 
00201 #if SMAP
00202 
00203 void smap_readline(char *buffer, size_t bufsize)
00204 {
00205        int c;
00206 
00207        while ((c=READ()) != '\n')
00208        {
00209               if (bufsize > 1)
00210               {
00211                      *buffer++ = c;
00212                      --bufsize;
00213               }
00214        }
00215        *buffer=0;
00216 }
00217 
00218 #endif
00219 
00220 static struct imaptoken *do_readtoken(int touc)
00221 {
00222 int    c=0;
00223 unsigned l;
00224 
00225 #define       appendch(c)   alloc_tokenbuf(l+1); curtoken.tokenbuf[l++]=(c);
00226 
00227        if (curtoken.tokentype == IT_ERROR)       return (&curtoken);
00228 
00229        do
00230        {
00231               c=READ();
00232        } while (c == '\r' || c == ' ' || c == '\t');
00233 
00234        if (c == '\n')
00235        {
00236               UNREAD(c);
00237               curtoken.tokentype=IT_EOL;
00238               return (&curtoken);
00239        }
00240        c=(unsigned char)c;
00241        if (c == LPAREN_CHAR)
00242        {
00243               curtoken.tokentype=IT_LPAREN;
00244               return (&curtoken);
00245        }
00246 
00247        if (c == RPAREN_CHAR)
00248        {
00249               curtoken.tokentype=IT_RPAREN;
00250               return (&curtoken);
00251        }
00252 
00253        if (c == LBRACKET_CHAR)
00254        {
00255               curtoken.tokentype=IT_LBRACKET;
00256               return (&curtoken);
00257        }
00258 
00259        if (c == RBRACKET_CHAR)
00260        {
00261               curtoken.tokentype=IT_RBRACKET;
00262               return (&curtoken);
00263        }
00264 
00265        if (c == '"')
00266        {
00267               l=0;
00268               while ((c=READ()) != '"')
00269               {
00270                      if (c == '\\')
00271                             c=READ();
00272                      if (c == '\r' || c == '\n')
00273                      {
00274                             UNREAD(c);
00275                             curtoken.tokentype=IT_ERROR;
00276                             return (&curtoken);
00277                      }
00278                      if (l < 8192)
00279                      {
00280                             appendch(c);
00281                      }
00282               }
00283               appendch(0);
00284               curtoken.tokentype=IT_QUOTED_STRING;
00285               return (&curtoken);
00286        }
00287 
00288        if (c == '{')
00289        {
00290               curtoken.tokennum=0;
00291               while ((c=READ()) != '}')
00292               {
00293                      if (!isdigit((int)(unsigned char)c))
00294                      {
00295                             UNREAD(c);
00296                             curtoken.tokentype=IT_ERROR;
00297                             return (&curtoken);
00298                      }
00299                      curtoken.tokennum = curtoken.tokennum*10 + (c-'0');
00300               }
00301               c=READ();
00302               if (c == '\r')
00303               {
00304                      c=READ();
00305               }
00306               if (c != '\n')
00307               {
00308                      curtoken.tokentype=IT_ERROR;
00309                      return (&curtoken);
00310               }
00311               curtoken.tokentype=IT_LITERAL_STRING_START;
00312               return (&curtoken);
00313        }
00314 
00315        l=0;
00316        if (c == '\\')
00317        {
00318               appendch(c);  /* Message flag */
00319               c=READ();
00320        }
00321        else if (isdigit(c))
00322        {
00323               curtoken.tokentype=IT_NUMBER;
00324               curtoken.tokennum=0;
00325               do
00326               {
00327                      appendch(c);
00328                      curtoken.tokennum = curtoken.tokennum*10 +
00329                             (c-'0');
00330                      c=READ();
00331               } while (isdigit( (int)(unsigned char)c));
00332 
00333               /* Could be stuff like mime.spec, so continue reading. */
00334        }
00335 
00336        while (c != '\r' && c != '\n'
00337               && !isspace((int)(unsigned char)c)
00338               && c != '\\' && c != '"' && c != LPAREN_CHAR && c != RPAREN_CHAR
00339               && c != '{' && c != '}' && c != LBRACKET_CHAR && c != RBRACKET_CHAR)
00340        {
00341               curtoken.tokentype=IT_ATOM;
00342               if (l < IT_MAX_ATOM_SIZE)
00343               {
00344                      if (touc)
00345                             c=toupper(c);
00346                      appendch(c);
00347               }
00348               else
00349               {
00350                      write_error_exit("max atom size too small");  
00351               }
00352               c=READ();
00353        }
00354        if (l == 0)
00355        {
00356               curtoken.tokentype=IT_ERROR;
00357               return (&curtoken);
00358        }
00359        appendch(0);
00360        UNREAD(c);
00361 
00362        if (strcmp(curtoken.tokenbuf, "NIL") == 0)
00363               curtoken.tokentype=IT_NIL;
00364        return (&curtoken);
00365 }
00366 
00367 static struct imaptoken *readtoken(int touc)
00368 {
00369 struct imaptoken *tok=do_readtoken(touc);
00370 
00371        if (tok->tokentype == IT_LITERAL_STRING_START)
00372        {
00373        unsigned long nbytes=curtoken.tokennum;
00374 
00375               if (nbytes > 8192)
00376               {
00377                      writes("* NO [ALERT] IMAP command too long.\r\n");
00378                      tok->tokentype=IT_ERROR;
00379               }
00380               else
00381               {
00382               unsigned long i;
00383 
00384                      writes("+ OK\r\n");
00385                      writeflush();
00386                      alloc_tokenbuf(nbytes+1);
00387                      for (i=0; i<nbytes; i++)
00388                             tok->tokenbuf[i]= READ();
00389                      tok->tokenbuf[i]=0;
00390                      tok->tokentype=IT_QUOTED_STRING;
00391               }
00392        }
00393 
00394        if (debugfile)
00395        {
00396        char   *p=0;
00397 
00398               fprintf(debugfile, "READ: ");
00399               switch (tok->tokentype) {
00400               case IT_ATOM:
00401                      p=curtoken.tokenbuf; fprintf(debugfile, "ATOM"); break;
00402               case IT_NUMBER:
00403                      p=curtoken.tokenbuf; fprintf(debugfile, "NUMBER"); break;
00404               case IT_QUOTED_STRING:
00405                      p=curtoken.tokenbuf; fprintf(debugfile, "QUOTED_STRING"); break;
00406               case IT_LPAREN:
00407                      fprintf(debugfile, "LPAREN"); break;
00408               case IT_RPAREN:
00409                      fprintf(debugfile, "RPAREN"); break;
00410               case IT_NIL:
00411                      fprintf(debugfile, "NIL"); break;
00412               case IT_ERROR:
00413                      fprintf(debugfile, "ERROR"); break;
00414               case IT_EOL:
00415                      fprintf(debugfile, "EOL"); break;
00416               case IT_LBRACKET:
00417                      fprintf(debugfile, "LBRACKET"); break;
00418               case IT_RBRACKET:
00419                      fprintf(debugfile, "RBRACKET"); break;
00420               }
00421 
00422               if (p)
00423                      fprintf(debugfile, ": %s", p);
00424               fprintf(debugfile, "\n");
00425               fflush(debugfile);
00426        }
00427        return (tok);
00428 }
00429 
00430 struct imaptoken *nexttoken(void)
00431 {
00432        return (readtoken(1));
00433 }
00434 
00435 struct imaptoken *nexttoken_nouc(void)
00436 {
00437        return (readtoken(0));
00438 }
00439 
00440 /* RFC 2060 sucks */
00441 
00442 struct imaptoken *nexttoken_okbracket(void)
00443 {
00444        struct imaptoken *t;
00445 
00446        LBRACKET_CHAR=RBRACKET_CHAR='\n';
00447 
00448        t=nexttoken();
00449 
00450        LBRACKET_CHAR='[';
00451        RBRACKET_CHAR=']';
00452        return (t);
00453 }
00454 
00455 struct imaptoken *nexttoken_nouc_okbracket(void)
00456 {
00457        struct imaptoken *t;
00458 
00459        LBRACKET_CHAR=RBRACKET_CHAR='\n';
00460 
00461        t=nexttoken_nouc();
00462 
00463        LBRACKET_CHAR='[';
00464        RBRACKET_CHAR=']';
00465        return (t);
00466 }
00467 
00468 struct imaptoken *currenttoken(void)
00469 {
00470        return (&curtoken);
00471 }
00472 
00473 struct imaptoken *nexttoken_noparseliteral(void)
00474 {
00475        return (do_readtoken(0));
00476 }
00477 
00478 /* Read an IMAP literal string (or a portion of) */
00479 
00480 void read_string(char **ptr, unsigned long *left, unsigned long cnt)
00481 {
00482        if (imap_readptrleft == 0)
00483        {
00484               /* Keep reading until we fill the buffer or until we've
00485               ** read the entire string.
00486               */
00487 
00488               read_timeout(SOCKET_TIMEOUT);
00489               imap_readptr=readbuf;
00490               while (imap_readptrleft < sizeof(readbuf) && imap_readptrleft < cnt)
00491                      imap_readptrleft += doread(readbuf+imap_readptrleft,
00492                                           sizeof(readbuf)-imap_readptrleft);
00493        }
00494 
00495        if (cnt < imap_readptrleft) /* Can satisfy fully from buffer */
00496        {
00497               *ptr=imap_readptr;
00498               *left=cnt;
00499               imap_readptr += cnt;
00500               imap_readptrleft -= cnt;
00501               return;
00502        }
00503 
00504        *ptr=imap_readptr;
00505        *left=imap_readptrleft;
00506        imap_readptrleft=0;
00507        return;
00508 }
00509 
00510 char *my_strdup(const char *s)
00511 {
00512 char   *q=strdup(s);
00513 
00514        if (!q)       write_error_exit("malloc");
00515        return (q);
00516 }
00517 
00518 int ismsgset(struct imaptoken *tok)
00519         /* See if this token is a syntactically valid message set */
00520 {
00521 
00522        if (tok->tokentype == IT_NUMBER)   return (1);
00523        if (tok->tokentype != IT_ATOM)            return (0);
00524 
00525        return ismsgset_str(tok->tokenbuf);
00526 }
00527 
00528 int ismsgset_str(const char *p)
00529 {
00530        while (isdigit((int)(unsigned char)*p) || *p == '*')
00531        {
00532               if (*p == '0')       return (0);
00533               if (*p == '*')
00534                      ++p;
00535               else
00536                      do
00537                      {
00538                             ++p;
00539                      } while (isdigit((int)(unsigned char)*p));
00540 
00541               if (*p == ':')
00542               {
00543                      ++p;
00544                      if (!isdigit((int)(unsigned char)*p) &&
00545                             *p != '*')
00546                             return (0);
00547                      if (*p == '0')       return (0);
00548                      if (*p == '*')
00549                             ++p;
00550                      else
00551                             do
00552                             {
00553                                    ++p;
00554                             } while (isdigit((int)(unsigned char)*p));
00555               }
00556               if (*p != ',')       break;
00557               ++p;
00558        }
00559        if (*p)       return (0);
00560        return (1);
00561 }
00562