Back to index

courier  0.68.2
rfc822_parsedt.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 /*
00007 */
00008 #include      "config.h"
00009 #include      <stdio.h>
00010 #include      <string.h>
00011 #include      <time.h>
00012 
00013 #define my_isalpha(c) ( ( (c) >= 'a' && (c) <= 'z' ) || \
00014                      ( (c) >= 'A' && (c) <= 'Z' ) )
00015 
00016 #define my_isdigit(c) ( (c) >= '0' && (c) <= '9' )
00017 
00018 #define my_isalnum(c) ( my_isalpha(c) || my_isdigit(c) )
00019 
00020 #define my_isspace(c) ( (c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
00021 
00022 /*
00023 ** time_t rfc822_parsedate(const char *p)
00024 **
00025 ** p - contents of the Date: header, attempt to parse it into a time_t.
00026 **
00027 ** returns - time_t, or 0 if the date cannot be parsed
00028 */
00029 
00030 static unsigned parsedig(const char **p)
00031 {
00032        unsigned i=0;
00033 
00034        while (my_isdigit(**p))
00035        {
00036               i=i*10 + **p - '0';
00037               ++*p;
00038        }
00039        return (i);
00040 }
00041 
00042 static const char * const weekdays[7]={
00043        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00044        } ;
00045 
00046 static const char * const mnames[13]={
00047        "Jan", "Feb", "Mar", "Apr",
00048        "May", "Jun", "Jul", "Aug",
00049        "Sep", "Oct", "Nov", "Dec", NULL};
00050 
00051 #define       leap(y)       ( \
00052                      ((y) % 400) == 0 || \
00053                      (((y) % 4) == 0 && (y) % 100) )
00054 
00055 static unsigned mlength[]={31,28,31,30,31,30,31,31,30,31,30,31};
00056 #define       mdays(m,y)    ( (m) != 2 ? mlength[(m)-1] : leap(y) ? 29:28)
00057 
00058 static const char * const zonenames[] = {
00059        "UT","GMT",
00060        "EST","EDT",
00061        "CST","CDT",
00062        "MST","MDT",
00063        "PST","PDT",
00064        "Z",
00065        "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", 
00066        "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
00067        NULL};
00068 
00069 #define       ZH(n)  ( (n) * 60 * 60 )
00070 
00071 static int zoneoffset[] = {
00072        0, 0,
00073        ZH(-5), ZH(-4),
00074        ZH(-6), ZH(-5),
00075        ZH(-7), ZH(-6),
00076        ZH(-8), ZH(-7),
00077        0,
00078 
00079        ZH(-1), ZH(-2), ZH(-3), ZH(-4), ZH(-5), ZH(-6), ZH(-7), ZH(-8), ZH(-9), ZH(-10), ZH(-11), ZH(-12),
00080        ZH(1), ZH(2), ZH(3), ZH(4), ZH(5), ZH(6), ZH(7), ZH(8), ZH(9), ZH(10), ZH(11), ZH(12) };
00081 
00082 #define lc(x) ((x) >= 'A' && (x) <= 'Z' ? (x) + ('a'-'A'):(x))
00083 
00084 static unsigned parsekey(const char **mon, const char * const *ary)
00085 {
00086 unsigned m, j;
00087 
00088        for (m=0; ary[m]; m++)
00089        {
00090               for (j=0; ary[m][j]; j++)
00091                      if (lc(ary[m][j]) != lc((*mon)[j]))
00092                             break;
00093               if (!ary[m][j])
00094               {
00095                      *mon += j;
00096                      return (m+1);
00097               }
00098        }
00099        return (0);
00100 }
00101 
00102 static int parsetime(const char **t)
00103 {
00104        unsigned h,m,s=0;
00105 
00106        if (!my_isdigit(**t))       return (-1);
00107 
00108        h=parsedig(t);
00109        if (h > 23)          return (-1);
00110        if (**t != ':')             return (-1);
00111        ++*t;
00112        if (!my_isdigit(**t))       return (-1);
00113        m=parsedig(t);
00114        if (**t == ':')
00115        {
00116               ++*t;
00117 
00118               if (!my_isdigit(**t))       return (-1);
00119               s=parsedig(t);
00120        }
00121        if (m > 59 || s > 59)       return (-1);
00122        return (h * 60 * 60 + m * 60 + s);
00123 }
00124 
00125 time_t rfc822_parsedt(const char *rfcdt)
00126 {
00127 unsigned day=0, mon=0, year;
00128 int secs;
00129 int offset;
00130 time_t t;
00131 unsigned y;
00132 
00133        /* Ignore day of the week.  Tolerate "Tue, 25 Feb 1997 ... "
00134        ** without the comma.  Tolerate "Feb 25 1997 ...".
00135        */
00136 
00137        while (!day || !mon)
00138        {
00139               if (!*rfcdt)  return (0);
00140               if (my_isalpha(*rfcdt))
00141               {
00142                      if (mon)      return (0);
00143                      mon=parsekey(&rfcdt, mnames);
00144                      if (!mon)
00145                             while (*rfcdt && my_isalpha(*rfcdt))
00146                                    ++rfcdt;
00147                      continue;
00148               }
00149 
00150               if (my_isdigit(*rfcdt))
00151               {
00152                      if (day)      return (0);
00153                      day=parsedig(&rfcdt);
00154                      if (!day)     return (0);
00155                      continue;
00156               }
00157               ++rfcdt;
00158        }
00159 
00160        while (*rfcdt && my_isspace(*rfcdt))
00161               ++rfcdt;
00162        if (!my_isdigit(*rfcdt))    return (0);
00163        year=parsedig(&rfcdt);
00164        if (year < 70)       year += 2000;
00165        if (year < 100)      year += 1900;
00166 
00167        while (*rfcdt && my_isspace(*rfcdt))
00168               ++rfcdt;
00169 
00170        if (day == 0 || mon == 0 || mon > 12 || day > mdays(mon,year))
00171               return (0);
00172 
00173        secs=parsetime(&rfcdt);
00174        if (secs < 0) return (0);
00175 
00176        offset=0;
00177 
00178        /* RFC822 sez no parenthesis, but I've seen (EST) */
00179 
00180        while ( *rfcdt )
00181        {
00182               if (my_isalnum(*rfcdt) || *rfcdt == '+' || *rfcdt == '-')
00183                      break;
00184               ++rfcdt;
00185        }
00186 
00187        if (my_isalpha((int)(unsigned char)*rfcdt))
00188        {
00189        int    n=parsekey(&rfcdt, zonenames);
00190 
00191               if (n > 0)    offset= zoneoffset[n-1];
00192        }
00193        else
00194        {
00195        int    sign=1;
00196        unsigned n;
00197 
00198               switch (*rfcdt)      {
00199               case '-':
00200                      sign= -1;
00201               case '+':
00202                      ++rfcdt;
00203               }
00204 
00205               if (my_isdigit(*rfcdt))
00206               {
00207                      n=parsedig(&rfcdt);
00208                      if (n > 2359 || (n % 100) > 59)    n=0;
00209                      offset = sign * ( (n % 100) * 60 + n / 100 * 60 * 60);
00210               }
00211        }
00212 
00213        if (year < 1970)     return (0);
00214        if (year > 9999)     return (0);
00215 
00216        t=0;
00217        for (y=1970; y<year; y++)
00218        {
00219               if ( leap(y) )
00220               {
00221                      if (year-y >= 4)
00222                      {
00223                             y += 3;
00224                             t += ( 365*3+366 ) * 24 * 60 * 60;
00225                             continue;
00226                      }
00227                      t += 24 * 60 * 60;
00228               }
00229               t += 365 * 24 * 60 * 60;
00230        }
00231 
00232        for (y=1; y < mon; y++)
00233               t += mdays(y, year) * 24 * 60 * 60;
00234 
00235        return ( t + (day-1) * 24 * 60 * 60 + secs - offset );
00236 }
00237 
00238 const char *rfc822_mkdt(time_t t)
00239 {
00240 static char buf[80];
00241 struct tm *tmptr=gmtime(&t);
00242 
00243        buf[0]=0;
00244        if (tmptr)
00245        {
00246               sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT",
00247                      weekdays[tmptr->tm_wday],
00248                      tmptr->tm_mday,
00249                      mnames[tmptr->tm_mon],
00250                      tmptr->tm_year + 1900,
00251                      tmptr->tm_hour,
00252                      tmptr->tm_min,
00253                      tmptr->tm_sec);
00254        }
00255        return (buf);
00256 }