// distribution boxbackup-0.11.1 (svn version: 2821_2827)
// Box Backup, http://www.boxbackup.org/
// 
// Copyright (c) 2003-2010, Ben Summers and contributors.
// All rights reserved.
// 
// Note that this project uses mixed licensing. Any file with this license
// attached, or where the code LICENSE-DUAL appears on the first line, falls
// under this license. See the file COPYING.txt for more information.
// 
// This file is dual licensed. You may use and distribute it providing that you
// comply EITHER with the terms of the BSD license, OR the GPL license. It is
// not necessary to comply with both licenses, only one.
// 
// The BSD license option follows:
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  
// 3. Neither the name of the Box Backup nor the names of its contributors may
//    be used to endorse or promote products derived from this software without
//    specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// [http://en.wikipedia.org/wiki/BSD_licenses#3-clause_license_.28.22New_BSD_License.22.29]
// 
// The GPL license option follows:
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
// --------------------------------------------------------------------------
//
// File
//		Name:    HTTPQueryDecoder.cpp
//		Purpose: Utility class to decode HTTP query strings
//		Created: 26/3/04
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <stdlib.h>

#include "HTTPQueryDecoder.h"

#include "MemLeakFindOn.h"


// --------------------------------------------------------------------------
//
// Function
//		Name:    HTTPQueryDecoder::HTTPQueryDecoder(
//			 HTTPRequest::Query_t &)
//		Purpose: Constructor. Pass in the query contents you want
//			 to decode the query string into.
//		Created: 26/3/04
//
// --------------------------------------------------------------------------
HTTPQueryDecoder::HTTPQueryDecoder(HTTPRequest::Query_t &rDecodeInto)
	: mrDecodeInto(rDecodeInto),
	  mInKey(true),
	  mEscapedState(0)
{
	// Insert the terminator for escaped characters
	mEscaped[2] = '\0';
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    HTTPQueryDecoder::~HTTPQueryDecoder()
//		Purpose: Destructor.
//		Created: 26/3/04
//
// --------------------------------------------------------------------------
HTTPQueryDecoder::~HTTPQueryDecoder()
{
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    HTTPQueryDecoder::Decode(const char *, int)
//		Purpose: Decode a chunk of query string -- call several times with
//				 the bits as they are received, and then call Finish()
//		Created: 26/3/04
//
// --------------------------------------------------------------------------
void HTTPQueryDecoder::DecodeChunk(const char *pQueryString, int QueryStringSize)
{
	for(int l = 0; l < QueryStringSize; ++l)
	{
		char c = pQueryString[l];
		
		// BEFORE unescaping, check to see if we need to flip key / value
		if(mEscapedState == 0)
		{
			if(mInKey && c == '=')
			{
				// Set to store characters in the value
				mInKey = false;
				continue;
			}
			else if(!mInKey && c == '&')
			{
				// Need to store the current key/value pair
				mrDecodeInto.insert(HTTPRequest::QueryEn_t(mCurrentKey, mCurrentValue));
				// Blank the strings
				mCurrentKey.erase();
				mCurrentValue.erase();
			
				// Set to store characters in the key
				mInKey = true;
				continue;
			}
		}
		
		// Decode an escaped value?
		if(mEscapedState == 1)
		{
			// Waiting for char one of the escaped hex value
			mEscaped[0] = c;
			mEscapedState = 2;
			continue;
		}
		else if(mEscapedState == 2)
		{
			// Escaped value, decode it
			mEscaped[1] = c;	// str terminated in constructor
			mEscapedState = 0;	// stop being in escaped mode
			long ch = ::strtol(mEscaped, NULL, 16);
			if(ch <= 0 || ch > 255)
			{
				// Bad character, just ignore
				continue;
			}
			
			// Use this instead
			c = (char)ch;
		}		
		else if(c == '+')
		{
			c = ' ';
		}
		else if(c == '%')
		{
			mEscapedState = 1;
			continue;
		}

		// Store decoded value into the appropriate string
		if(mInKey)
		{
			mCurrentKey += c;
		}
		else
		{
			mCurrentValue += c;
		}
	}
	
	// Don't do anything here with left over values, DecodeChunk might be called
	// again. Let Finish() clean up.
}


// --------------------------------------------------------------------------
//
// Function
//		Name:    HTTPQueryDecoder::Finish()
//		Purpose: Finish the decoding. Necessary to get the last item!
//		Created: 26/3/04
//
// --------------------------------------------------------------------------
void HTTPQueryDecoder::Finish()
{
	// Insert any remaining value.
	if(!mCurrentKey.empty())
	{
		mrDecodeInto.insert(HTTPRequest::QueryEn_t(mCurrentKey, mCurrentValue));
		// Blank values, just in case
		mCurrentKey.erase();
		mCurrentValue.erase();
	}
}


