1 // distribution boxbackup-0.11_trunk_2979 (svn version: 2979)
2 // Box Backup, http://www.boxbackup.org/
3 //
4 // Copyright (c) 2003-2010, Ben Summers and contributors.
5 // All rights reserved.
6 //
7 // Note that this project uses mixed licensing. Any file with this license
8 // attached, or where the code LICENSE-DUAL appears on the first line, falls
9 // under this license. See the file COPYING.txt for more information.
10 //
11 // This file is dual licensed. You may use and distribute it providing that you
12 // comply EITHER with the terms of the BSD license, OR the GPL license. It is
13 // not necessary to comply with both licenses, only one.
14 //
15 // The BSD license option follows:
16 //
17 // Redistribution and use in source and binary forms, with or without
18 // modification, are permitted provided that the following conditions are met:
19 //
20 // 1. Redistributions of source code must retain the above copyright
21 //    notice, this list of conditions and the following disclaimer.
22 //
23 // 2. Redistributions in binary form must reproduce the above copyright
24 //    notice, this list of conditions and the following disclaimer in the
25 //    documentation and/or other materials provided with the distribution.
26 //
27 // 3. Neither the name of the Box Backup nor the names of its contributors may
28 //    be used to endorse or promote products derived from this software without
29 //    specific prior written permission.
30 //
31 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
32 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
35 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 //
42 // [http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29]
43 //
44 // The GPL license option follows:
45 //
46 // This program is free software; you can redistribute it and/or
47 // modify it under the terms of the GNU General Public License
48 // as published by the Free Software Foundation; either version 2
49 // of the License, or (at your option) any later version.
50 //
51 // This program is distributed in the hope that it will be useful,
52 // but WITHOUT ANY WARRANTY; without even the implied warranty of
53 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
54 // GNU General Public License for more details.
55 //
56 // You should have received a copy of the GNU General Public License
57 // along with this program; if not, write to the Free Software
58 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
59 //
60 // [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
61 // --------------------------------------------------------------------------
62 //
63 // File
64 //		Name:    HTTPRequest.cpp
65 //		Purpose: Request object for HTTP connections
66 //		Created: 26/3/04
67 //
68 // --------------------------------------------------------------------------
69 
70 #include "Box.h"
71 
72 #include <string.h>
73 #include <strings.h>
74 #include <stdlib.h>
75 #include <stdio.h>
76 
77 #include "HTTPRequest.h"
78 #include "HTTPResponse.h"
79 #include "HTTPQueryDecoder.h"
80 #include "autogen_HTTPException.h"
81 #include "IOStream.h"
82 #include "IOStreamGetLine.h"
83 #include "Logging.h"
84 
85 #include "MemLeakFindOn.h"
86 
87 #define MAX_CONTENT_SIZE	(128*1024)
88 
89 #define ENSURE_COOKIE_JAR_ALLOCATED \
90 	if(mpCookies == 0) {mpCookies = new CookieJar_t;}
91 
92 
93 
94 // --------------------------------------------------------------------------
95 //
96 // Function
97 //		Name:    HTTPRequest::HTTPRequest()
98 //		Purpose: Constructor
99 //		Created: 26/3/04
100 //
101 // --------------------------------------------------------------------------
HTTPRequest()102 HTTPRequest::HTTPRequest()
103 	: mMethod(Method_UNINITIALISED),
104 	  mHostPort(80),	// default if not specified
105 	  mHTTPVersion(0),
106 	  mContentLength(-1),
107 	  mpCookies(0),
108 	  mClientKeepAliveRequested(false),
109 	  mExpectContinue(false),
110 	  mpStreamToReadFrom(NULL)
111 {
112 }
113 
114 
115 // --------------------------------------------------------------------------
116 //
117 // Function
118 //		Name:    HTTPRequest::HTTPRequest(enum Method,
119 //			 const std::string&)
120 //		Purpose: Alternate constructor for hand-crafted requests
121 //		Created: 03/01/09
122 //
123 // --------------------------------------------------------------------------
HTTPRequest(enum Method method,const std::string & rURI)124 HTTPRequest::HTTPRequest(enum Method method, const std::string& rURI)
125 	: mMethod(method),
126 	  mRequestURI(rURI),
127 	  mHostPort(80), // default if not specified
128 	  mHTTPVersion(HTTPVersion_1_1),
129 	  mContentLength(-1),
130 	  mpCookies(0),
131 	  mClientKeepAliveRequested(false),
132 	  mExpectContinue(false),
133 	  mpStreamToReadFrom(NULL)
134 {
135 }
136 
137 
138 
139 // --------------------------------------------------------------------------
140 //
141 // Function
142 //		Name:    HTTPRequest::~HTTPRequest()
143 //		Purpose: Destructor
144 //		Created: 26/3/04
145 //
146 // --------------------------------------------------------------------------
~HTTPRequest()147 HTTPRequest::~HTTPRequest()
148 {
149 	// Clean up any cookies
150 	if(mpCookies != 0)
151 	{
152 		delete mpCookies;
153 		mpCookies = 0;
154 	}
155 }
156 
157 
158 // --------------------------------------------------------------------------
159 //
160 // Function
161 //		Name:    HTTPRequest::Receive(IOStreamGetLine &, int)
162 //		Purpose: Read the request from an IOStreamGetLine (and
163 //			 attached stream).
164 //			 Returns false if there was no valid request,
165 //			 probably due to a kept-alive connection closing.
166 //		Created: 26/3/04
167 //
168 // --------------------------------------------------------------------------
Receive(IOStreamGetLine & rGetLine,int Timeout)169 bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout)
170 {
171 	// Check caller's logic
172 	if(mMethod != Method_UNINITIALISED)
173 	{
174 		THROW_EXCEPTION(HTTPException, RequestAlreadyBeenRead)
175 	}
176 
177 	// Read the first line, which is of a different format to the rest of the lines
178 	std::string requestLine;
179 	if(!rGetLine.GetLine(requestLine, false /* no preprocessing */, Timeout))
180 	{
181 		// Didn't get the request line, probably end of connection which had been kept alive
182 		return false;
183 	}
184 	BOX_TRACE("Request line: " << requestLine);
185 
186 	// Check the method
187 	size_t p = 0;	// current position in string
188 	p = requestLine.find(' '); // end of first word
189 
190 	if (p == std::string::npos)
191 	{
192 		// No terminating space, looks bad
193 		p = requestLine.size();
194 	}
195 	else
196 	{
197 		mHttpVerb = requestLine.substr(0, p);
198 		if (mHttpVerb == "GET")
199 		{
200 			mMethod = Method_GET;
201 		}
202 		else if (mHttpVerb == "HEAD")
203 		{
204 			mMethod = Method_HEAD;
205 		}
206 		else if (mHttpVerb == "POST")
207 		{
208 			mMethod = Method_POST;
209 		}
210 		else if (mHttpVerb == "PUT")
211 		{
212 			mMethod = Method_PUT;
213 		}
214 		else
215 		{
216 			mMethod = Method_UNKNOWN;
217 		}
218 	}
219 
220 	// Skip spaces to find URI
221 	const char *requestLinePtr = requestLine.c_str();
222 	while(requestLinePtr[p] != '\0' && requestLinePtr[p] == ' ')
223 	{
224 		++p;
225 	}
226 
227 	// Check there's a URI following...
228 	if(requestLinePtr[p] == '\0')
229 	{
230 		// Didn't get the request line, probably end of connection which had been kept alive
231 		return false;
232 	}
233 
234 	// Read the URI, unescaping any %XX hex codes
235 	while(requestLinePtr[p] != ' ' && requestLinePtr[p] != '\0')
236 	{
237 		// End of URI, on to query string?
238 		if(requestLinePtr[p] == '?')
239 		{
240 			// Put the rest into the query string, without escaping anything
241 			++p;
242 			while(requestLinePtr[p] != ' ' && requestLinePtr[p] != '\0')
243 			{
244 				mQueryString += requestLinePtr[p];
245 				++p;
246 			}
247 			break;
248 		}
249 		// Needs unescaping?
250 		else if(requestLinePtr[p] == '+')
251 		{
252 			mRequestURI += ' ';
253 		}
254 		else if(requestLinePtr[p] == '%')
255 		{
256 			// Be tolerant about this... bad things are silently accepted,
257 			// rather than throwing an error.
258 			char code[4] = {0,0,0,0};
259 			code[0] = requestLinePtr[++p];
260 			if(code[0] != '\0')
261 			{
262 				code[1] = requestLinePtr[++p];
263 			}
264 
265 			// Convert into a char code
266 			long c = ::strtol(code, NULL, 16);
267 
268 			// Accept it?
269 			if(c > 0 && c <= 255)
270 			{
271 				mRequestURI += (char)c;
272 			}
273 		}
274 		else
275 		{
276 			// Simple copy of character
277 			mRequestURI += requestLinePtr[p];
278 		}
279 
280 		++p;
281 	}
282 
283 	// End of URL?
284 	if(requestLinePtr[p] == '\0')
285 	{
286 		// Assume HTTP 0.9
287 		mHTTPVersion = HTTPVersion_0_9;
288 	}
289 	else
290 	{
291 		// Skip any more spaces
292 		while(requestLinePtr[p] != '\0' && requestLinePtr[p] == ' ')
293 		{
294 			++p;
295 		}
296 
297 		// Check to see if there's the right string next...
298 		if(::strncmp(requestLinePtr + p, "HTTP/", 5) == 0)
299 		{
300 			// Find the version numbers
301 			int major, minor;
302 			if(::sscanf(requestLinePtr + p + 5, "%d.%d", &major, &minor) != 2)
303 			{
304 				THROW_EXCEPTION(HTTPException, BadRequest)
305 			}
306 
307 			// Store version
308 			mHTTPVersion = (major * HTTPVersion__MajorMultiplier) + minor;
309 		}
310 		else
311 		{
312 			// Not good -- wrong string found
313 			THROW_EXCEPTION(HTTPException, BadRequest)
314 		}
315 	}
316 
317 	BOX_TRACE("HTTPRequest: method=" << mMethod << ", uri=" <<
318 		mRequestURI << ", version=" << mHTTPVersion);
319 
320 	// If HTTP 1.1 or greater, assume keep-alive
321 	if(mHTTPVersion >= HTTPVersion_1_1)
322 	{
323 		mClientKeepAliveRequested = true;
324 	}
325 
326 	// Decode query string?
327 	if((mMethod == Method_GET || mMethod == Method_HEAD) && !mQueryString.empty())
328 	{
329 		HTTPQueryDecoder decoder(mQuery);
330 		decoder.DecodeChunk(mQueryString.c_str(), mQueryString.size());
331 		decoder.Finish();
332 	}
333 
334 	// Now parse the headers
335 	ParseHeaders(rGetLine, Timeout);
336 
337 	std::string expected;
338 	if (GetHeader("Expect", &expected))
339 	{
340 		if (expected == "100-continue")
341 		{
342 			mExpectContinue = true;
343 		}
344 	}
345 
346 	// Parse form data?
347 	if(mMethod == Method_POST && mContentLength >= 0)
348 	{
349 		// Too long? Don't allow people to be nasty by sending lots of data
350 		if(mContentLength > MAX_CONTENT_SIZE)
351 		{
352 			THROW_EXCEPTION(HTTPException, POSTContentTooLong)
353 		}
354 
355 		// Some data in the request to follow, parsing it bit by bit
356 		HTTPQueryDecoder decoder(mQuery);
357 		// Don't forget any data left in the GetLine object
358 		int fromBuffer = rGetLine.GetSizeOfBufferedData();
359 		if(fromBuffer > mContentLength) fromBuffer = mContentLength;
360 		if(fromBuffer > 0)
361 		{
362 			BOX_TRACE("Decoding " << fromBuffer << " bytes of "
363 				"data from getline buffer");
364 			decoder.DecodeChunk((const char *)rGetLine.GetBufferedData(), fromBuffer);
365 			// And tell the getline object to ignore the data we just used
366 			rGetLine.IgnoreBufferedData(fromBuffer);
367 		}
368 		// Then read any more data, as required
369 		int bytesToGo = mContentLength - fromBuffer;
370 		while(bytesToGo > 0)
371 		{
372 			char buf[4096];
373 			int toRead = sizeof(buf);
374 			if(toRead > bytesToGo) toRead = bytesToGo;
375 			IOStream &rstream(rGetLine.GetUnderlyingStream());
376 			int r = rstream.Read(buf, toRead, Timeout);
377 			if(r == 0)
378 			{
379 				// Timeout, just error
380 				THROW_EXCEPTION(HTTPException, RequestReadFailed)
381 			}
382 			decoder.DecodeChunk(buf, r);
383 			bytesToGo -= r;
384 		}
385 		// Finish off
386 		decoder.Finish();
387 	}
388 	else if (mContentLength > 0)
389 	{
390 		IOStream::pos_type bytesToCopy = rGetLine.GetSizeOfBufferedData();
391 		if (bytesToCopy > mContentLength)
392 		{
393 			bytesToCopy = mContentLength;
394 		}
395 		Write(rGetLine.GetBufferedData(), bytesToCopy);
396 		SetForReading();
397 		mpStreamToReadFrom = &(rGetLine.GetUnderlyingStream());
398 	}
399 
400 	return true;
401 }
402 
ReadContent(IOStream & rStreamToWriteTo)403 void HTTPRequest::ReadContent(IOStream& rStreamToWriteTo)
404 {
405 	Seek(0, SeekType_Absolute);
406 
407 	CopyStreamTo(rStreamToWriteTo);
408 	IOStream::pos_type bytesCopied = GetSize();
409 
410 	while (bytesCopied < mContentLength)
411 	{
412 		char buffer[1024];
413 		IOStream::pos_type bytesToCopy = sizeof(buffer);
414 		if (bytesToCopy > mContentLength - bytesCopied)
415 		{
416 			bytesToCopy = mContentLength - bytesCopied;
417 		}
418 		bytesToCopy = mpStreamToReadFrom->Read(buffer, bytesToCopy);
419 		rStreamToWriteTo.Write(buffer, bytesToCopy);
420 		bytesCopied += bytesToCopy;
421 	}
422 }
423 
424 // --------------------------------------------------------------------------
425 //
426 // Function
427 //		Name:    HTTPRequest::Send(IOStream &, int)
428 //		Purpose: Write the request to an IOStream using HTTP.
429 //		Created: 03/01/09
430 //
431 // --------------------------------------------------------------------------
Send(IOStream & rStream,int Timeout,bool ExpectContinue)432 bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue)
433 {
434 	switch (mMethod)
435 	{
436 	case Method_UNINITIALISED:
437 		THROW_EXCEPTION(HTTPException, RequestNotInitialised); break;
438 	case Method_UNKNOWN:
439 		THROW_EXCEPTION(HTTPException, BadRequest); break;
440 	case Method_GET:
441 		rStream.Write("GET"); break;
442 	case Method_HEAD:
443 		rStream.Write("HEAD"); break;
444 	case Method_POST:
445 		rStream.Write("POST"); break;
446 	case Method_PUT:
447 		rStream.Write("PUT"); break;
448 	}
449 
450 	rStream.Write(" ");
451 	rStream.Write(mRequestURI.c_str());
452 	rStream.Write(" ");
453 
454 	switch (mHTTPVersion)
455 	{
456 	case HTTPVersion_0_9: rStream.Write("HTTP/0.9"); break;
457 	case HTTPVersion_1_0: rStream.Write("HTTP/1.0"); break;
458 	case HTTPVersion_1_1: rStream.Write("HTTP/1.1"); break;
459 	default:
460 		THROW_EXCEPTION(HTTPException, NotImplemented);
461 	}
462 
463 	rStream.Write("\n");
464 	std::ostringstream oss;
465 
466 	if (mContentLength != -1)
467 	{
468 		oss << "Content-Length: " << mContentLength << "\n";
469 	}
470 
471 	if (mContentType != "")
472 	{
473 		oss << "Content-Type: " << mContentType << "\n";
474 	}
475 
476 	if (mHostName != "")
477 	{
478 		if (mHostPort != 80)
479 		{
480 			oss << "Host: " << mHostName << ":" << mHostPort <<
481 				"\n";
482 		}
483 		else
484 		{
485 			oss << "Host: " << mHostName << "\n";
486 		}
487 	}
488 
489 	if (mpCookies)
490 	{
491 		THROW_EXCEPTION(HTTPException, NotImplemented);
492 	}
493 
494 	if (mClientKeepAliveRequested)
495 	{
496 		oss << "Connection: keep-alive\n";
497 	}
498 	else
499 	{
500 		oss << "Connection: close\n";
501 	}
502 
503 	for (std::vector<Header>::iterator i = mExtraHeaders.begin();
504 		i != mExtraHeaders.end(); i++)
505 	{
506 		oss << i->first << ": " << i->second << "\n";
507 	}
508 
509 	if (ExpectContinue)
510 	{
511 		oss << "Expect: 100-continue\n";
512 	}
513 
514 	rStream.Write(oss.str().c_str());
515 	rStream.Write("\n");
516 
517 	return true;
518 }
519 
SendWithStream(IOStream & rStreamToSendTo,int Timeout,IOStream * pStreamToSend,HTTPResponse & rResponse)520 void HTTPRequest::SendWithStream(IOStream &rStreamToSendTo, int Timeout,
521 	IOStream* pStreamToSend, HTTPResponse& rResponse)
522 {
523 	IOStream::pos_type size = pStreamToSend->BytesLeftToRead();
524 
525 	if (size != IOStream::SizeOfStreamUnknown)
526 	{
527 		mContentLength = size;
528 	}
529 
530 	Send(rStreamToSendTo, Timeout, true);
531 
532 	rResponse.Receive(rStreamToSendTo, Timeout);
533 	if (rResponse.GetResponseCode() != 100)
534 	{
535 		// bad response, abort now
536 		return;
537 	}
538 
539 	pStreamToSend->CopyStreamTo(rStreamToSendTo, Timeout);
540 
541 	// receive the final response
542 	rResponse.Receive(rStreamToSendTo, Timeout);
543 }
544 
545 // --------------------------------------------------------------------------
546 //
547 // Function
548 //		Name:    HTTPRequest::ParseHeaders(IOStreamGetLine &, int)
549 //		Purpose: Private. Parse the headers of the request
550 //		Created: 26/3/04
551 //
552 // --------------------------------------------------------------------------
ParseHeaders(IOStreamGetLine & rGetLine,int Timeout)553 void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
554 {
555 	std::string header;
556 	bool haveHeader = false;
557 	while(true)
558 	{
559 		if(rGetLine.IsEOF())
560 		{
561 			// Header terminates unexpectedly
562 			THROW_EXCEPTION(HTTPException, BadRequest)
563 		}
564 
565 		std::string currentLine;
566 		if(!rGetLine.GetLine(currentLine, false /* no preprocess */, Timeout))
567 		{
568 			// Timeout
569 			THROW_EXCEPTION(HTTPException, RequestReadFailed)
570 		}
571 
572 		// Is this a continuation of the previous line?
573 		bool processHeader = haveHeader;
574 		if(!currentLine.empty() && (currentLine[0] == ' ' || currentLine[0] == '\t'))
575 		{
576 			// A continuation, don't process anything yet
577 			processHeader = false;
578 		}
579 		//TRACE3("%d:%d:%s\n", processHeader, haveHeader, currentLine.c_str());
580 
581 		// Parse the header -- this will actually process the header
582 		// from the previous run around the loop.
583 		if(processHeader)
584 		{
585 			// Find where the : is in the line
586 			const char *h = header.c_str();
587 			int p = 0;
588 			while(h[p] != '\0' && h[p] != ':')
589 			{
590 				++p;
591 			}
592 			// Skip white space
593 			int dataStart = p + 1;
594 			while(h[dataStart] == ' ' || h[dataStart] == '\t')
595 			{
596 				++dataStart;
597 			}
598 
599 			std::string header_name(ToLowerCase(std::string(h,
600 				p)));
601 
602 			if (header_name == "content-length")
603 			{
604 				// Decode number
605 				long len = ::strtol(h + dataStart, NULL, 10);	// returns zero in error case, this is OK
606 				if(len < 0) len = 0;
607 				// Store
608 				mContentLength = len;
609 			}
610 			else if (header_name == "content-type")
611 			{
612 				// Store rest of string as content type
613 				mContentType = h + dataStart;
614 			}
615 			else if (header_name == "host")
616 			{
617 				// Store host header
618 				mHostName = h + dataStart;
619 
620 				// Is there a port number to split off?
621 				std::string::size_type colon = mHostName.find_first_of(':');
622 				if(colon != std::string::npos)
623 				{
624 					// There's a port in the string... attempt to turn it into an int
625 					mHostPort = ::strtol(mHostName.c_str() + colon + 1, 0, 10);
626 
627 					// Truncate the string to just the hostname
628 					mHostName = mHostName.substr(0, colon);
629 
630 					BOX_TRACE("Host: header, hostname = " <<
631 						"'" << mHostName << "', host "
632 						"port = " << mHostPort);
633 				}
634 			}
635 			else if (header_name == "cookie")
636 			{
637 				// Parse cookies
638 				ParseCookies(header, dataStart);
639 			}
640 			else if (header_name == "connection")
641 			{
642 				// Connection header, what is required?
643 				const char *v = h + dataStart;
644 				if(::strcasecmp(v, "close") == 0)
645 				{
646 					mClientKeepAliveRequested = false;
647 				}
648 				else if(::strcasecmp(v, "keep-alive") == 0)
649 				{
650 					mClientKeepAliveRequested = true;
651 				}
652 				// else don't understand, just assume default for protocol version
653 			}
654 			else
655 			{
656 				mExtraHeaders.push_back(Header(header_name,
657 					h + dataStart));
658 			}
659 
660 			// Unset have header flag, as it's now been processed
661 			haveHeader = false;
662 		}
663 
664 		// Store the chunk of header the for next time round
665 		if(haveHeader)
666 		{
667 			header += currentLine;
668 		}
669 		else
670 		{
671 			header = currentLine;
672 			haveHeader = true;
673 		}
674 
675 		// End of headers?
676 		if(currentLine.empty())
677 		{
678 			// All done!
679 			break;
680 		}
681 	}
682 }
683 
684 
685 // --------------------------------------------------------------------------
686 //
687 // Function
688 //		Name:    HTTPRequest::ParseCookies(const std::string &, int)
689 //		Purpose: Parse the cookie header
690 //		Created: 20/8/04
691 //
692 // --------------------------------------------------------------------------
ParseCookies(const std::string & rHeader,int DataStarts)693 void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts)
694 {
695 	const char *data = rHeader.c_str() + DataStarts;
696 	const char *pos = data;
697 	const char *itemStart = pos;
698 	std::string name;
699 
700 	enum
701 	{
702 		s_NAME, s_VALUE, s_VALUE_QUOTED, s_FIND_NEXT_NAME
703 	} state = s_NAME;
704 
705 	do
706 	{
707 		switch(state)
708 		{
709 		case s_NAME:
710 			{
711 				if(*pos == '=')
712 				{
713 					// Found the name. Store
714 					name.assign(itemStart, pos - itemStart);
715 					// Looking at values now
716 					state = s_VALUE;
717 					if((*(pos + 1)) == '"')
718 					{
719 						// Actually it's a quoted value, skip over that
720 						++pos;
721 						state = s_VALUE_QUOTED;
722 					}
723 					// Record starting point for this item
724 					itemStart = pos + 1;
725 				}
726 			}
727 			break;
728 
729 		case s_VALUE:
730 			{
731 				if(*pos == ';' || *pos == ',' || *pos == '\0')
732 				{
733 					// Name ends
734 					ENSURE_COOKIE_JAR_ALLOCATED
735 					std::string value(itemStart, pos - itemStart);
736 					(*mpCookies)[name] = value;
737 					// And move to the waiting stage
738 					state = s_FIND_NEXT_NAME;
739 				}
740 			}
741 			break;
742 
743 		case s_VALUE_QUOTED:
744 			{
745 				if(*pos == '"')
746 				{
747 					// That'll do nicely, save it
748 					ENSURE_COOKIE_JAR_ALLOCATED
749 					std::string value(itemStart, pos - itemStart);
750 					(*mpCookies)[name] = value;
751 					// And move to the waiting stage
752 					state = s_FIND_NEXT_NAME;
753 				}
754 			}
755 			break;
756 
757 		case s_FIND_NEXT_NAME:
758 			{
759 				// Skip over terminators and white space to get to the next name
760 				if(*pos != ';' && *pos != ',' && *pos != ' ' && *pos != '\t')
761 				{
762 					// Name starts here
763 					itemStart = pos;
764 					state = s_NAME;
765 				}
766 			}
767 			break;
768 
769 		default:
770 			// Ooops
771 			THROW_EXCEPTION(HTTPException, Internal)
772 			break;
773 		}
774 	}
775 	while(*(pos++) != 0);
776 }
777 
778 
779 // --------------------------------------------------------------------------
780 //
781 // Function
782 //		Name:    HTTPRequest::GetCookie(const char *, std::string &) const
783 //		Purpose: Fetch a cookie's value. If cookie not present, returns false
784 //				 and string is unaltered.
785 //		Created: 20/8/04
786 //
787 // --------------------------------------------------------------------------
GetCookie(const char * CookieName,std::string & rValueOut) const788 bool HTTPRequest::GetCookie(const char *CookieName, std::string &rValueOut) const
789 {
790 	// Got any cookies?
791 	if(mpCookies == 0)
792 	{
793 		return false;
794 	}
795 
796 	// See if it's there
797 	CookieJar_t::const_iterator v(mpCookies->find(std::string(CookieName)));
798 	if(v != mpCookies->end())
799 	{
800 		// Return the value
801 		rValueOut = v->second;
802 		return true;
803 	}
804 
805 	return false;
806 }
807 
808 
809 // --------------------------------------------------------------------------
810 //
811 // Function
812 //		Name:    HTTPRequest::GetCookie(const char *)
813 //		Purpose: Return a string for the given cookie, or the null string if the
814 //				 cookie has not been recieved.
815 //		Created: 22/8/04
816 //
817 // --------------------------------------------------------------------------
GetCookie(const char * CookieName) const818 const std::string &HTTPRequest::GetCookie(const char *CookieName) const
819 {
820 	static const std::string noCookie;
821 
822 	// Got any cookies?
823 	if(mpCookies == 0)
824 	{
825 		return noCookie;
826 	}
827 
828 	// See if it's there
829 	CookieJar_t::const_iterator v(mpCookies->find(std::string(CookieName)));
830 	if(v != mpCookies->end())
831 	{
832 		// Return the value
833 		return v->second;
834 	}
835 
836 	return noCookie;
837 }
838 
839 
840 
841