Panda3D
 All Classes Functions Variables Enumerations
httpAuthorization.cxx
00001 // Filename: httpAuthorization.cxx
00002 // Created by:  drose (22Oct02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "httpAuthorization.h"
00016 #include "httpChannel.h"
00017 #include "urlSpec.h"
00018 
00019 #ifdef HAVE_OPENSSL
00020 
00021 static const char base64_table[64] = {
00022   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
00023   'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
00024   'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
00025   'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
00026   'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
00027   'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
00028   'w', 'x', 'y', 'z', '0', '1', '2', '3',
00029   '4', '5', '6', '7', '8', '9', '+', '/',
00030 };
00031 
00032 static unsigned char base64_invert[128];
00033 static bool got_base64_invert = false;
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: HTTPAuthorization::Constructor
00037 //       Access: Protected
00038 //  Description: 
00039 ////////////////////////////////////////////////////////////////////
00040 HTTPAuthorization::
00041 HTTPAuthorization(const HTTPAuthorization::Tokens &tokens, 
00042                   const URLSpec &url, bool is_proxy) {
00043   Tokens::const_iterator ti;
00044   ti = tokens.find("realm");
00045   if (ti != tokens.end()) {
00046     _realm = (*ti).second;
00047   }
00048 
00049   URLSpec canon = get_canonical_url(url);
00050 
00051   ti = tokens.find("domain");
00052   if (ti != tokens.end() && !is_proxy) {
00053     // Now the domain consists of a series of space-separated URL
00054     // prefixes.
00055     const string &domain = (*ti).second;
00056     size_t p = 0;
00057     while (p < domain.length()) {
00058       while (p < domain.length() && isspace(domain[p])) {
00059         ++p;
00060       }
00061       size_t q = p;
00062       while (q < domain.length() && !isspace(domain[q])) {
00063         ++q;
00064       }
00065       if (q > p) {
00066         string domain_str = domain.substr(p, q - p);
00067         URLSpec domain_url(domain_str);
00068         if (domain_url.has_server()) {
00069           // A fully-qualified URL.
00070           _domain.push_back(get_canonical_url(domain_url).get_url());
00071         } else {
00072           // A relative URL; relative to this path.
00073           domain_url = canon;
00074           domain_url.set_path(domain_str);
00075           _domain.push_back(domain_url.get_url());
00076         }
00077       }
00078       p = q;
00079     }
00080 
00081   } else {
00082     // If no domain is defined by the server, use the supplied URL.
00083     // Truncate it to the rightmost slash.
00084     string canon_str = canon.get_url();
00085     size_t slash = canon_str.rfind('/');
00086     nassertv(slash != string::npos);
00087     _domain.push_back(canon_str.substr(0, slash + 1));
00088   }
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: HTTPAuthorization::Destructor
00093 //       Access: Public, Virtual
00094 //  Description: 
00095 ////////////////////////////////////////////////////////////////////
00096 HTTPAuthorization::
00097 ~HTTPAuthorization() {
00098 }
00099 
00100 ////////////////////////////////////////////////////////////////////
00101 //     Function: HTTPAuthorization::is_valid
00102 //       Access: Public, Virtual
00103 //  Description: Returns true if the authorization challenge was
00104 //               correctly parsed and is usable, or false if there was
00105 //               some unsupported algorithm or some such requested by
00106 //               the server, rendering the challenge unmeetable.
00107 ////////////////////////////////////////////////////////////////////
00108 bool HTTPAuthorization::
00109 is_valid() {
00110   return true;
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: HTTPAuthorization::parse_authentication_schemes
00115 //       Access: Public, Static
00116 //  Description: Decodes the text following a WWW-Authenticate: or
00117 //               Proxy-Authenticate: header field.
00118 ////////////////////////////////////////////////////////////////////
00119 void HTTPAuthorization::
00120 parse_authentication_schemes(HTTPAuthorization::AuthenticationSchemes &schemes,
00121                              const string &field_value) {
00122   // This string will consist of one or more records of the form:
00123   //
00124   //  scheme token=value[,token=value[,...]]
00125   //
00126   // If there are multiple records, they will be comma-delimited,
00127   // which makes parsing just a bit tricky.
00128 
00129   // Start by skipping initial whitespace.
00130   size_t p = 0;
00131   while (p < field_value.length() && isspace(field_value[p])) {
00132     ++p;
00133   }
00134 
00135   if (p < field_value.length()) {
00136     size_t q = p;
00137     while (q < field_value.length() && !isspace(field_value[q])) {
00138       ++q;
00139     }
00140     // Here's our first scheme.
00141     string scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
00142     Tokens *tokens = &(schemes[scheme]);
00143     
00144     // Now pull off the tokens, one at a time.
00145     p = q + 1;
00146     while (p < field_value.length()) {
00147       q = p;
00148       while (q < field_value.length() && field_value[q] != '=' && 
00149              field_value[q] != ',' && !isspace(field_value[q])) {
00150         ++q;
00151       }
00152       if (field_value[q] == '=') {
00153         // This is a token.
00154         string token = HTTPChannel::downcase(field_value.substr(p, q - p));
00155         string value;
00156         p = scan_quoted_or_unquoted_string(value, field_value, q + 1);
00157         (*tokens)[token] = value;
00158 
00159         // Skip trailing whitespace and extra commas.
00160         while (p < field_value.length() && 
00161                (field_value[p] == ',' || isspace(field_value[p]))) {
00162           ++p;
00163         }
00164 
00165       } else {
00166         // This is not a token; it must be the start of a new scheme.
00167         scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
00168         tokens = &(schemes[scheme]);
00169         p = q + 1;
00170       }
00171     }
00172   }
00173 }
00174 
00175 ////////////////////////////////////////////////////////////////////
00176 //     Function: HTTPAuthorization::get_canonical_url
00177 //       Access: Public, Static
00178 //  Description: Returns the "canonical" URL corresponding to this
00179 //               URL.  This is the same URL with an explicit port
00180 //               indication, an explicit scheme, and a non-empty path,
00181 //               etc.
00182 ////////////////////////////////////////////////////////////////////
00183 URLSpec HTTPAuthorization::
00184 get_canonical_url(const URLSpec &url) {
00185   URLSpec canon = url;
00186   canon.set_scheme(canon.get_scheme());
00187   canon.set_username(string());
00188   canon.set_port(canon.get_port());
00189   canon.set_path(canon.get_path());
00190 
00191   return canon;
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: HTTPAuthorization::base64_encode
00196 //       Access: Public, Static
00197 //  Description: Returns the input string encoded using base64.  No
00198 //               respect is paid to maintaining a 76-char line length.
00199 ////////////////////////////////////////////////////////////////////
00200 string HTTPAuthorization::
00201 base64_encode(const string &s) {
00202   // Collect the string 3 bytes at a time into 24-bit words, then
00203   // output each word using 4 bytes.
00204   size_t num_words = (s.size() + 2) / 3;
00205   string result;
00206   result.reserve(num_words * 4);
00207   size_t p;
00208   for (p = 0; p + 2 < s.size(); p += 3) {
00209     unsigned int word = 
00210       ((unsigned)s[p] << 16) |
00211       ((unsigned)s[p + 1] << 8) |
00212       ((unsigned)s[p + 2]);
00213     result += base64_table[(word >> 18) & 0x3f];
00214     result += base64_table[(word >> 12) & 0x3f];
00215     result += base64_table[(word >> 6) & 0x3f];
00216     result += base64_table[(word) & 0x3f];
00217   }
00218   // What's left over?
00219   if (p < s.size()) {
00220     unsigned int word = ((unsigned)s[p] << 16);
00221     p++;
00222     if (p < s.size()) {
00223       word |= ((unsigned)s[p] << 8);
00224       p++;
00225       nassertr(p == s.size(), result);
00226 
00227       result += base64_table[(word >> 18) & 0x3f];
00228       result += base64_table[(word >> 12) & 0x3f];
00229       result += base64_table[(word >> 6) & 0x3f];
00230       result += '=';
00231     } else {
00232       result += base64_table[(word >> 18) & 0x3f];
00233       result += base64_table[(word >> 12) & 0x3f];
00234       result += '=';
00235       result += '=';
00236     }
00237   }
00238 
00239   return result;
00240 }
00241 
00242 ////////////////////////////////////////////////////////////////////
00243 //     Function: HTTPAuthorization::base64_decode
00244 //       Access: Public, Static
00245 //  Description: Returns the string decoded from base64.
00246 ////////////////////////////////////////////////////////////////////
00247 string HTTPAuthorization::
00248 base64_decode(const string &s) {
00249   // Build up the invert table if this is the first time.
00250   if (!got_base64_invert) {
00251     int i;
00252     for (i = 0; i < 128; i++) {
00253       base64_invert[i] = 0xff;
00254     }
00255 
00256     for (int i = 0; i < 64; i++) {
00257       base64_invert[(int)base64_table[i]] = i;
00258     }
00259 
00260     base64_invert[(int)'='] = 0;
00261 
00262     got_base64_invert = true;
00263   }
00264 
00265   // Collect the string 4 bytes at a time; decode this back into a
00266   // 24-bit word and output the 3 corresponding bytes.
00267   size_t num_words = s.size() / 4;
00268   string result;
00269   result.reserve(num_words * 3);
00270   size_t p;
00271   for (p = 0; p < s.size(); p += 4) {
00272     unsigned int c0 = base64_invert[s[p] & 0x7f];
00273     unsigned int c1 = base64_invert[s[p + 1] & 0x7f];
00274     unsigned int c2 = base64_invert[s[p + 2] & 0x7f];
00275     unsigned int c3 = base64_invert[s[p + 3] & 0x7f];
00276 
00277     unsigned int word = 
00278       (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
00279 
00280     result += (char)((word >> 16) & 0xff);
00281     if (s[p + 2] != '=') {
00282       result += (char)((word >> 8) & 0xff);
00283       if (s[p + 3] != '=') {
00284         result += (char)(word & 0xff);
00285       }
00286     }
00287   }
00288 
00289   return result;
00290 }
00291 
00292 ////////////////////////////////////////////////////////////////////
00293 //     Function: HTTPAuthorization::scan_quoted_or_unquoted_string
00294 //       Access: Protected, Static
00295 //  Description: Scans the string source beginning at character
00296 //               position start, to identify either the
00297 //               (space-delimited) unquoted string there, or the
00298 //               (quote-delimited) quoted string.  In either case,
00299 //               fills the string found into result, and returns the
00300 //               next character position after the string (or after
00301 //               its closing quote mark).
00302 ////////////////////////////////////////////////////////////////////
00303 size_t HTTPAuthorization::
00304 scan_quoted_or_unquoted_string(string &result, const string &source, 
00305                                size_t start) {
00306   result = string();
00307 
00308   if (start < source.length()) {
00309     if (source[start] == '"') {
00310       // Quoted string.
00311       size_t p = start + 1;
00312       while (p < source.length() && source[p] != '"') {
00313         if (source[p] == '\\') {
00314           // Backslash escapes.
00315           ++p;
00316           if (p < source.length()) {
00317             result += source[p];
00318             ++p;
00319           }
00320         } else {
00321           result += source[p];
00322           ++p;
00323         }
00324       }
00325       if (p < source.length()) {
00326         ++p;
00327       }
00328       return p;
00329     }
00330 
00331     // Unquoted string.
00332     size_t p = start;
00333     while (p < source.length() && source[p] != ',' && !isspace(source[p])) {
00334       result += source[p];
00335       ++p;
00336     }
00337 
00338     return p;
00339   }
00340 
00341   // Empty string.
00342   return start;
00343 }
00344 
00345 #endif  // HAVE_OPENSSL
 All Classes Functions Variables Enumerations