/***************************************************************************
 *   Copyright (C) 2005 by Christof Donat   *
 *   cdonat@gmx.de   *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <iosfwd>
#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/iostreams/stream.hpp>

#include "sslclient.h"

#ifndef HSADMIN_HTTPCLIENT
#define HSADMIN_HTTPCLIENT

using std::string;
using std::iostream;
using std::streamsize;
using boost::lexical_cast;
using boost::bad_lexical_cast;

//! A Socket as a Device
class TCPDevice : public boost::iostreams::device<boost::iostreams::bidirectional> {
public:
        //! create a TCP-Stream by connecting to the given port at the given address
        TCPDevice(const string& address, const short int port);
        //! create a TCP-Stream by connecting to the given port at the given address
        TCPDevice(const char* address, const short int port);
        //! create a TCP-Stream from a connected socket - use this with accept
        TCPDevice(const int socket);
	//! copy constructor
	TCPDevice(const TCPDevice& other);
        //! does not close the socket - use closeSocket before deleting the Device.
        virtual ~TCPDevice();
	//! closes the socket
	void closeSocket();

        //! read n Bytes from socket
        virtual streamsize read(char* s, streamsize n);
        //! write n Bytes to socket
        virtual streamsize write(const char* s, streamsize n);
private:
	void init(const char* address, const short int port);
        int m_socket;
};

//class SSLDevice;

const int HTTP = 0;
const int HTTPS = 1;

//! the basic HTTP Client - can only handle POST method.
class GenericHttpClient {
	public:
	//! initialize HTTP CLient with URL and default Port (usually 80)
	GenericHttpClient(const string &url, const unsigned short defaultPort);
	//! Post 
	string post(string postcontent);

	protected:
	//! get a buffered iostream for the connection - must be overridden by derived classes
	virtual iostream * getConnection(const string &host, unsigned short port) = 0;
	//! check if this class can handle the protocol - must be overridden by derived classes
	virtual bool checkProtocol(const string &protocol) { return false; };
	//! close the underlying connection
	virtual void close() = 0;

	private:
	iostream * parseUrl();
	const string& m_url;
	string m_protocol;
	string m_hostname;
	string m_path;
	unsigned short m_port;
	const unsigned short m_defaultPort;
};

//! This template should usually be used in one of the specialized Versions below.
template <int protocol> class HttpClient: public GenericHttpClient {
	public:
	HttpClient(const string &url, unsigned short defaultPort = 80): GenericHttpClient(url,defaultPort) {};
	protected:
	iostream * getConnection(const string &host, unsigned short port) { return (iostream *)0; };
	bool checkProtocol(const string &p) { return false; };
	virtual void close() { };
};

//! a HTTP CLient with a plain TCP socket as transport
template <> class HttpClient<HTTP>: public GenericHttpClient {
	public:
	//! initialize HTTP CLient with URL and default Port (usually 80)
	HttpClient(const string &url, unsigned short defaultPort = 80): GenericHttpClient(url,defaultPort), device(0) {};
	protected:
	//! get a buffered iostream for the connection
	iostream * getConnection(const string &host, unsigned short port) {
		if( device == 0 ) device = new TCPDevice(host,port);
		return (iostream *) new boost::iostreams::stream<TCPDevice>(*device); 
	}
	//! check if this class can handle the protocol
	bool checkProtocol(const string &protocol) { return (protocol == "http"); };
	//! close the underlying connection
	virtual void close() { device->closeSocket(); };
	private:
	TCPDevice *device;
};
//! a HTTP CLient with a SSL stream as transport
template <> class HttpClient<HTTPS>: public GenericHttpClient {
	public:
	//! initialize HTTPS CLient with URL and default Port (usually 443)
	HttpClient(const string &url, unsigned short defaultPort = 443): GenericHttpClient(url,defaultPort), device(0) {};
	protected:
	//! get a buffered iostream for the connection
	iostream * getConnection(const string &host, unsigned short port) { 
		try {
			if( device == 0 ) device = new SSLDevice(host,port);
			return (iostream *) new boost::iostreams::stream<SSLDevice>(*device); 
		} catch( SSLDevice::CertificateError &e ) {
			if( device != 0 ) {
				delete(device);
				device = 0;
			}
			std::cerr << e.msg << std::endl;
			return (iostream *)0;
		}
	}
	//! check if this class can handle the protocol
	bool checkProtocol(const string &protocol) { return (protocol == "https"); };
	//! close the underlying connection
	virtual void close() { device->closeSocket(); };
	private:
	SSLDevice *device;
};

/** \brief get the best HTTP client for the defined Protocol
 * 
 * returns HttpClient<HTTP> or HttpClient<HTTPS> depending on the protocol
 */
GenericHttpClient *createHttpClient(const string &uri);

#else  /* HSADMIN_HTTPCLIENT */
class TCPDevice;
class GenericHttpClient;
genericHttpClient *createHttpClient(url);

extern const int HTTP  = 0;
extern const int HTTPS = 1;
#endif /* HSADMIN_HTTPCLIENT */
