/*
 * xmpp.h - XMPP "core" library API
 * Copyright (C) 2003  Justin Karneges
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifndef XMPP_H
#define XMPP_H

#include <tqobject.h>
#include <tqstring.h>
#include <tqhostaddress.h>
#include <tqstring.h>
#include <tqcstring.h>
#include <tqxml.h>
#include <tqdom.h>

namespace TQCA
{
	class TLS;
}

#ifndef CS_XMPP
class ByteStream;
#endif

namespace XMPP
{
	// CS_IMPORT_BEGIN cutestuff/bytestream.h
#ifdef CS_XMPP
	class ByteStream;
#endif
	// CS_IMPORT_END

	class Debug
	{
	public:
		virtual ~Debug();

		virtual void msg(const TQString &)=0;
		virtual void outgoingTag(const TQString &)=0;
		virtual void incomingTag(const TQString &)=0;
		virtual void outgoingXml(const TQDomElement &)=0;
		virtual void incomingXml(const TQDomElement &)=0;
	};

	void setDebug(Debug *);

	class Connector : public TQObject
	{
		Q_OBJECT
  
	public:
		Connector(TQObject *parent=0);
		virtual ~Connector();

		virtual void connectToServer(const TQString &server)=0;
		virtual ByteStream *stream() const=0;
		virtual void done()=0;

		bool useSSL() const;
		bool havePeerAddress() const;
		TQHostAddress peerAddress() const;
		TQ_UINT16 peerPort() const;

	signals:
		void connected();
		void error();

	protected:
		void setUseSSL(bool b);
		void setPeerAddressNone();
		void setPeerAddress(const TQHostAddress &addr, TQ_UINT16 port);

	private:
		bool ssl;
		bool haveaddr;
		TQHostAddress addr;
		TQ_UINT16 port;
	};

	class AdvancedConnector : public Connector
	{
		Q_OBJECT
  
	public:
		enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream };
		AdvancedConnector(TQObject *parent=0);
		virtual ~AdvancedConnector();

		class Proxy
		{
		public:
			enum { None, HttpConnect, HttpPoll, Socks };
			Proxy();
			~Proxy();

			int type() const;
			TQString host() const;
			TQ_UINT16 port() const;
			TQString url() const;
			TQString user() const;
			TQString pass() const;
			int pollInterval() const;

			void setHttpConnect(const TQString &host, TQ_UINT16 port);
			void setHttpPoll(const TQString &host, TQ_UINT16 port, const TQString &url);
			void setSocks(const TQString &host, TQ_UINT16 port);
			void setUserPass(const TQString &user, const TQString &pass);
			void setPollInterval(int secs);

		private:
			int t;
			TQString v_host, v_url;
			TQ_UINT16 v_port;
			TQString v_user, v_pass;
			int v_poll;
		};

		void setProxy(const Proxy &proxy);
		void setOptHostPort(const TQString &host, TQ_UINT16 port);
		void setOptProbe(bool);
		void setOptSSL(bool);

		void changePollInterval(int secs);

		void connectToServer(const TQString &server);
		ByteStream *stream() const;
		void done();

		int errorCode() const;

	signals:
		void srvLookup(const TQString &server);
		void srvResult(bool success);
		void httpSyncStarted();
		void httpSyncFinished();

	private slots:
		void dns_done();
		void srv_done();
		void bs_connected();
		void bs_error(int);
		void http_syncStarted();
		void http_syncFinished();

	private:
		class Private;
		Private *d;

		void cleanup();
		void do_resolve();
		void do_connect();
		void tryNextSrv();
	};

	class TLSHandler : public TQObject
	{
		Q_OBJECT
  
	public:
		TLSHandler(TQObject *parent=0);
		virtual ~TLSHandler();

		virtual void reset()=0;
		virtual void startClient(const TQString &host)=0;
		virtual void write(const TQByteArray &a)=0;
		virtual void writeIncoming(const TQByteArray &a)=0;

	signals:
		void success();
		void fail();
		void closed();
		void readyRead(const TQByteArray &a);
		void readyReadOutgoing(const TQByteArray &a, int plainBytes);
	};

	class TQCATLSHandler : public TLSHandler
	{
		Q_OBJECT
  
	public:
		TQCATLSHandler(TQCA::TLS *parent);
		~TQCATLSHandler();

		TQCA::TLS *tls() const;
		int tlsError() const;

		void reset();
		void startClient(const TQString &host);
		void write(const TQByteArray &a);
		void writeIncoming(const TQByteArray &a);

	signals:
		void tlsHandshaken();

	public slots:
		void continueAfterHandshake();

	private slots:
		void tls_handshaken();
		void tls_readyRead();
		void tls_readyReadOutgoing(int);
		void tls_closed();
		void tls_error(int);

	private:
		class Private;
		Private *d;
	};

	class Jid
	{
	public:
		Jid();
		~Jid();

		Jid(const TQString &s);
		Jid(const char *s);
		Jid & operator=(const TQString &s);
		Jid & operator=(const char *s);

		void set(const TQString &s);
		void set(const TQString &domain, const TQString &node, const TQString &resource="");

		void setDomain(const TQString &s);
		void setNode(const TQString &s);
		void setResource(const TQString &s);

		const TQString & domain() const { return d; }
		const TQString & node() const { return n; }
		const TQString & resource() const { return r; }
		const TQString & bare() const { return b; }
		const TQString & full() const { return f; }

		Jid withNode(const TQString &s) const;
		Jid withResource(const TQString &s) const;

		bool isValid() const;
		bool isEmpty() const;
		bool compare(const Jid &a, bool compareRes=true) const;

		static bool validDomain(const TQString &s, TQString *norm=0);
		static bool validNode(const TQString &s, TQString *norm=0);
		static bool validResource(const TQString &s, TQString *norm=0);

		// TODO: kill these later
		const TQString & host() const { return d; }
		const TQString & user() const { return n; }
		const TQString & userHost() const { return b; }

	private:
		void reset();
		void update();

		TQString f, b, d, n, r;
		bool valid;
	};

	class Stream;
	class Stanza
	{
	public:
		enum Kind { Message, Presence, IQ };
		enum ErrorType { Cancel, Continue, Modify, Auth, Wait };
		enum ErrorCond
		{
			BadRequest,
			Conflict,
			FeatureNotImplemented,
			Forbidden,
			InternalServerError,
			ItemNotFound,
			JidMalformed,
			NotAllowed,
			PaymentRequired,
			RecipientUnavailable,
			RegistrationRequired,
			ServerNotFound,
			ServerTimeout,
			ResourceConstraint,
			ServiceUnavailable,
			SubscriptionRequired,
			UndefinedCondition,
			UnexpectedRequest
		};

		Stanza();
		Stanza(const Stanza &from);
		Stanza & operator=(const Stanza &from);
		virtual ~Stanza();

		class Error
		{
		public:
			Error(int type=Cancel, int condition=UndefinedCondition, const TQString &text="", const TQDomElement &appSpec=TQDomElement());

			int type;
			int condition;
			TQString text;
			TQDomElement appSpec;
		};

		bool isNull() const;

		TQDomElement element() const;
		TQString toString() const;

		TQDomDocument & doc() const;
		TQString baseNS() const;
		TQString xhtmlImNS() const;
		TQString xhtmlNS() const;
		TQDomElement createElement(const TQString &ns, const TQString &tagName);
		TQDomElement createTextElement(const TQString &ns, const TQString &tagName, const TQString &text);
		TQDomElement createXHTMLElement(const TQString &xHTML);
		void appendChild(const TQDomElement &e);

		Kind kind() const;
		void setKind(Kind k);

		Jid to() const;
		Jid from() const;
		TQString id() const;
		TQString type() const;
		TQString lang() const;

		void setTo(const Jid &j);
		void setFrom(const Jid &j);
		void setId(const TQString &id);
		void setType(const TQString &type);
		void setLang(const TQString &lang);

		Error error() const;
		void setError(const Error &err);
		void clearError();

	private:
		friend class Stream;
		Stanza(Stream *s, Kind k, const Jid &to, const TQString &type, const TQString &id);
		Stanza(Stream *s, const TQDomElement &e);

		class Private;
		Private *d;
	};

	class Stream : public TQObject
	{
		Q_OBJECT
  
	public:
		enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 };
		enum StreamCond {
			GenericStreamError,
			Conflict,
			ConnectionTimeout,
			InternalServerError,
			InvalidFrom,
			InvalidXml,
			PolicyViolation,
			ResourceConstraint,
			SystemShutdown
		};

		Stream(TQObject *parent=0);
		virtual ~Stream();

		virtual TQDomDocument & doc() const=0;
		virtual TQString baseNS() const=0;
		virtual TQString xhtmlImNS() const=0;
		virtual TQString xhtmlNS() const=0;
		virtual bool old() const=0;

		virtual void close()=0;
		virtual bool stanzaAvailable() const=0;
		virtual Stanza read()=0;
		virtual void write(const Stanza &s)=0;

		virtual int errorCondition() const=0;
		virtual TQString errorText() const=0;
		virtual TQDomElement errorAppSpec() const=0;

		Stanza createStanza(Stanza::Kind k, const Jid &to="", const TQString &type="", const TQString &id="");
		Stanza createStanza(const TQDomElement &e);

		static TQString xmlToString(const TQDomElement &e, bool clip=false);

	signals:
		void connectionClosed();
		void delayedCloseFinished();
		void readyRead();
		void stanzaWritten();
		void error(int);
	};

	class ClientStream : public Stream
	{
		Q_OBJECT
  
	public:
		enum Error {
			ErrConnection = ErrCustom,  // Connection error, ask Connector-subclass what's up
			ErrNeg,                     // Negotiation error, see condition
			ErrTLS,                     // TLS error, see condition
			ErrAuth,                    // Auth error, see condition
			ErrSecurityLayer,           // broken SASL security layer
			ErrBind                     // Resource binding error
		};
		enum Warning {
			WarnOldVersion,             // server uses older XMPP/Jabber "0.9" protocol
			WarnNoTLS                   // there is no chance for TLS at this point
		};
		enum NegCond {
			HostGone,                   // host no longer hosted
			HostUnknown,                // unknown host
			RemoteConnectionFailed,     // unable to connect to a required remote resource
			SeeOtherHost,               // a 'redirect', see errorText() for other host
			UnsupportedVersion          // unsupported XMPP version
		};
		enum TLSCond {
			TLSStart,                   // server rejected STARTTLS
			TLSFail                     // TLS failed, ask TLSHandler-subclass what's up
		};
		enum SecurityLayer {
			LayerTLS,
			LayerSASL
		};
		enum AuthCond {
			GenericAuthError,           // all-purpose "can't login" error
			NoMech,                     // No appropriate auth mech available
			BadProto,                   // Bad SASL auth protocol
			BadServ,                    // Server failed mutual auth
			EncryptionRequired,         // can't use mech without TLS
			InvalidAuthzid,             // bad input JID
			InvalidMech,                // bad mechanism
			InvalidRealm,               // bad realm
			MechTooWeak,                // can't use mech with this authzid
			NotAuthorized,              // bad user, bad password, bad creditials
			TemporaryAuthFailure        // please try again later!
		};
		enum BindCond {
			BindNotAllowed,             // not allowed to bind a resource
			BindConflict                // resource in-use
		};

		ClientStream(Connector *conn, TLSHandler *tlsHandler=0, TQObject *parent=0);
		ClientStream(const TQString &host, const TQString &defRealm, ByteStream *bs, TQCA::TLS *tls=0, TQObject *parent=0); // server
		~ClientStream();

		Jid jid() const;
		void connectToServer(const Jid &jid, bool auth=true);
		void accept(); // server
		bool isActive() const;
		bool isAuthenticated() const;

		// login params
		void setUsername(const TQString &s);
		void setPassword(const TQString &s);
		void setRealm(const TQString &s);
		void continueAfterParams();

		// SASL information
		TQString saslMechanism() const;
		int saslSSF() const;

		// binding
		void setResourceBinding(bool);

		// security options (old protocol only uses the first !)
		void setAllowPlain(bool);
		void setRequireMutualAuth(bool);
		void setSSFRange(int low, int high);
		void setOldOnly(bool);
		void setSASLMechanism(const TQString &s);
		void setLocalAddr(const TQHostAddress &addr, TQ_UINT16 port);

		// reimplemented
		TQDomDocument & doc() const;
		TQString baseNS() const;
		TQString xhtmlImNS() const;
		TQString xhtmlNS() const;
		bool old() const;

		void close();
		bool stanzaAvailable() const;
		Stanza read();
		void write(const Stanza &s);

		int errorCondition() const;
		TQString errorText() const;
		TQDomElement errorAppSpec() const;

		// extra
		void writeDirect(const TQString &s);
		void setNoopTime(int mills);

	signals:
		void connected();
		void securityLayerActivated(int);
		void needAuthParams(bool user, bool pass, bool realm);
		void authenticated();
		void warning(int);
		void incomingXml(const TQString &s);
		void outgoingXml(const TQString &s);

	public slots:
		void continueAfterWarning();

	private slots:
		void cr_connected();
		void cr_error();

		void bs_connectionClosed();
		void bs_delayedCloseFinished();
		void bs_error(int); // server only

		void ss_readyRead();
		void ss_bytesWritten(int);
		void ss_tlsHandshaken();
		void ss_tlsClosed();
		void ss_error(int);

		void sasl_clientFirstStep(const TQString &mech, const TQByteArray *clientInit);
		void sasl_nextStep(const TQByteArray &stepData);
		void sasl_needParams(bool user, bool authzid, bool pass, bool realm);
		void sasl_authCheck(const TQString &user, const TQString &authzid);
		void sasl_authenticated();
		void sasl_error(int);

		void doNoop();
		void doReadyRead();

	private:
		class Private;
		Private *d;

		void reset(bool all=false);
		void processNext();
		int convertedSASLCond() const;
		bool handleNeed();
		void handleError();
		void srvProcessNext();
	};
}

#endif
