| 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 |  | 
| 25 | using Poco::icompare; | 
| 26 | using Poco::Ascii; | 
| 27 |  | 
| 28 |  | 
| 29 | namespace | 
| 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 (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 |  | 
| 64 | namespace Poco { | 
| 65 | namespace Net { | 
| 66 |  | 
| 67 |  | 
| 68 | const std::string HTTPAuthenticationParams::REALM("realm" ); | 
| 69 | const std::string HTTPAuthenticationParams::NTLM("NTLM" ); | 
| 70 | const std::string HTTPAuthenticationParams::WWW_AUTHENTICATE("WWW-Authenticate" ); | 
| 71 | const std::string HTTPAuthenticationParams::PROXY_AUTHENTICATE("Proxy-Authenticate" ); | 
| 72 |  | 
| 73 |  | 
| 74 | HTTPAuthenticationParams::HTTPAuthenticationParams() | 
| 75 | { | 
| 76 | } | 
| 77 |  | 
| 78 |  | 
| 79 | HTTPAuthenticationParams::HTTPAuthenticationParams(const std::string& authInfo) | 
| 80 | { | 
| 81 | 	fromAuthInfo(authInfo); | 
| 82 | } | 
| 83 |  | 
| 84 |  | 
| 85 | HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPRequest& request) | 
| 86 | { | 
| 87 | 	fromRequest(request); | 
| 88 | } | 
| 89 |  | 
| 90 |  | 
| 91 | HTTPAuthenticationParams::HTTPAuthenticationParams(const HTTPResponse& response, const std::string& ) | 
| 92 | { | 
| 93 | 	fromResponse(response, header); | 
| 94 | } | 
| 95 |  | 
| 96 |  | 
| 97 | HTTPAuthenticationParams::~HTTPAuthenticationParams() | 
| 98 | { | 
| 99 | } | 
| 100 |  | 
| 101 |  | 
| 102 | HTTPAuthenticationParams& HTTPAuthenticationParams::operator = (const HTTPAuthenticationParams& authParams) | 
| 103 | { | 
| 104 | 	NameValueCollection::operator = (authParams); | 
| 105 |  | 
| 106 | 	return *this; | 
| 107 | } | 
| 108 |  | 
| 109 |  | 
| 110 | void HTTPAuthenticationParams::fromAuthInfo(const std::string& authInfo) | 
| 111 | { | 
| 112 | 	parse(authInfo.begin(), authInfo.end()); | 
| 113 | } | 
| 114 |  | 
| 115 |  | 
| 116 | void 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 |  | 
| 130 | void HTTPAuthenticationParams::fromResponse(const HTTPResponse& response, const std::string& ) | 
| 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&  = 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 |  | 
| 161 | const std::string& HTTPAuthenticationParams::getRealm() const | 
| 162 | { | 
| 163 | 	return get(REALM); | 
| 164 | } | 
| 165 |  | 
| 166 |  | 
| 167 | void HTTPAuthenticationParams::setRealm(const std::string& realm) | 
| 168 | { | 
| 169 | 	set(REALM, realm); | 
| 170 | } | 
| 171 |  | 
| 172 |  | 
| 173 | std::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 |  | 
| 200 | void 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 |  |