/* * 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 02111-1307 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; QStringList 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 QDomDocument doc; // sasl-related QString saslMech() const; QByteArray saslStep() const; void setSASLMechList(const QStringList &list); void setSASLFirst(const QString &mech, const QByteArray &step); void setSASLNext(const QByteArray &step); void setSASLAuthed(); // send / recv void sendStanza(const QDomElement &e); void sendDirect(const QString &s); void sendWhitespace(); QDomElement recvStanza(); // shutdown void shutdown(); void shutdownWithError(int cond, const QString &otherHost=""); // information QString to, from, id, lang; Version version; // error output int errCond; QString errText; QDomElement errAppSpec; QString otherHost; QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer bool isReady() const; enum { TypeElement, TypeStanza, TypeDirect, TypePing }; protected: static int stringToSASLCond(const QString &s); static int stringToStreamCond(const QString &s); static QString saslCondToString(int); static QString streamCondToString(int); void send(const QDomElement &e, bool clip=false); void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); void sendStreamError(const QString &text); // old-style bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); bool error(int code); void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement()); void delayError(int code); // reimplemented QDomElement docElement(); void handleDocOpen(const Parser::Event &pe); bool handleError(); bool handleCloseFinished(); bool doStep(const QDomElement &e); void itemWritten(int id, int size); virtual QString defaultNamespace(); virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...] virtual void handleStreamOpen(const Parser::Event &pe); virtual bool doStep2(const QDomElement &e)=0; void setReady(bool b); QString sasl_mech; QStringList sasl_mechlist; QByteArray sasl_step; bool sasl_authed; QDomElement stanzaToRecv; private: struct SASLCondEntry { const char *str; int cond; }; static SASLCondEntry saslCondTable[]; struct StreamCondEntry { const char *str; int cond; }; static StreamCondEntry streamCondTable[]; struct SendItem { QDomElement stanzaToSend; QString stringToSend; bool doWhitespace; }; QValueList sendList; bool doShutdown, delayedError, closeError, ready; int stanzasPending, stanzasWritten; void init(); void extractStreamError(const QDomElement &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 QString &to); void startDialbackOut(const QString &to, const QString &from); void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key); void startClientIn(const QString &id); void startServerIn(const QString &id); void setLang(const QString &s); void setAllowTLS(bool b); void setAllowBind(bool b); void setAllowPlain(bool b); // old-mode void setPassword(const QString &s); void setFrom(const QString &s); void setDialbackKey(const QString &s); // input QString user, host; // status bool old; StreamFeatures features; //static QString xmlToString(const QDomElement &e, bool clip=false); class DBItem { public: enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated }; int type; Jid to, from; QString 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 }; QValueList 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; QString password; QString dialback_id, dialback_key; QString self_from; void init(); static int getOldErrorCode(const QDomElement &e); bool loginComplete(); bool isValidStanza(const QDomElement &e) const; bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item); bool normalStep(const QDomElement &e); bool dialbackStep(const QDomElement &e); // reimplemented bool stepAdvancesParser() const; bool stepRequiresElement() const; void stringSend(const QString &s); void stringRecv(const QString &s); QString defaultNamespace(); QStringList extraNamespaces(); void handleStreamOpen(const Parser::Event &pe); bool doStep2(const QDomElement &e); void elementSend(const QDomElement &e); void elementRecv(const QDomElement &e); }; } #endif