Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/downloader/httpDate.cxx

Go to the documentation of this file.
00001 // Filename: httpDate.cxx
00002 // Created by:  drose (28Jan03)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "httpDate.h"
00020 
00021 #include <ctype.h>
00022 
00023 static const int num_weekdays = 7;
00024 static const char * const weekdays[num_weekdays] = {
00025   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00026 };
00027 
00028 static const int num_months = 12;
00029 static const char * const months[num_months] = {
00030   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00031   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00032 };
00033 
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: HTTPDate::Constructor
00037 //       Access: Published
00038 //  Description: Decodes the string into a sensible date.  Returns 0
00039 //               (!is_valid()) if the string cannot be correctly
00040 //               decoded.
00041 ////////////////////////////////////////////////////////////////////
00042 HTTPDate::
00043 HTTPDate(const string &format) {
00044   _time = (time_t)(-1);
00045 
00046   struct tm t;
00047   memset(&t, 0, sizeof(t));
00048 
00049   bool got_weekday = false;
00050   bool got_month = false;
00051   bool got_day = false;
00052   bool got_year = false;
00053   bool got_hour = false;
00054   bool got_minute = false;
00055   bool got_second = false;
00056 
00057   enum ExpectNext { 
00058     EN_none,
00059     EN_second,
00060     EN_year
00061   };
00062   ExpectNext expect_next = EN_none;
00063 
00064   size_t pos = 0;
00065   string token = get_token(format, pos);
00066   while (!token.empty()) {
00067     ExpectNext expected = expect_next;
00068     expect_next = EN_none;
00069 
00070     if (isdigit(token[0])) {
00071       // Here's a number.
00072       int value = atoi(token.c_str());
00073       if (token[token.length() - 1] == ':') {
00074         // If it ends in a colon, it must be hh or mm.
00075         if (!got_hour) {
00076           t.tm_hour = value;
00077           got_hour = true;
00078           
00079         } else if (!got_minute) {
00080           t.tm_min = value;
00081           got_minute = true;
00082           expect_next = EN_second;
00083 
00084         } else {
00085           return;
00086         }
00087 
00088       } else if (token[token.length() - 1] == '/') {
00089         // If it ends in a colon, it must be mm/dd/.
00090         if (!got_month) {
00091           t.tm_mon = value - 1;
00092           got_month = true;
00093           
00094         } else if (!got_day) {
00095           t.tm_mday = value;
00096           got_day = true;
00097           expect_next = EN_year;
00098 
00099         } else {
00100           return;
00101         }
00102 
00103       } else {
00104         if (expected == EN_second) {
00105           // The first number following hh:mm: is always the seconds.
00106           t.tm_sec = value;
00107           got_second = true;
00108 
00109         } else if (expected == EN_year) {
00110           // The first number following mm/dd/ is always the year.
00111           t.tm_year = value;
00112           got_year = true;
00113           
00114         } else if (!got_day) {
00115           // Assume it's a day.
00116           t.tm_mday = value;
00117           got_day = true;
00118           
00119         } else if (!got_year) {
00120           // It must be the year.
00121           t.tm_year = value;
00122           got_year = true;
00123           
00124         } else if (!got_hour) {
00125           t.tm_hour = value;
00126           got_hour = true;
00127           
00128         } else if (!got_minute) {
00129           t.tm_min = value;
00130           got_minute = true;
00131           
00132         } else if (!got_second) {
00133           t.tm_sec = value;
00134           got_second = true;
00135           
00136         } else {
00137           // Huh, an unexpected numeric value.
00138           return;
00139         }
00140       }
00141 
00142     } else {
00143       // This is a string token.  It should be either a month name or
00144       // a day name, or a timezone name--but the only timezone name we
00145       // expect to see is "GMT".
00146       bool matched = false;
00147       int i;
00148 
00149       for (i = 0; i < num_weekdays && !matched; i++) {
00150         if (token == weekdays[i]) {
00151           if (got_weekday) {
00152             return;
00153           }
00154           matched = true;
00155           got_weekday = true;
00156           t.tm_wday = i;
00157         }
00158       }
00159 
00160       for (i = 0; i < num_months && !matched; i++) {
00161         if (token == months[i]) {
00162           if (got_month) {
00163             return;
00164           }
00165           matched = true;
00166           got_month = true;
00167           t.tm_mon = i;
00168         }
00169       }
00170 
00171       if (!matched && token == "Gmt") {
00172         matched = true;
00173       }
00174 
00175       if (!matched) {
00176         // Couldn't figure this one out.
00177         return;
00178       }
00179     }
00180 
00181     token = get_token(format, pos);
00182   }
00183 
00184   // Now check that we got the minimum expected tokens.
00185   if (!(got_month && got_day && got_year && got_hour && got_minute)) {
00186     return;
00187   }
00188 
00189   // Also validate the tokens we did get.
00190   if (t.tm_year < 100) {
00191     // Two-digit year.  Assume it's in the same century, unless
00192     // that assumption puts it more than 50 years in the future.
00193     time_t now = time(NULL);
00194     struct tm *tp = gmtime(&now);
00195     t.tm_year += 100 * (tp->tm_year / 100);
00196     if (t.tm_year - tp->tm_year > 50) {
00197       t.tm_year -= 100;
00198     }
00199     
00200   } else if (t.tm_year < 1900) {
00201     // Invalid three- or four-digit year.  Give up.
00202     return;
00203     
00204   } else {
00205     t.tm_year -= 1900;
00206   }
00207           
00208   if (!((t.tm_mon >= 0 && t.tm_mon < num_months) &&
00209         (t.tm_mday >= 1 && t.tm_mday <= 31) &&
00210         (t.tm_hour >= 0 && t.tm_hour < 60) &&
00211         (t.tm_min >= 0 && t.tm_min < 60) &&
00212         (t.tm_sec >= 0 && t.tm_sec < 62) /* maybe leap seconds */)) {
00213     return;
00214   }
00215 
00216   // Everything checks out; convert the date.
00217   _time = mktime(&t);
00218 
00219   if (_time != (time_t)-1) {
00220     // Unfortunately, mktime() assumes local time; convert this back
00221     // to GMT.
00222     extern long int timezone;
00223     _time -= timezone;
00224   }
00225 }
00226 
00227 ////////////////////////////////////////////////////////////////////
00228 //     Function: HTTPDate::get_string
00229 //       Access: Published
00230 //  Description:
00231 ////////////////////////////////////////////////////////////////////
00232 string HTTPDate::
00233 get_string() const {
00234   if (!is_valid()) {
00235     return "Invalid Date";
00236   }
00237 
00238   struct tm *tp = gmtime(&_time);
00239 
00240   ostringstream result;
00241   result
00242     << weekdays[tp->tm_wday] << ", " 
00243     << setw(2) << setfill('0') << tp->tm_mday << " "
00244     << months[tp->tm_mon] << " "
00245     << setw(4) << setfill('0') << tp->tm_year + 1900 << " "
00246     << setw(2) << setfill('0') << tp->tm_hour << ":"
00247     << setw(2) << setfill('0') << tp->tm_min << ":"
00248     << setw(2) << setfill('0') << tp->tm_sec << " GMT";
00249 
00250   return result.str();
00251 }
00252 
00253 
00254 ////////////////////////////////////////////////////////////////////
00255 //     Function: HTTPDate::input
00256 //       Access: Published
00257 //  Description: 
00258 ////////////////////////////////////////////////////////////////////
00259 bool HTTPDate::
00260 input(istream &in) {
00261   (*this) = HTTPDate();
00262 
00263   // Extract out the quoted date string.
00264   char ch;
00265   in >> ch;
00266   if (ch != '"') {
00267     return false;
00268   }
00269 
00270   string date;
00271   ch = in.get();
00272   while (!in.fail() && !in.eof() && ch != '"') {
00273     date += ch;
00274     ch = in.get();
00275   }
00276 
00277   if (ch != '"') {
00278     return false;
00279   }
00280 
00281   // Visual C++ has problems with "(*this) = HTTPDate(date)".
00282   HTTPDate new_date(date);
00283   (*this) = new_date;
00284   return is_valid();
00285 }
00286 
00287 ////////////////////////////////////////////////////////////////////
00288 //     Function: HTTPDate::output
00289 //       Access: Published
00290 //  Description: 
00291 ////////////////////////////////////////////////////////////////////
00292 void HTTPDate::
00293 output(ostream &out) const {
00294   // We put quotes around the string on output, so we can reliably
00295   // detect the end of the date string on input, above.
00296   out << '"' << get_string() << '"';
00297 }
00298 
00299 ////////////////////////////////////////////////////////////////////
00300 //     Function: HTTPDate::get_token
00301 //       Access: Published
00302 //  Description: Extracts the next token from the string starting at
00303 //               the indicated position.  Returns the token and
00304 //               updates pos.  When the last token has been extracted,
00305 //               returns empty string.
00306 //
00307 //               A token is defined as a contiguous sequence of digits
00308 //               or letters.  If it is a sequence of letters, the
00309 //               function quietly truncates it to three letters before
00310 //               returning, and forces the first letter to capital and
00311 //               the second two to lowercase.  If it is a sequence of
00312 //               digits, the function also returns the next character
00313 //               following the last digit (unless it is a letter).
00314 ////////////////////////////////////////////////////////////////////
00315 string HTTPDate::
00316 get_token(const string &str, size_t &pos) {
00317   // Start by scanning for the first alphanumeric character.
00318   size_t start = pos;
00319   while (start < str.length() && !isalnum(str[start])) {
00320     start++;
00321   }
00322 
00323   if (start >= str.length()) {
00324     // End of the line.
00325     pos = string::npos;
00326     return string();
00327   }
00328 
00329   string token;
00330 
00331   if (isalpha(str[start])) {
00332     // A string of letters.
00333     token = toupper(str[start]);
00334     pos = start + 1;
00335     while (pos < str.length() && isalpha(str[pos])) {
00336       if (token.length() < 3) {
00337         token += tolower(str[pos]);
00338       }
00339       pos++;
00340     }
00341 
00342   } else {
00343     // A string of digits.
00344     pos = start + 1;
00345     while (pos < str.length() && isdigit(str[pos])) {
00346       pos++;
00347     }
00348     // Get one more character, so we can identify things like hh:
00349     if (pos < str.length() && !isalpha(str[pos])) {
00350       pos++;
00351     }
00352     token = str.substr(start, pos - start);
00353   }
00354 
00355   return token;
00356 }

Generated on Fri May 2 00:36:49 2003 for Panda by doxygen1.3