/* * protocol.h - XMPP-Core protocol state machine * Copyright (C) 2004 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 PROTOCOL_H #define PROTOCOL_H #include #include "xmlprotocol.h" #include "xmpp.h" #define NS_ETHERX "http://etherx.jabber.org/streams" #define NS_CLIENT "jabber:client" #define NS_SERVER "jabber:server" #define NS_DIALBACK "jabber:server:dialback" #define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams" #define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls" #define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl" #define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session" #define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" #define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind" #define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im" #define NS_XHTML "http://www.w3.org/1999/xhtml" #define NS_CHATSTATES "http://jabber.org/protocol/chatstates" namespace XMPP { class Version { public: Version(int maj=0, int min=0); int major, minor; }; class StreamFeatures { public: StreamFeatures(); bool tls_supported, sasl_supported, bind_supported; bool tls_required; TQStringList sasl_mechs; }; class BasicProtocol : public XmlProtocol { public: // xmpp 1.0 error conditions enum SASLCond { Aborted, IncorrectEncoding, InvalidAuthzid, InvalidMech, MechTooWeak, NotAuthorized, TemporaryAuthFailure }; enum StreamCond { BadFormat, BadNamespacePrefix, Conflict, ConnectionTimeout, HostGone, HostUnknown, ImproperAddressing, InternalServerError, InvalidFrom, InvalidId, InvalidNamespace, InvalidXml, StreamNotAuthorized, PolicyViolation, RemoteConnectionFailed, ResourceConstraint, RestrictedXml, SeeOtherHost, SystemShutdown, UndefinedCondition, UnsupportedEncoding, UnsupportedStanzaType, UnsupportedVersion, XmlNotWellFormed }; enum BindCond { BindBadRequest, BindNotAllowed, BindConflict }; // extend the XmlProtocol enums enum Need { NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist NStartTLS, // need to switch on TLS layer NSASLFirst, // need SASL first step NSASLNext, // need SASL next step NSASLLayer, // need to switch on SASL layer NCustom = XmlProtocol::NCustom+10 }; enum Event { EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received ESASLSuccess, // breakpoint after successful sasl auth EStanzaReady, // a stanza was received EStanzaSent, // a stanza was sent EReady, // stream is ready for stanza use ECustom = XmlProtocol::ECustom+10 }; enum Error { ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange ErrStream, // , see errCond, errText, and errAppSpec for details ErrStartTLS, // server refused starttls ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol) ErrBind, // server refused resource bind ErrCustom = XmlProtocol::ErrCustom+10 }; BasicProtocol(); ~BasicProtocol(); void reset(); // for outgoing xml TQDomDocument doc; // sasl-related TQString saslMech() const; TQByteArray saslStep() const; void setSASLMechList(const TQStringList &list); void setSASLFirst(const TQString &mech, const TQByteArray &step); void setSASLNext(const TQByteArray &step); void setSASLAuthed(); // send / recv void sendStanza(const TQDomElement &e); void sendDirect(const TQString &s); void sendWhitespace(); TQDomElement recvStanza(); // shutdown void shutdown(); void shutdownWithError(int cond, const TQString &otherHost=""); // information TQString to, from, id, lang; Version version; // error output int errCond; TQString errText; TQDomElement errAppSpec; TQString otherHost; TQByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer bool isReady() const; enum { TypeElement, TypeStanza, TypeDirect, TypePing }; protected: static int stringToSASLCond(const TQString &s); static int stringToStreamCond(const TQString &s); static TQString saslCondToString(int); static TQString streamCondToString(int); void send(const TQDomElement &e, bool clip=false); void sendStreamError(int cond, const TQString &text="", const TQDomElement &appSpec=TQDomElement()); void sendStreamError(const TQString &text); // old-style bool errorAndClose(int cond, const TQString &text="", const TQDomElement &appSpec=TQDomElement()); bool error(int code); void delayErrorAndClose(int cond, const TQString &text="", const TQDomElement &appSpec=TQDomElement()); void delayError(int code); // reimplemented TQDomElement docElement(); void handleDocOpen(const Parser::Event &pe); bool handleError(); bool handleCloseFinished(); bool doStep(const TQDomElement &e); void itemWritten(int id, int size); virtual TQString defaultNamespace(); virtual TQStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...] virtual void handleStreamOpen(const Parser::Event &pe); virtual bool doStep2(const TQDomElement &e)=0; void setReady(bool b); TQString sasl_mech; TQStringList sasl_mechlist; TQByteArray sasl_step; bool sasl_authed; TQDomElement stanzaToRecv; private: struct SASLCondEntry { const char *str; int cond; }; static SASLCondEntry saslCondTable[]; struct StreamCondEntry { const char *str; int cond; }; static StreamCondEntry streamCondTable[]; struct SendItem { TQDomElement stanzaToSend; TQString stringToSend; bool doWhitespace; }; TQValueList sendList; bool doShutdown, delayedError, closeError, ready; int stanzasPending, stanzasWritten; void init(); void extractStreamError(const TQDomElement &e); }; class CoreProtocol : public BasicProtocol { public: enum { NPassword = NCustom, // need password for old-mode EDBVerify = ECustom, // breakpoint after db:verify request ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally }; CoreProtocol(); ~CoreProtocol(); void reset(); void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth); void startServerOut(const TQString &to); void startDialbackOut(const TQString &to, const TQString &from); void startDialbackVerifyOut(const TQString &to, const TQString &from, const TQString &id, const TQString &key); void startClientIn(const TQString &id); void startServerIn(const TQString &id); void setLang(const TQString &s); void setAllowTLS(bool b); void setAllowBind(bool b); void setAllowPlain(bool b); // old-mode void setPassword(const TQString &s); void setFrom(const TQString &s); void setDialbackKey(const TQString &s); // input TQString user, host; // status bool old; StreamFeatures features; //static TQString xmlToString(const TQDomElement &e, bool clip=false); class DBItem { public: enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated }; int type; Jid to, from; TQString key, id; bool ok; }; private: enum Step { Start, Done, SendFeatures, GetRequest, HandleTLS, GetSASLResponse, IncHandleSASLSuccess, GetFeatures, // read features packet HandleFeatures, // act on features, by initiating tls, sasl, or bind GetTLSProceed, // read tls response GetSASLFirst, // perform sasl first step using provided data GetSASLChallenge, // read server sasl challenge GetSASLNext, // perform sasl next step using provided data HandleSASLSuccess, // handle what must be done after reporting sasl success GetBindResponse, // read bind response HandleAuthGet, // send old-protocol auth-get GetAuthGetResponse, // read auth-get response HandleAuthSet, // send old-protocol auth-set GetAuthSetResponse // read auth-set response }; TQValueList dbrequests, dbpending, dbvalidated; bool server, dialback, dialback_verify; int step; bool digest; bool tls_started, sasl_started; Jid jid; bool oldOnly; bool allowPlain; bool doTLS, doAuth, doBinding; TQString password; TQString dialback_id, dialback_key; TQString self_from; void init(); static int getOldErrorCode(const TQDomElement &e); bool loginComplete(); bool isValidStanza(const TQDomElement &e) const; bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item); bool normalStep(const TQDomElement &e); bool dialbackStep(const TQDomElement &e); // reimplemented bool stepAdvancesParser() const; bool stepRequiresElement() const; void stringSend(const TQString &s); void stringRecv(const TQString &s); TQString defaultNamespace(); TQStringList extraNamespaces(); void handleStreamOpen(const Parser::Event &pe); bool doStep2(const TQDomElement &e); void elementSend(const TQDomElement &e); void elementRecv(const TQDomElement &e); }; } #endif