Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Rfc1123.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Rfc1123.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032         
00033         @version        Initial version; May 2005      
00034 
00035         @author         Kris
00036 
00037 
00038 *******************************************************************************/
00039 
00040 module mango.convert.Rfc1123;
00041 
00042 private import mango.sys.Epoch;
00043 
00044 private import mango.convert.Sprint;
00045 
00046 extern (C) int memcmp (char *, char *, uint);
00047 
00048 
00049 /******************************************************************************
00050 
00051         Converts between native and text representations of HTTP time
00052         values. Internally, time is represented as UTC with an epoch 
00053         fixed at Jan 1st 1970. The text representation is formatted in
00054         accordance with RFC 1123, and the parser will accept one of 
00055         RFC 1123, RFC 850, or asctime formats.
00056 
00057         See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html for
00058         further detail.
00059 
00060 ******************************************************************************/
00061 
00062 class Rfc1123 : Epoch
00063 {
00064         /**********************************************************************
00065 
00066                 RFC 1123 formatted time
00067 
00068                 Converts to the format "Sun, 06 Nov 1994 08:49:37 GMT", and
00069                 returns a populated slice of the provided buffer; with zero
00070                 length if the date was invalid.
00071 
00072                 Note that RFC 1123 format is always in absolute GMT time.
00073 
00074                 A 40-element buffer is sufficient for the longest string.
00075 
00076         **********************************************************************/
00077 
00078         final static char[] format (char[] buffer, ulong time)
00079         {
00080                 // ignore invalid time values
00081                 if (time == InvalidEpoch)
00082                     return "";
00083 
00084                 // convert time to field values
00085                 Fields fields;
00086                 fields.setUtcTime (time);
00087 
00088                 // point formatter at the output buffer
00089                 SprintStruct sprint;
00090                 sprint.ctor (buffer);
00091         
00092                 // format fields according to RFC 1123
00093                 return sprint ("%.3s, %02d %.3s %04d %02d:%02d:%02d GMT",
00094                                fields.toDowName,
00095                                fields.day,
00096                                fields.toMonthName,
00097                                fields.year,
00098                                fields.hour, 
00099                                fields.min,
00100                                fields.sec
00101                                );
00102         }
00103 
00104         /**********************************************************************
00105               
00106               Parse provided input and return a UTC epoch time. A return 
00107               value of InvalidEpoch indicated a parse-failure.
00108               
00109               An option is provided to return the count of characters
00110               parsed ~ a zero value here also indicates invalid input.
00111 
00112         **********************************************************************/
00113 
00114         static ulong parse (char[] date, uint* ate = null)
00115         {
00116                 int     len;
00117                 ulong   value;
00118 
00119                 if ((len = rfc1123 (date, value)) > 0 || 
00120                     (len = rfc850  (date, value)) > 0 || 
00121                     (len = asctime (date, value)) > 0)
00122                    {
00123                    if (ate)
00124                        *ate = len;
00125                    return value;
00126                    }
00127                 return InvalidEpoch;
00128         }
00129 
00130 
00131         /**********************************************************************
00132               
00133                 RFC 822, updated by RFC 1123
00134 
00135                 "Sun, 06 Nov 1994 08:49:37 GMT"
00136                   
00137         **********************************************************************/
00138 
00139         private static int rfc1123 (char[] src, inout ulong value)
00140         {
00141                 Fields fields;
00142                 char* p = src.ptr;
00143 
00144                 bool date (inout char* p)
00145                 {
00146                         return cast(bool)
00147                                 ((fields.day = parseInt(p)) > 0    &&
00148                                  *p++ == ' '                       &&
00149                                 (fields.month = parseMonth(p)) > 0 &&
00150                                  *p++ == ' '                       &&
00151                                 (fields.year = parseInt(p)) > 0);
00152                 }
00153 
00154                 if (parseShortDay(p) >= 0 &&
00155                     *p++ == ','           &&
00156                     *p++ == ' '           &&
00157                     date (p)              &&
00158                     *p++ == ' '           &&
00159                     time (fields, p)      &&
00160                     *p++ == ' '           &&
00161                     p[0..3] == "GMT")
00162                     {
00163                     value = fields.getUtcTime;
00164                     return (p+3) - src.ptr;
00165                     }
00166 
00167                 return 0;
00168         }
00169 
00170         /**********************************************************************
00171               
00172                 RFC 850, obsoleted by RFC 1036
00173 
00174                 "Sunday, 06-Nov-94 08:49:37 GMT"
00175 
00176         **********************************************************************/
00177 
00178         private static int rfc850 (char[] src, inout ulong value)
00179         {
00180                 Fields fields;
00181                 char* p = src.ptr;
00182 
00183                 bool date (inout char* p)
00184                 {
00185                         return cast(bool)
00186                                 ((fields.day = parseInt(p)) > 0    &&
00187                                  *p++ == '-'                       &&
00188                                 (fields.month = parseMonth(p)) > 0 &&
00189                                  *p++ == '-'                       &&
00190                                 (fields.year = parseInt(p)) > 0);
00191                 }
00192 
00193                 if (parseFullDay(p) >= 0 &&
00194                     *p++ == ','          &&
00195                     *p++ == ' '          &&
00196                     date (p)             &&
00197                     *p++ == ' '          &&
00198                     time (fields, p)     &&
00199                     *p++ == ' '          &&
00200                     p[0..3] == "GMT")
00201                     {
00202                     if (fields.year <= 70)
00203                         fields.year += 2000;
00204                     else
00205                        if (fields.year <= 99)
00206                            fields.year += 1900;
00207 
00208                     value = fields.getUtcTime;
00209                     return (p+3) - src.ptr;
00210                     }
00211 
00212                 return 0;
00213         }
00214 
00215         /**********************************************************************
00216               
00217                 ANSI C's asctime() format
00218 
00219                 "Sun Nov  6 08:49:37 1994"
00220 
00221         **********************************************************************/
00222 
00223         private static int asctime (char[] src, inout ulong value)
00224         {
00225                 Fields fields;
00226                 char* p = src.ptr;
00227 
00228                 bool date (inout char* p)
00229                 {
00230                         return cast(bool)
00231                                 ((fields.month = parseMonth(p)) > 0 &&
00232                                  *p++ == ' '                        &&
00233                                 ((fields.day = parseInt(p)) > 0     ||
00234                                 (*p++ == ' '                        &&
00235                                 (fields.day = parseInt(p)) > 0)));
00236                 }
00237 
00238                 if (parseShortDay(p) >= 0 &&
00239                     *p++ == ' '           &&
00240                     date (p)              &&
00241                     *p++ == ' '           &&
00242                     time (fields, p)      &&
00243                     *p++ == ' '           &&
00244                     (fields.year = parseInt (p)) > 0)
00245                     {
00246                     value = fields.getUtcTime;
00247                     return p - src.ptr;
00248                     }
00249 
00250                 return 0;
00251         }
00252 
00253         /**********************************************************************
00254               
00255                 Parse a time field
00256 
00257         **********************************************************************/
00258 
00259         private static bool time (inout Fields fields, inout char* p)
00260         {
00261                 return cast(bool) 
00262                        ((fields.hour = parseInt(p)) > 0 &&
00263                          *p++ == ':'                    &&
00264                         (fields.min = parseInt(p)) > 0  &&
00265                          *p++ == ':'                    &&
00266                         (fields.sec = parseInt(p)) > 0);
00267         }
00268 
00269         /**********************************************************************
00270               
00271                 Match a month from the input
00272 
00273         **********************************************************************/
00274 
00275         private static int parseMonth (inout char* p)
00276         {
00277                 int month;
00278 
00279                 switch (p[0..3])
00280                        {
00281                        case "Jan":
00282                             month = 1;
00283                             break; 
00284                        case "Feb":
00285                             month = 2;
00286                             break; 
00287                        case "Mar":
00288                             month = 3;
00289                             break; 
00290                        case "Apr":
00291                             month = 4;
00292                             break; 
00293                        case "May":
00294                             month = 5;
00295                             break; 
00296                        case "Jun":
00297                             month = 6;
00298                             break; 
00299                        case "Jul":
00300                             month = 7;
00301                             break; 
00302                        case "Aug":
00303                             month = 8;
00304                             break; 
00305                        case "Sep":
00306                             month = 9;
00307                             break; 
00308                        case "Oct":
00309                             month = 10;
00310                             break; 
00311                        case "Nov":
00312                             month = 11;
00313                             break; 
00314                        case "Dec":
00315                             month = 12;
00316                             break; 
00317                        default:
00318                             return month;
00319                        }
00320 
00321                 p += 3;
00322                 return month;
00323         }
00324 
00325         /**********************************************************************
00326               
00327                 Match a day from the input
00328 
00329         **********************************************************************/
00330 
00331         private static int parseShortDay (inout char* p)
00332         {
00333                 int day;
00334 
00335                 switch (p[0..3])
00336                        {
00337                        case "Sun":
00338                             day = 0;
00339                             break;
00340                        case "Mon":
00341                             day = 1;
00342                             break; 
00343                        case "Tue":
00344                             day = 2;
00345                             break; 
00346                        case "Wed":
00347                             day = 3;
00348                             break; 
00349                        case "Thu":
00350                             day = 4;
00351                             break; 
00352                        case "Fri":
00353                             day = 5;
00354                             break; 
00355                        case "Sat":
00356                             day = 6;
00357                             break; 
00358                        default:
00359                             return -1;
00360                        }
00361 
00362                 p += 3;
00363                 return day;
00364         }
00365 
00366         /**********************************************************************
00367               
00368                 Match a day from the input
00369 
00370         **********************************************************************/
00371 
00372         private static int parseFullDay (inout char* p)
00373         {
00374                 foreach (int i, char[] day; Fields.Days)
00375                          if (memcmp (day, p, day.length) == 0)
00376                             {
00377                             p += day.length;
00378                             return i;
00379                             }
00380                 return -1;
00381         }
00382 
00383 
00384         /**********************************************************************
00385               
00386                 Extract an integer from the input
00387 
00388         **********************************************************************/
00389 
00390         private static int parseInt (inout char* p)
00391         {
00392                 int value;
00393 
00394                 while (*p >= '0' && *p <= '9')
00395                        value = value * 10 + *p++ - '0';
00396                 return value;
00397         }
00398 }

Generated on Sat Dec 24 17:28:33 2005 for Mango by  doxygen 1.4.0