// HTTP_Helpers.cpp -- Helper utilities for both server and client #include "HTTP_Helpers.h" #include "ace/Log_Msg.h" #include "ace/OS_NS_string.h" #include "ace/Guard_T.h" #include "ace/OS_NS_time.h" #include "ace/OS_NS_stdio.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 // static const char rfc1123_date[] = "%3s,;%2d;%3s;%4d;%2d:%2d:%2d;GMT"; // static const char rfc850_date[] = "%s,;%2d-%3s-%2d;%2d:%2d:%2d;GMT"; // static 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, "%3s,;%2d;%3s;%4d;%2d:%2d:%2d;GMT", // RFC-1123 date format weekday, &tms.tm_mday, month, &tms.tm_year, &tms.tm_hour, &tms.tm_min, &tms.tm_sec) == 7) ; else if (::sscanf(buf, "%s,;%2d-%3s-%2d;%2d:%2d:%2d;GMT", // RFC-850 date format 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, "%3s;%3s;%2d;%2d:%2d:%2d;%4d", // ASCTIME date format. 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_RETURN (ACE_SYNCH_MUTEX, g, HTTP_Helper::mutex_, -1)); #endif /* NOT ACE_HAS_REENTRANT_LIBC */ return ACE_OS::mktime (&tms); } } const char * HTTP_Helper::HTTP_date () { if (HTTP_Helper::date_string_ == 0) { ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, m, HTTP_Helper::mutex_, 0)); if (HTTP_Helper::date_string_ == 0) { // 40 bytes is all I need. ACE_NEW_RETURN (HTTP_Helper::date_string_, char[40], 0); if (!HTTP_Helper::HTTP_date (HTTP_Helper::date_string_)) { delete [] HTTP_Helper::date_string_; HTTP_Helper::date_string_ = 0; } } } return HTTP_Helper::date_string_; } const char * HTTP_Helper::HTTP_date (char *s) { // Return the date-string formatted per HTTP standards. Time must // be in UTC, so using the 'strftime' call (which obeys the locale) // isn't correct. static const char* months[] = {"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; static const char* days[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; time_t tloc; struct tm tms; char * date_string = s; if (ACE_OS::time (&tloc) != (time_t) -1 && ACE_OS::gmtime_r (&tloc, &tms) != 0) { ACE_OS::sprintf (date_string, "%s, %2.2d %s %4.4d %2.2d:%2.2d:%2.2d GMT", days[tms.tm_wday], tms.tm_mday, months[tms.tm_mon], tms.tm_year + 1900, tms.tm_hour, tms.tm_min, tms.tm_sec); } 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 () { if (HTTP_Status_Code::instance_ == 0) { ACE_MT (ACE_GUARD_RETURN (ACE_SYNCH_MUTEX, g, lock_, 0)); 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; }