/* $Revision: 15 $ $Date: 18/08/05 13:08 $ Copyright © 1999-2007, FSL Technologies Limited. Contact "http://fost.3.felspar.com". */ #ifndef FOST__INTERNET_HPP #define FOST__INTERNET_HPP #pragma once #include #include #include #ifdef F3UTIL_EXPORTS #define FOST_INTERNET_HPP_ARCHIVE L"$Archive: /FOST.3/F3Util/FOST.internet.hpp $" #define FOST_INTERNET_HPP_REVISION L"$Revision: 15 $" #define FOST_INTERNET_HPP_DATE L"$Date: 18/08/05 13:08 $" #endif // SSL implementation structs struct ssl_method_st; struct ssl_ctx_st; struct ssl_st; namespace FSLib { // Any function using Winsock must call this function first in order to ensure that Winsock is // initialised. void F3UTIL_DECLSPEC initWSA(); typedef unsigned __int16 t_port; typedef unsigned __int32 t_socket; class F3UTIL_DECLSPEC Host { public: inline Host(); inline Host( const FSLib::wstring & ); inline Host( unsigned long int ); inline Host( unsigned char, unsigned char, unsigned char, unsigned char ); inline unsigned long int address() const; inline FSLib::wstring hostName() const; inline operator const void *() const; private: inline void addressFromName() const; inline void nameFromAddress(); private: FSLib::wstring m_name; mutable unsigned long int m_address; }; class TCPServer; class F3UTIL_DECLSPEC TCPStream : public std::basic_iostream< char >, public boost::noncopyable { public: // Client connections (originate) inline TCPStream( const Host &remote, t_port port ); // Server connections (listen - will be made by a TCPServer object) inline TCPStream( TCPServer *server, t_socket socket ); inline ~TCPStream() throw (); inline void close(); protected: inline void open( const Host &remote, t_port port ); private: TimeStamp m_connected, m_closed; t_socket m_socket; t_port m_port; Host m_remoteHost; // Only used by server streams Nullable< Host > m_localHost; TCPServer *m_server; }; class F3UTIL_DECLSPEC TCPBuf : public std::basic_streambuf< char >, public boost::noncopyable { public: inline TCPBuf(); inline TCPBuf( t_socket a_socket ); inline ~TCPBuf() throw (); inline void attach( t_socket a_socket ); inline int close(); protected: inline int underflow(); inline int overflow(int=EOF); inline int sync(); inline int showmanyc(); private: unsigned __int64 m_transferredOut, m_transferredIn; t_socket m_socket; char *m_outBuffer; }; class F3UTIL_DECLSPEC TCPServer { public: inline TCPServer( t_port port ); inline virtual ~TCPServer() throw () {} inline TCPStream &stream(); inline const Host &host() const; inline t_port port() const; protected: void listen(); private: Host m_host; t_port m_port; t_socket m_socket; boost::scoped_ptr< TCPStream > m_stream; }; class F3UTIL_DECLSPEC SSLStream : public std::basic_iostream< char >, public boost::noncopyable { public: SSLStream( const Host &, const t_port, const FSLib::wstring &certificate = L"" ); ~SSLStream() throw (); void open( const Host &, const t_port, const FSLib::wstring &certificate = L"" ); private: FSLib::wstring m_cert_name; }; class F3UTIL_DECLSPEC SSLBuf : public std::basic_streambuf< char > { public: SSLBuf(); ~SSLBuf() throw (); void open( const Host &, const t_port ); void useCertificate( const FSLib::wstring &a_cert_name ); private: int underflow(); int overflow( int = EOF ); int sync(); private: t_socket m_socket; struct sockaddr_in *m_sin; struct ssl_method_st * m_meth; struct ssl_ctx_st * m_ctx; struct ssl_st * m_ssl; FSLib::wstring m_cert_name; char *m_outBuffer; }; // Define the paramaters of a scheme as used by a URL. class F3UTIL_DECLSPEC Scheme { public: Scheme( const FSLib::wstring & ); Scheme( const FSLib::wstring &, t_port ); t_port port() const; FSLib::wstring monicker() const; private: FSLib::wstring m_name; t_port m_port; }; // Define a supported scheme to give a default port number. class F3UTIL_DECLSPEC SchemePort { public: inline SchemePort( const FSLib::wstring &s, t_port p ); const FSLib::wstring &monicker() const; t_port port() const; private: FSLib::wstring m_monicker; t_port m_port; }; class F3UTIL_DECLSPEC QueryString { public: enum t_form { e_pathname, e_encoded }; QueryString( const t_form, const FSLib::wstring & ); void append( const wstring &name, const Nullable< wstring > &value ); wstring pathspec() const; private: void readPathname( const FSLib::wstring & ); void readEncoded( const FSLib::wstring & ); private: wstring m_get; }; class F3UTIL_DECLSPEC Url { public: Url(); Url( const FSLib::wstring & ); Url( const Scheme &, const Host &, const t_port ); Url( const Scheme &, const Host &, const FSLib::Nullable< FSLib::wstring > &username = FSLib::Nullable< FSLib::wstring >(), const FSLib::Nullable< FSLib::wstring > &password = FSLib::Nullable< FSLib::wstring >(), const t_port port = 0 ); FSLib::wstring asString() const; Host host() const; Scheme scheme() const; t_port port() const; FSLib::Nullable< FSLib::wstring > user() const; FSLib::Nullable< FSLib::wstring > password() const; const FSLib::wstring &pathSpec() const; void pathSpec( const FSLib::wstring &pathName ); private: Host m_host; Scheme m_scheme; FSLib::Nullable< FSLib::wstring > m_username; FSLib::Nullable< FSLib::wstring > m_password; t_port m_port; mutable FSLib::wstring m_pathSpec; typedef std::list< FSLib::wstring > pathlist; mutable pathlist m_pathList; // Used to build the pathSpec. // Above two mutable to allow a URL( ... ) anonymous constructor to work. Similar with below being const qualified. // void pathSpec( const FSLib::wstring & ) could actually be const qualified, but this isn't logically or semantically correct. void SplitPathSpec() const; }; /* Base class used for MIME, mail, and HTTP etc. headers */ class F3UTIL_DECLSPEC __declspec( novtable ) Headers { public: class Value; Headers(); void parse( const string &headers ); bool exists( const wstring & ) const; Value &add( const wstring &name, const Value & ); const Value &operator[]( const wstring & ) const; typedef std::map< wstring, Value >::const_iterator const_iterator; const_iterator begin() const; const_iterator end() const; class F3UTIL_DECLSPEC Value { public: Value(); Value( const wchar_t * ); Value( const wstring & ); Value( const wstring &, const std::map< wstring, wstring > & ); const wstring &value() const; const wstring &subvalue( const wstring &k, const wstring &v ); Nullable< wstring > subvalue( const wstring &k ) const; typedef std::map< wstring, wstring >::const_iterator const_iterator; const_iterator begin() const; const_iterator end() const; private: wstring m_value; std::map< wstring, wstring > m_subvalues; }; protected: virtual std::pair< FSLib::wstring, FSLib::Headers::Value > value( const wstring &name, const wstring &value ) = 0; private: std::map< wstring, Value > m_headers; }; /* This initial implementation is only intended to be able to parse HTTP POST multipart/form-data encoded data */ class F3UTIL_DECLSPEC Mime : public boost::noncopyable { public: class F3UTIL_DECLSPEC Headers : public FSLib::Headers { protected: std::pair< FSLib::wstring, FSLib::Headers::Value > value( const wstring &name, const wstring &value ); }; Mime( const Headers &headers, const unsigned char *data, size_t octets ); Headers headers; boost::shared_array< unsigned char > data; std::list< boost::shared_ptr< Mime > > items; }; class F3UTIL_DECLSPEC UserAgent { public: // A HTTP style request for a file class F3UTIL_DECLSPEC Request { public: typedef std::map< FSLib::wstring, FSLib::wstring > t_headers; typedef std::multimap< FSLib::wstring, FSLib::wstring > t_values; inline Request( const FSLib::wstring &a_method, const Url &a_url ); inline void print( std::ostream &a_output ); inline void addHeader( const FSLib::wstring a_header, const FSLib::wstring a_val ); inline void addValue( const FSLib::wstring a_var, const FSLib::wstring a_val ); inline const Url &url() const; inline const FSLib::wstring &method() const; inline const t_headers &headers() const; inline const t_values &values() const; private: inline void addStdHeaders(); // WWW-style encoding of special characters. inline FSLib::string url_encode_variables() const; inline FSLib::string url_encode_to_hex( const unsigned char ch ) const ; inline FSLib::string url_encode( const FSLib::string & data ) const; // WWW-style base64 encoding - this does *NOT* handle line breaking, so it's no use for email. inline char base64_encode_6bits( const unsigned short int ) const; inline FSLib::string base64_encode_3bytes( const FSLib::string & ) const; inline FSLib::string base64_encode( const FSLib::string & data ) const; private: t_headers m_header; t_values m_post_values; Url m_url; FSLib::wstring m_method; }; // The reply from a HTTP server class F3UTIL_DECLSPEC Reply { public: typedef std::map< FSLib::wstring, FSLib::wstring > t_headers; inline Reply(); inline void fetch( std::istream &a_input ); inline const FSLib::wstring &header( const FSLib::wstring &a_header ) const; inline const t_headers &headers() const; inline const FSLib::string &body() const; inline int status() const; inline int statusHex() const; private: inline int unHex( const FSLib::string & ) const; inline void fetchBody( std::istream & ); inline void fetchBody( std::istream &, int ); inline void fetchBodyChunked( std::istream & ); inline void fetchHeaders( std::istream & ); inline bool fetchStatus( std::istream & ); inline std::pair< bool, FSLib::string > get_line( std::istream & ) const; private: t_headers m_header; FSLib::string m_body; FSLib::wstring m_status_line; int m_status; }; public: explicit inline UserAgent( const Url &a_url ); inline UserAgent( const Url &a_url, const FSLib::wstring &a_certificate ); inline UserAgent( const FSLib::wstring &a_method, const Url &a_url ); inline UserAgent( const FSLib::wstring &a_method, const Url &a_url, const FSLib::wstring &a_certificate ); // This is the intended public interface... inline void go(); inline Request &request(); inline const Reply &reply() const; private: Request m_request; Reply m_reply; Url m_url; // Needed for create_connection - not for anything else. FSLib::wstring m_certificate; boost::shared_ptr< std::iostream > m_connection; protected: inline void create_connection(); }; namespace HTML { class Translator; } class F3UTIL_DECLSPEC InputGet : public Input { public: InputGet( const FSLib::string &query ); InputGet( ATL::CComPtr< IRequest > ); using Input::exists; using Input::value; size_t exists( const FSLib::wstring &name, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; _variant_t value( const FSLib::wstring &name, const Nullable< size_t > &num = Null, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; typedef std::map< wstring, std::vector< _variant_t > >::const_iterator t_name; typedef std::vector< _variant_t >::const_iterator t_value; t_name begin() const; t_name end() const; t_value begin( const t_name n ) const; t_value end( const t_name n ) const; private: ATL::CComPtr< IRequest > m_request; mutable bool m_parsed; mutable std::map< wstring, std::vector< _variant_t > > m_data; void parse() const; }; class F3UTIL_DECLSPEC InputCookie : public Input { public: InputCookie( const Input &server ); using Input::exists; using Input::value; size_t exists( const FSLib::wstring &name, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; _variant_t value( const FSLib::wstring &name, const Nullable< size_t > &num = Null, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; private: void parse( const Nullable< wstring > &cookies ); private: std::map< std::pair< wstring, wstring >, _variant_t > m_values; }; namespace Isapi { class __declspec( novtable ) F3UTIL_DECLSPEC Response : public Input { public: virtual ~Response(); Url url; string status; boost::shared_ptr< HTML::Translator > translator; const Input &server; const InputCookie cookies; const InputGet query; using Input::exists; using Input::value; size_t exists( const FSLib::wstring &name, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; _variant_t value( const FSLib::wstring &name, const Nullable< size_t > &num = Null, const FSLib::Nullable< FSLib::wstring > &partition = Null ) const; void partial( const string &str ); void partial( const wstring &str ) { partial( narrow( str ) ); } void complete( const string &str ); void complete( const wstring &str ) { complete( narrow( str ) ); } class Headers : public FSLib::Headers { std::pair< FSLib::wstring, FSLib::Headers::Value > value( const wstring &name, const wstring &value ); } headers; protected: Response( const Input &server ); virtual void send_headers( const string &headers ) const = 0; virtual void send_data( const string & ) const; virtual void send_data( const utf8 *dta, size_t bytes, bool check = true ) const = 0; private: bool m_preamble; }; enum t_completion { e_complete, e_next }; } class F3UTIL_DECLSPEC Smtp { public: Smtp( const Host & mailhost ); void Hello(); void MailFrom( const FSLib::wstring & ); void RcptTo( const FSLib::wstring & ); void Data( const FSLib::wstring & ); void Quit(); private: boost::scoped_ptr< TCPStream > m_connection; }; namespace Exceptions { class F3UTIL_DECLSPEC SocketError : public Exception { public: inline SocketError( const FSLib::wstring &message ); inline SocketError( const FSLib::wstring &message, int wsaErrorNumber ); inline SocketError( int wsaErrorNumber ); protected: inline const wchar_t * const message() const; private: void errorText( int wsaErrorNumber ); }; class F3UTIL_DECLSPEC Protocol : public Exception { public: Protocol( const FSLib::wstring &message ); Protocol( const FSLib::wstring &message, const wstring &info ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC RelativePath : public FSLib::Exceptions::Exception { public: RelativePath( const FSLib::wstring &basePathName, const FSLib::wstring &relPathName, const FSLib::wstring &error ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC ParseError : public FSLib::Exceptions::Exception { public: ParseError( const FSLib::wstring &message ); ParseError( const FSLib::wstring &message, const FSLib::wstring &value ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC InvalidScheme : public FSLib::Exceptions::Exception { public: InvalidScheme( const FSLib::wstring &message ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC NotHttp : public Exception { public: inline NotHttp( const Scheme & ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC BadRequest : public Exception { public: inline BadRequest( const FSLib::wstring & ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC Base64 : public Exception { public: inline Base64( const FSLib::wstring &message, int value ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC Header : public Protocol { public: inline Header( const FSLib::wstring &message ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC Transport : public Protocol { public: inline Transport( const FSLib::wstring &message ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC Body : public Protocol { public: inline Body( const FSLib::wstring &message ); protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC CertificateError : public Exception { public: CertificateError() : FSLib::Exceptions::Exception() {} protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC PrivateKeyError : public Exception { public: PrivateKeyError() : FSLib::Exceptions::Exception() {} protected: inline const wchar_t * const message() const; }; class F3UTIL_DECLSPEC SSLSocketError : public SocketError { public: SSLSocketError( const FSLib::wstring &, long ssl_code ); protected: inline const wchar_t * const message() const; }; } } #endif // FOST__INTERNET_HPP