1//
2// HTTPAuthenticationParams.cpp
3//
4// Library: Net
5// Package: HTTP
6// Module: HTTPAuthenticationParams
7//
8// Copyright (c) 2011, Anton V. Yabchinskiy (arn at bestmx dot ru).
9// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
10// and Contributors.
11//
12// SPDX-License-Identifier: BSL-1.0
13//
14
15
16#include "Poco/Exception.h"
17#include "Poco/Net/HTTPAuthenticationParams.h"
18#include "Poco/Net/HTTPRequest.h"
19#include "Poco/Net/HTTPResponse.h"
20#include "Poco/Net/NetException.h"
21#include "Poco/String.h"
22#include "Poco/Ascii.h"
23
24
25using Poco::icompare;
26using Poco::Ascii;
27
28
29namespace
30{
31 bool mustBeQuoted(const std::string& name)
32 {
33 return
34 icompare(name, "cnonce") == 0 ||
35 icompare(name, "domain") == 0 ||
36 icompare(name, "nonce") == 0 ||
37 icompare(name, "opaque") == 0 ||
38 icompare(name, "qop") == 0 ||
39 icompare(name, "realm") == 0 ||
40 icompare(name, "response") == 0 ||
41 icompare(name, "uri") == 0 ||
42 icompare(name, "username") == 0;
43 }
44
45
46 void formatParameter(std::string& result, const std::string& name, const std::string& value)
47 {
48 result += name;
49 result += '=';
50 if (mustBeQuoted(name))
51 {
52 result += '"';
53 result += value;
54 result += '"';
55 }
56 else
57 {
58 result += value;
59 }
60 }
61}
62
63
64namespace Poco {
65namespace Net {
66
67
68const std::string HTTPAuthenticationParams::REALM("realm");
69const std::string HTTPAuthenticationParams::WWW_AUTHENTICATE("WWW-Authenticate");
70const std::string HTTPAuthenticationParams::PROXY_AUTHENTICATE("Proxy-Authenticate");
71
72
73HTTPAuthenticationParams::HTTPAuthenticationParams()
74{
75}
76
77
78HTTPAuthenticationParams::HTTPAuthenticationParams(const std::string& authInfo)
79{
80 fromAuthInfo(authInfo);
81}
82
83
84HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPRequest& request)
85{
86 fromRequest(request);
87}
88
89
90HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPResponse& response, const std::string& header)
91{
92 fromResponse(response, header);
93}
94
95
96HTTPAuthenticationParams::~HTTPAuthenticationParams()
97{
98}
99
100
101HTTPAuthenticationParams& HTTPAuthenticationParams::operator = (const HTTPAuthenticationParams& authParams)
102{
103 NameValueCollection::operator = (authParams);
104
105 return *this;
106}
107
108
109void HTTPAuthenticationParams::fromAuthInfo(const std::string& authInfo)
110{
111 parse(authInfo.begin(), authInfo.end());
112}
113
114
115void HTTPAuthenticationParams::fromRequest(const HTTPRequest& request)
116{
117 std::string scheme;
118 std::string authInfo;
119
120 request.getCredentials(scheme, authInfo);
121
122 if (icompare(scheme, "Digest") != 0)
123 throw InvalidArgumentException("Could not parse non-Digest authentication information", scheme);
124
125 fromAuthInfo(authInfo);
126}
127
128
129void HTTPAuthenticationParams::fromResponse(const HTTPResponse& response, const std::string& header)
130{
131 NameValueCollection::ConstIterator it = response.find(header);
132 if (it == response.end())
133 throw NotAuthenticatedException("HTTP response has no authentication header");
134
135 bool found = false;
136 while (!found && it != response.end() && icompare(it->first, header) == 0)
137 {
138 const std::string& header = it->second;
139 if (icompare(header, 0, 6, "Basic ") == 0)
140 {
141 parse(header.begin() + 6, header.end());
142 found = true;
143 }
144 else if (icompare(header, 0, 7, "Digest ") == 0)
145 {
146 parse(header.begin() + 7, header.end());
147 found = true;
148 }
149 ++it;
150 }
151 if (!found) throw NotAuthenticatedException("No Basic or Digest authentication header found");
152}
153
154
155const std::string& HTTPAuthenticationParams::getRealm() const
156{
157 return get(REALM);
158}
159
160
161void HTTPAuthenticationParams::setRealm(const std::string& realm)
162{
163 set(REALM, realm);
164}
165
166
167std::string HTTPAuthenticationParams::toString() const
168{
169 ConstIterator iter = begin();
170 std::string result;
171
172 if (iter != end())
173 {
174 formatParameter(result, iter->first, iter->second);
175 ++iter;
176 }
177
178 for (; iter != end(); ++iter)
179 {
180 result.append(", ");
181 formatParameter(result, iter->first, iter->second);
182 }
183
184 return result;
185}
186
187
188void HTTPAuthenticationParams::parse(std::string::const_iterator first, std::string::const_iterator last)
189{
190 enum State
191 {
192 STATE_INITIAL = 0x0100,
193 STATE_FINAL = 0x0200,
194
195 STATE_SPACE = STATE_INITIAL | 0,
196 STATE_TOKEN = 1,
197 STATE_EQUALS = 2,
198 STATE_VALUE = STATE_FINAL | 3,
199 STATE_VALUE_QUOTED = 4,
200 STATE_VALUE_ESCAPE = 5,
201 STATE_COMMA = STATE_FINAL | 6
202 };
203
204 int state = STATE_SPACE;
205 std::string token;
206 std::string value;
207
208 for (std::string::const_iterator it = first; it != last; ++it)
209 {
210 switch (state)
211 {
212 case STATE_SPACE:
213 if (Ascii::isAlphaNumeric(*it) || *it == '_')
214 {
215 token += *it;
216 state = STATE_TOKEN;
217 }
218 else if (Ascii::isSpace(*it))
219 {
220 // Skip
221 }
222 else throw SyntaxException("Invalid authentication information");
223 break;
224
225 case STATE_TOKEN:
226 if (*it == '=')
227 {
228 state = STATE_EQUALS;
229 }
230 else if (Ascii::isAlphaNumeric(*it) || *it == '_')
231 {
232 token += *it;
233 }
234 else throw SyntaxException("Invalid authentication information");
235 break;
236
237 case STATE_EQUALS:
238 if (Ascii::isAlphaNumeric(*it) || *it == '_')
239 {
240 value += *it;
241 state = STATE_VALUE;
242 }
243 else if (*it == '"')
244 {
245 state = STATE_VALUE_QUOTED;
246 }
247 else throw SyntaxException("Invalid authentication information");
248 break;
249
250 case STATE_VALUE_QUOTED:
251 if (*it == '\\')
252 {
253 state = STATE_VALUE_ESCAPE;
254 }
255 else if (*it == '"')
256 {
257 add(token, value);
258 token.clear();
259 value.clear();
260 state = STATE_COMMA;
261 }
262 else
263 {
264 value += *it;
265 }
266 break;
267
268 case STATE_VALUE_ESCAPE:
269 value += *it;
270 state = STATE_VALUE_QUOTED;
271 break;
272
273 case STATE_VALUE:
274 if (Ascii::isSpace(*it))
275 {
276 add(token, value);
277 token.clear();
278 value.clear();
279 state = STATE_COMMA;
280 }
281 else if (*it == ',')
282 {
283 add(token, value);
284 token.clear();
285 value.clear();
286 state = STATE_SPACE;
287 }
288 else
289 {
290 value += *it;
291 }
292 break;
293
294 case STATE_COMMA:
295 if (*it == ',')
296 {
297 state = STATE_SPACE;
298 }
299 else if (Ascii::isSpace(*it))
300 {
301 // Skip
302 }
303 else throw SyntaxException("Invalid authentication information");
304 break;
305 }
306 }
307
308 if (state == STATE_VALUE)
309 add(token, value);
310
311 if (!(state & STATE_FINAL))
312 throw SyntaxException("Invalid authentication information");
313}
314
315
316} } // namespace Poco::Net
317