// $Id$ // HTTP_Helpers.cpp -- Helper utilities for both server and client #include "HTTP_Helpers.h" // = Static initialization. const char *const HTTP_Helper::months_[12]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char const *HTTP_Helper::alphabet_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char * HTTP_Helper::date_string_ = 0; ACE_SYNCH_MUTEX HTTP_Helper::mutex_; ACE_SYNCH_MUTEX HTTP_Status_Code::lock_; int HTTP_Status_Code::instance_ = 0; const char *HTTP_Status_Code::Reason[HTTP_Status_Code::MAX_STATUS_CODE + 1]; time_t HTTP_Helper::HTTP_mktime (const char *httpdate) { char *buf; ACE_NEW_RETURN (buf, char[ACE_OS::strlen (httpdate) + 1], (time_t) -1); // Make spaces in the date be semi-colons so we can parse robustly // with sscanf. const char *ptr1 = httpdate; char *ptr2 = buf; do { if (*ptr1 == ' ') *ptr2++ = ';'; else *ptr2++ = *ptr1; } while (*ptr1++ != '\0'); // In HTTP/1.0, there are three versions of an HTTP_date. // rfc1123-date = wkday "," SP dd month yyyy SP hh:mm:ss SP "GMT" // rfc850-date = weekday "," SP dd-month-yy SP hh:mm:ss SP "GMT" // asctime-date = wkday SP month dd SP hh:mm:ss SP yyyy const char *rfc1123_date = "%3s,;%2d;%3s;%4d;%2d:%2d:%2d;GMT"; const char *rfc850_date = "%s,;%2d-%3s-%2d;%2d:%2d:%2d;GMT"; const char *asctime_date = "%3s;%3s;%2d;%2d:%2d:%2d;%4d"; // Should also support other versions (such as from NNTP and SMTP) // for robustness, but it should be clear how to extend this. struct tm tms; char month[4]; char weekday[10]; if (::sscanf(buf, rfc1123_date, weekday, &tms.tm_mday, month, &tms.tm_year, &tms.tm_hour, &tms.tm_min, &tms.tm_sec) == 7) ; else if (::sscanf(buf, rfc850_date, weekday, &tms.tm_mday, month, &tms.tm_year, &tms.tm_hour, &tms.tm_min, &tms.tm_sec) == 7) { weekday[3] = '\0'; } else if (::sscanf(buf, asctime_date, weekday, month, &tms.tm_mday, &tms.tm_hour, &tms.tm_min, &tms.tm_sec, &tms.tm_year) == 7) ; delete buf; tms.tm_year = HTTP_Helper::fixyear (tms.tm_year); tms.tm_mon = HTTP_Helper::HTTP_month (month); if (tms.tm_mon == -1) return (time_t) -1; // mktime is a Standard C function. { #if !defined (ACE_HAS_REENTRANT_LIBC) ACE_MT (ACE_Guard g (HTTP_Helper::mutex_)); #endif /* NOT ACE_HAS_REENTRANT_LIBC */ return ACE_OS::mktime (&tms); } } const char * HTTP_Helper::HTTP_date (void) { if (HTTP_Helper::date_string_ == 0) { ACE_MT (ACE_Guard m (HTTP_Helper::mutex_)); time_t tloc; struct tm tms; if (HTTP_Helper::date_string_ == 0) { // 40 bytes is all I need. HTTP_Helper::date_string_ = new char[40]; if (ACE_OS::time (&tloc) != (time_t) -1 && ACE_OS::gmtime_r (&tloc, &tms) != NULL) ACE_OS::strftime (HTTP_Helper::date_string_, 40, "%a, %d %b %Y %T GMT", &tms); else { delete [] HTTP_Helper::date_string_; HTTP_Helper::date_string_ = 0; } } } return HTTP_Helper::date_string_; } const char * HTTP_Helper::HTTP_date (char *s) { time_t tloc; struct tm tms; char * date_string = s; if (ACE_OS::time (&tloc) != (time_t) -1 && ACE_OS::gmtime_r (&tloc, &tms) != NULL) ACE_OS::strftime (date_string, 40, "%a, %d %b %Y %T GMT", &tms); else date_string = 0; return date_string; } int HTTP_Helper::HTTP_month (const char *month) { for (size_t i = 0; i < 12; i++) if (ACE_OS::strcmp(month, HTTP_Helper::months_[i]) == 0) return i; return -1; } const char * HTTP_Helper::HTTP_month (int month) { if (month < 0 || month >= 12) return 0; return HTTP_Helper::months_[month]; } // Fix the path if it needs fixing/is fixable. char * HTTP_Helper::HTTP_decode_string (char *path) { // replace the percentcodes with the actual character int i, j; char percentcode[3]; for (i = j = 0; path[i] != '\0'; i++, j++) { if (path[i] == '%') { percentcode[0] = path[++i]; percentcode[1] = path[++i]; percentcode[2] = '\0'; path[j] = (char) ACE_OS::strtol (percentcode, (char **) 0, 16); } else path[j] = path[i]; } path[j] = path[i]; return path; } char * HTTP_Helper::HTTP_decode_base64 (char *data) { char inalphabet[256], decoder[256]; ACE_OS::memset (inalphabet, 0, sizeof (inalphabet)); ACE_OS::memset (decoder, 0, sizeof (decoder)); for (int i = ACE_OS::strlen (HTTP_Helper::alphabet_) - 1; i >= 0; i--) { inalphabet[(unsigned int) HTTP_Helper::alphabet_[i]] = 1; decoder[(unsigned int) HTTP_Helper::alphabet_[i]] = i; } char *indata = data; char *outdata = data; int bits = 0; int c; int char_count = 0; int errors = 0; while ((c = *indata++) != '\0') { if (c == '=') break; if (c > 255 || ! inalphabet[c]) continue; bits += decoder[c]; char_count++; if (char_count == 4) { *outdata++ = (bits >> 16); *outdata++ = ((bits >> 8) & 0xff); *outdata++ = (bits & 0xff); bits = 0; char_count = 0; } else bits <<= 6; } if (c == '\0') { if (char_count) { ACE_DEBUG ((LM_DEBUG, "base64 encoding incomplete: at least %d bits truncated\n", ((4 - char_count) * 6))); errors++; } } else { // c == '=' switch (char_count) { case 1: ACE_DEBUG ((LM_DEBUG, "base64 encoding incomplete: at least 2 bits missing\n")); errors++; break; case 2: *outdata++ = (bits >> 10); break; case 3: *outdata++ = (bits >> 16); *outdata++ = ((bits >> 8) & 0xff); break; } } *outdata = '\0'; return errors ? 0 : data; } char * HTTP_Helper::HTTP_encode_base64 (char *data) { char buf[BUFSIZ]; int c; int error; int char_count = 0; int bits = 0; error = 0; char *indata = data; char *outdata = buf; const unsigned char ASCII_MAX = ~0; while ((c = *indata++) != '\0') { if (c > (int)ASCII_MAX) { ACE_DEBUG ((LM_DEBUG, "encountered char > 255 (decimal %d)\n", c)); error++; break; } bits += c; char_count++; if (char_count == 3) { *outdata++ = HTTP_Helper::alphabet_[bits >> 18]; *outdata++ = HTTP_Helper::alphabet_[(bits >> 12) & 0x3f]; *outdata++ = HTTP_Helper::alphabet_[(bits >> 6) & 0x3f]; *outdata++ = HTTP_Helper::alphabet_[bits & 0x3f]; bits = 0; char_count = 0; } else bits <<= 8; } if (!error) { if (char_count != 0) { bits <<= 16 - (8 * char_count); *outdata++ = HTTP_Helper::alphabet_[bits >> 18]; *outdata++ = HTTP_Helper::alphabet_[(bits >> 12) & 0x3f]; if (char_count == 1) { *outdata++ = '='; *outdata++ = '='; } else { *outdata++ = HTTP_Helper::alphabet_[(bits >> 6) & 0x3f]; *outdata++ = '='; } } *outdata = '\0'; ACE_OS::strcpy (data, buf); } return (error ? 0 : data); } int HTTP_Helper::fixyear (int year) { // Fix the year 2000 problem if (year > 1000) year -= 1900; else if (year < 100) { struct tm tms; time_t tloc; if (ACE_OS::time (&tloc) != (time_t) -1) { ACE_OS::gmtime_r (&tloc, &tms); if (tms.tm_year % 100 == year) year = tms.tm_year; // The last two cases check boundary conditions, in case the // year just changed at the moment we checked to see if we // need to fix it. if ((year+1) % 100 == tms.tm_year % 100) year = tms.tm_year - 1; if (year == (tms.tm_year + 1) % 100) year = tms.tm_year + 1; // What to do if none of the above? } } return year; } const char ** HTTP_Status_Code::instance (void) { if (HTTP_Status_Code::instance_ == 0) { ACE_Guard g (lock_); if (HTTP_Status_Code::instance_ == 0) { for (size_t i = 0; i < HTTP_Status_Code::MAX_STATUS_CODE + 1; i++) { switch (i) { case STATUS_OK: HTTP_Status_Code::Reason[i] = "OK"; break; case STATUS_CREATED: HTTP_Status_Code::Reason[i] = "Created"; break; case STATUS_ACCEPTED: HTTP_Status_Code::Reason[i] = "Accepted"; break; case STATUS_NO_CONTENT: HTTP_Status_Code::Reason[i] = "No Content"; break; case STATUS_MOVED_PERMANENTLY: HTTP_Status_Code::Reason[i] = "Moved Permanently"; break; case STATUS_MOVED_TEMPORARILY: HTTP_Status_Code::Reason[i] = "Moved Temporarily"; break; case STATUS_NOT_MODIFIED: HTTP_Status_Code::Reason[i] = "Not Modified"; break; case STATUS_BAD_REQUEST: HTTP_Status_Code::Reason[i] = "Bad Request"; break; case STATUS_UNAUTHORIZED: HTTP_Status_Code::Reason[i] = "Unauthorized"; break; case STATUS_FORBIDDEN: HTTP_Status_Code::Reason[i] = "Forbidden"; break; case STATUS_NOT_FOUND: HTTP_Status_Code::Reason[i] = "Not Found"; break; case STATUS_INTERNAL_SERVER_ERROR: HTTP_Status_Code::Reason[i] = "Internal Server Error"; break; case STATUS_NOT_IMPLEMENTED: HTTP_Status_Code::Reason[i] = "Not Implemented"; break; case STATUS_BAD_GATEWAY: HTTP_Status_Code::Reason[i] = "Bad Gateway"; break; case STATUS_SERVICE_UNAVAILABLE: HTTP_Status_Code::Reason[i] = "Service Unavailable"; break; default: HTTP_Status_Code::Reason[i] = "Unknown"; } } HTTP_Status_Code::instance_ = 1; } // GUARD released } return HTTP_Status_Code::Reason; }