Panda3D
Loading...
Searching...
No Matches
httpAuthorization.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file httpAuthorization.cxx
10 * @author drose
11 * @date 2002-10-22
12 */
13
14#include "httpAuthorization.h"
15#include "httpChannel.h"
16#include "urlSpec.h"
17
18#ifdef HAVE_OPENSSL
19
20using std::string;
21
22static const char base64_table[64] = {
23 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
24 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
25 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
26 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
27 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
28 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
29 'w', 'x', 'y', 'z', '0', '1', '2', '3',
30 '4', '5', '6', '7', '8', '9', '+', '/',
31};
32
33static unsigned char base64_invert[128];
34static bool got_base64_invert = false;
35
36/**
37 *
38 */
39HTTPAuthorization::
40HTTPAuthorization(const HTTPAuthorization::Tokens &tokens,
41 const URLSpec &url, bool is_proxy) {
42 Tokens::const_iterator ti;
43 ti = tokens.find("realm");
44 if (ti != tokens.end()) {
45 _realm = (*ti).second;
46 }
47
48 URLSpec canon = get_canonical_url(url);
49
50 ti = tokens.find("domain");
51 if (ti != tokens.end() && !is_proxy) {
52 // Now the domain consists of a series of space-separated URL prefixes.
53 const string &domain = (*ti).second;
54 size_t p = 0;
55 while (p < domain.length()) {
56 while (p < domain.length() && isspace(domain[p])) {
57 ++p;
58 }
59 size_t q = p;
60 while (q < domain.length() && !isspace(domain[q])) {
61 ++q;
62 }
63 if (q > p) {
64 string domain_str = domain.substr(p, q - p);
65 URLSpec domain_url(domain_str);
66 if (domain_url.has_server()) {
67 // A fully-qualified URL.
68 _domain.push_back(get_canonical_url(domain_url).get_url());
69 } else {
70 // A relative URL; relative to this path.
71 domain_url = canon;
72 domain_url.set_path(domain_str);
73 _domain.push_back(domain_url.get_url());
74 }
75 }
76 p = q;
77 }
78
79 } else {
80 // If no domain is defined by the server, use the supplied URL. Truncate
81 // it to the rightmost slash.
82 string canon_str = canon.get_url();
83 size_t slash = canon_str.rfind('/');
84 nassertv(slash != string::npos);
85 _domain.push_back(canon_str.substr(0, slash + 1));
86 }
87}
88
89/**
90 *
91 */
92HTTPAuthorization::
93~HTTPAuthorization() {
94}
95
96/**
97 * Returns true if the authorization challenge was correctly parsed and is
98 * usable, or false if there was some unsupported algorithm or some such
99 * requested by the server, rendering the challenge unmeetable.
100 */
101bool HTTPAuthorization::
102is_valid() {
103 return true;
104}
105
106/**
107 * Decodes the text following a WWW-Authenticate: or Proxy-Authenticate:
108 * header field.
109 */
110void HTTPAuthorization::
111parse_authentication_schemes(HTTPAuthorization::AuthenticationSchemes &schemes,
112 const string &field_value) {
113 // This string will consist of one or more records of the form: scheme
114 // token=value[,token=value[,...]] If there are multiple records, they will
115 // be comma-delimited, which makes parsing just a bit tricky.
116
117 // Start by skipping initial whitespace.
118 size_t p = 0;
119 while (p < field_value.length() && isspace(field_value[p])) {
120 ++p;
121 }
122
123 if (p < field_value.length()) {
124 size_t q = p;
125 while (q < field_value.length() && !isspace(field_value[q])) {
126 ++q;
127 }
128 // Here's our first scheme.
129 string scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
130 Tokens *tokens = &(schemes[scheme]);
131
132 // Now pull off the tokens, one at a time.
133 p = q + 1;
134 while (p < field_value.length()) {
135 q = p;
136 while (q < field_value.length() && field_value[q] != '=' &&
137 field_value[q] != ',' && !isspace(field_value[q])) {
138 ++q;
139 }
140 if (field_value[q] == '=') {
141 // This is a token.
142 string token = HTTPChannel::downcase(field_value.substr(p, q - p));
143 string value;
144 p = scan_quoted_or_unquoted_string(value, field_value, q + 1);
145 (*tokens)[token] = value;
146
147 // Skip trailing whitespace and extra commas.
148 while (p < field_value.length() &&
149 (field_value[p] == ',' || isspace(field_value[p]))) {
150 ++p;
151 }
152
153 } else {
154 // This is not a token; it must be the start of a new scheme.
155 scheme = HTTPChannel::downcase(field_value.substr(p, q - p));
156 tokens = &(schemes[scheme]);
157 p = q + 1;
158 }
159 }
160 }
161}
162
163/**
164 * Returns the "canonical" URL corresponding to this URL. This is the same
165 * URL with an explicit port indication, an explicit scheme, and a non-empty
166 * path, etc.
167 */
168URLSpec HTTPAuthorization::
169get_canonical_url(const URLSpec &url) {
170 URLSpec canon = url;
171 canon.set_scheme(canon.get_scheme());
172 canon.set_username(string());
173 canon.set_port(canon.get_port());
174 canon.set_path(canon.get_path());
175
176 return canon;
177}
178
179/**
180 * Returns the input string encoded using base64. No respect is paid to
181 * maintaining a 76-char line length.
182 */
183string HTTPAuthorization::
184base64_encode(const string &s) {
185 // Collect the string 3 bytes at a time into 24-bit words, then output each
186 // word using 4 bytes.
187 size_t num_words = (s.size() + 2) / 3;
188 string result;
189 result.reserve(num_words * 4);
190 size_t p;
191 for (p = 0; p + 2 < s.size(); p += 3) {
192 unsigned int word =
193 ((unsigned)s[p] << 16) |
194 ((unsigned)s[p + 1] << 8) |
195 ((unsigned)s[p + 2]);
196 result += base64_table[(word >> 18) & 0x3f];
197 result += base64_table[(word >> 12) & 0x3f];
198 result += base64_table[(word >> 6) & 0x3f];
199 result += base64_table[(word) & 0x3f];
200 }
201 // What's left over?
202 if (p < s.size()) {
203 unsigned int word = ((unsigned)s[p] << 16);
204 p++;
205 if (p < s.size()) {
206 word |= ((unsigned)s[p] << 8);
207 p++;
208 nassertr(p == s.size(), result);
209
210 result += base64_table[(word >> 18) & 0x3f];
211 result += base64_table[(word >> 12) & 0x3f];
212 result += base64_table[(word >> 6) & 0x3f];
213 result += '=';
214 } else {
215 result += base64_table[(word >> 18) & 0x3f];
216 result += base64_table[(word >> 12) & 0x3f];
217 result += '=';
218 result += '=';
219 }
220 }
221
222 return result;
223}
224
225/**
226 * Returns the string decoded from base64.
227 */
228string HTTPAuthorization::
229base64_decode(const string &s) {
230 // Build up the invert table if this is the first time.
231 if (!got_base64_invert) {
232 int i;
233 for (i = 0; i < 128; i++) {
234 base64_invert[i] = 0xff;
235 }
236
237 for (int i = 0; i < 64; i++) {
238 base64_invert[(int)base64_table[i]] = i;
239 }
240
241 base64_invert[(int)'='] = 0;
242
243 got_base64_invert = true;
244 }
245
246 // Collect the string 4 bytes at a time; decode this back into a 24-bit word
247 // and output the 3 corresponding bytes.
248 size_t num_words = s.size() / 4;
249 string result;
250 result.reserve(num_words * 3);
251 size_t p;
252 for (p = 0; p < s.size(); p += 4) {
253 unsigned int c0 = base64_invert[s[p] & 0x7f];
254 unsigned int c1 = base64_invert[s[p + 1] & 0x7f];
255 unsigned int c2 = base64_invert[s[p + 2] & 0x7f];
256 unsigned int c3 = base64_invert[s[p + 3] & 0x7f];
257
258 unsigned int word =
259 (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
260
261 result += (char)((word >> 16) & 0xff);
262 if (s[p + 2] != '=') {
263 result += (char)((word >> 8) & 0xff);
264 if (s[p + 3] != '=') {
265 result += (char)(word & 0xff);
266 }
267 }
268 }
269
270 return result;
271}
272
273/**
274 * Scans the string source beginning at character position start, to identify
275 * either the (space-delimited) unquoted string there, or the (quote-
276 * delimited) quoted string. In either case, fills the string found into
277 * result, and returns the next character position after the string (or after
278 * its closing quote mark).
279 */
280size_t HTTPAuthorization::
281scan_quoted_or_unquoted_string(string &result, const string &source,
282 size_t start) {
283 result = string();
284
285 if (start < source.length()) {
286 if (source[start] == '"') {
287 // Quoted string.
288 size_t p = start + 1;
289 while (p < source.length() && source[p] != '"') {
290 if (source[p] == '\\') {
291 // Backslash escapes.
292 ++p;
293 if (p < source.length()) {
294 result += source[p];
295 ++p;
296 }
297 } else {
298 result += source[p];
299 ++p;
300 }
301 }
302 if (p < source.length()) {
303 ++p;
304 }
305 return p;
306 }
307
308 // Unquoted string.
309 size_t p = start;
310 while (p < source.length() && source[p] != ',' && !isspace(source[p])) {
311 result += source[p];
312 ++p;
313 }
314
315 return p;
316 }
317
318 // Empty string.
319 return start;
320}
321
322#endif // HAVE_OPENSSL
A container for a URL, e.g.
Definition urlSpec.h:28
set_port
Replaces the port part of the URL specification.
Definition urlSpec.h:97
set_scheme
Replaces the scheme part of the URL specification.
Definition urlSpec.h:93
set_path
Replaces the path part of the URL specification.
Definition urlSpec.h:99
const std::string & get_url() const
Returns the complete URL specification.
Definition urlSpec.I:184
get_path
Returns the path specified by the URL, or "/" if no path is specified.
Definition urlSpec.h:99
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
Definition urlSpec.h:93
get_port
Returns the port number specified by the URL, or the default port if not specified.
Definition urlSpec.h:97
set_username
Replaces the username part of the URL specification.
Definition urlSpec.h:95
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.