Panda3D
|
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