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/Net/HTTPAuthenticationParams.h"
17#include "Poco/Net/HTTPRequest.h"
18#include "Poco/Net/HTTPResponse.h"
19#include "Poco/Net/NetException.h"
20#include "Poco/String.h"
21#include "Poco/Ascii.h"
22#include "Poco/Exception.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::NTLM("NTLM");
70const std::string HTTPAuthenticationParams::WWW_AUTHENTICATE("WWW-Authenticate");
71const std::string HTTPAuthenticationParams::PROXY_AUTHENTICATE("Proxy-Authenticate");
72
73
74HTTPAuthenticationParams::HTTPAuthenticationParams()
75{
76}
77
78
79HTTPAuthenticationParams::HTTPAuthenticationParams(const std::string& authInfo)
80{
81 fromAuthInfo(authInfo);
82}
83
84
85HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPRequest& request)
86{
87 fromRequest(request);
88}
89
90
91HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPResponse& response, const std::string& header)
92{
93 fromResponse(response, header);
94}
95
96
97HTTPAuthenticationParams::~HTTPAuthenticationParams()
98{
99}
100
101
102HTTPAuthenticationParams& HTTPAuthenticationParams::operator = (const HTTPAuthenticationParams& authParams)
103{
104 NameValueCollection::operator = (authParams);
105
106 return *this;
107}
108
109
110void HTTPAuthenticationParams::fromAuthInfo(const std::string& authInfo)
111{
112 parse(authInfo.begin(), authInfo.end());
113}
114
115
116void HTTPAuthenticationParams::fromRequest(const HTTPRequest& request)
117{
118 std::string scheme;
119 std::string authInfo;
120
121 request.getCredentials(scheme, authInfo);
122
123 if (icompare(scheme, "Digest") != 0)
124 throw InvalidArgumentException("Could not parse non-Digest authentication information", scheme);
125
126 fromAuthInfo(authInfo);
127}
128
129
130void HTTPAuthenticationParams::fromResponse(const HTTPResponse& response, const std::string& header)
131{
132 NameValueCollection::ConstIterator it = response.find(header);
133 if (it == response.end())
134 throw NotAuthenticatedException("HTTP response has no authentication header");
135
136 bool found = false;
137 while (!found && it != response.end() && icompare(it->first, header) == 0)
138 {
139 const std::string& headerValue = it->second;
140 if (icompare(headerValue, 0, 6, "Basic ") == 0)
141 {
142 parse(headerValue.begin() + 6, headerValue.end());
143 found = true;
144 }
145 else if (icompare(headerValue, 0, 7, "Digest ") == 0)
146 {
147 parse(headerValue.begin() + 7, headerValue.end());
148 found = true;
149 }
150 else if (icompare(headerValue, 0, 5, "NTLM ") == 0)
151 {
152 set(NTLM, headerValue.substr(5));
153 found = true;
154 }
155 ++it;
156 }
157 if (!found) throw NotAuthenticatedException("No Basic, Digest or NTLM authentication header found");
158}
159
160
161const std::string& HTTPAuthenticationParams::getRealm() const
162{
163 return get(REALM);
164}
165
166
167void HTTPAuthenticationParams::setRealm(const std::string& realm)
168{
169 set(REALM, realm);
170}
171
172
173std::string HTTPAuthenticationParams::toString() const
174{
175 std::string result;
176 if (size() == 1 && find(NTLM) != end())
177 {
178 result = get(NTLM);
179 }
180 else
181 {
182 ConstIterator iter = begin();
183
184 if (iter != end())
185 {
186 formatParameter(result, iter->first, iter->second);
187 ++iter;
188 }
189
190 for (; iter != end(); ++iter)
191 {
192 result.append(", ");
193 formatParameter(result, iter->first, iter->second);
194 }
195 }
196 return result;
197}
198
199
200void HTTPAuthenticationParams::parse(std::string::const_iterator first, std::string::const_iterator last)
201{
202 enum State
203 {
204 STATE_INITIAL = 0x0100,
205 STATE_FINAL = 0x0200,
206
207 STATE_SPACE = STATE_INITIAL | 0,
208 STATE_TOKEN = 1,
209 STATE_EQUALS = 2,
210 STATE_VALUE = STATE_FINAL | 3,
211 STATE_VALUE_QUOTED = 4,
212 STATE_VALUE_ESCAPE = 5,
213 STATE_COMMA = STATE_FINAL | 6
214 };
215
216 int state = STATE_SPACE;
217 std::string token;
218 std::string value;
219
220 for (std::string::const_iterator it = first; it != last; ++it)
221 {
222 switch (state)
223 {
224 case STATE_SPACE:
225 if (Ascii::isAlphaNumeric(*it) || *it == '_' || *it == '-')
226 {
227 token += *it;
228 state = STATE_TOKEN;
229 }
230 else if (Ascii::isSpace(*it))
231 {
232 // Skip
233 }
234 else throw SyntaxException("Invalid authentication information");
235 break;
236
237 case STATE_TOKEN:
238 if (*it == '=')
239 {
240 state = STATE_EQUALS;
241 }
242 else if (Ascii::isAlphaNumeric(*it) || *it == '_')
243 {
244 token += *it;
245 }
246 else throw SyntaxException("Invalid authentication information");
247 break;
248
249 case STATE_EQUALS:
250 if (Ascii::isAlphaNumeric(*it) || *it == '_' || *it == '-')
251 {
252 value += *it;
253 state = STATE_VALUE;
254 }
255 else if (*it == '"')
256 {
257 state = STATE_VALUE_QUOTED;
258 }
259 else throw SyntaxException("Invalid authentication information");
260 break;
261
262 case STATE_VALUE_QUOTED:
263 if (*it == '\\')
264 {
265 state = STATE_VALUE_ESCAPE;
266 }
267 else if (*it == '"')
268 {
269 add(token, value);
270 token.clear();
271 value.clear();
272 state = STATE_COMMA;
273 }
274 else
275 {
276 value += *it;
277 }
278 break;
279
280 case STATE_VALUE_ESCAPE:
281 value += *it;
282 state = STATE_VALUE_QUOTED;
283 break;
284
285 case STATE_VALUE:
286 if (Ascii::isSpace(*it))
287 {
288 add(token, value);
289 token.clear();
290 value.clear();
291 state = STATE_COMMA;
292 }
293 else if (*it == ',')
294 {
295 add(token, value);
296 token.clear();
297 value.clear();
298 state = STATE_SPACE;
299 }
300 else
301 {
302 value += *it;
303 }
304 break;
305
306 case STATE_COMMA:
307 if (*it == ',')
308 {
309 state = STATE_SPACE;
310 }
311 else if (Ascii::isSpace(*it))
312 {
313 // Skip
314 }
315 else throw SyntaxException("Invalid authentication information");
316 break;
317 }
318 }
319
320 if (state == STATE_VALUE)
321 add(token, value);
322
323 if (!(state & STATE_FINAL))
324 throw SyntaxException("Invalid authentication information");
325}
326
327
328} } // namespace Poco::Net
329