1 /*
2  * protocol.h - XMPP-Core protocol state machine
3  * Copyright (C) 2004  Justin Karneges
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * either version 2
9    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #ifndef PROTOCOL_H
23 #define PROTOCOL_H
24 
25 #include <QPair>
26 //Added by qt3to4:
27 #include <QList>
28 #include "xmlprotocol.h"
29 #include "xmpp.h"
30 
31 #define NS_ETHERX   "http://etherx.jabber.org/streams"
32 #define NS_CLIENT   "jabber:client"
33 #define NS_SERVER   "jabber:server"
34 #define NS_DIALBACK "jabber:server:dialback"
35 #define NS_STREAMS  "urn:ietf:params:xml:ns:xmpp-streams"
36 #define NS_TLS      "urn:ietf:params:xml:ns:xmpp-tls"
37 #define NS_SASL     "urn:ietf:params:xml:ns:xmpp-sasl"
38 #define NS_SESSION  "urn:ietf:params:xml:ns:xmpp-session"
39 #define NS_STANZAS  "urn:ietf:params:xml:ns:xmpp-stanzas"
40 #define NS_BIND     "urn:ietf:params:xml:ns:xmpp-bind"
41 #define NS_COMPRESS_FEATURE "http://jabber.org/features/compress"
42 #define NS_COMPRESS_PROTOCOL "http://jabber.org/protocol/compress"
43 #define NS_HOSTS    "http://barracuda.com/xmppextensions/hosts"
44 
45 namespace XMPP
46 {
47 	class Version
48 	{
49 	public:
50 		Version(int maj=0, int min=0);
51 
52 		int major, minor;
53 	};
54 
55 	class StreamFeatures
56 	{
57 	public:
58 		StreamFeatures();
59 
60 		bool tls_supported, sasl_supported, bind_supported, compress_supported;
61 		bool tls_required;
62 		QStringList sasl_mechs;
63 		QStringList compression_mechs;
64 		QStringList hosts;
65 	};
66 
67 	class BasicProtocol : public XmlProtocol
68 	{
69 	public:
70 		// xmpp 1.0 error conditions
71 		enum SASLCond {
72 			Aborted,
73 			IncorrectEncoding,
74 			InvalidAuthzid,
75 			InvalidMech,
76 			MechTooWeak,
77 			NotAuthorized,
78 			TemporaryAuthFailure
79 		};
80 		enum StreamCond {
81 			BadFormat,
82 			BadNamespacePrefix,
83 			Conflict,
84 			ConnectionTimeout,
85 			HostGone,
86 			HostUnknown,
87 			ImproperAddressing,
88 			InternalServerError,
89 			InvalidFrom,
90 			InvalidId,
91 			InvalidNamespace,
92 			InvalidXml,
93 			StreamNotAuthorized,
94 			PolicyViolation,
95 			RemoteConnectionFailed,
96 			ResourceConstraint,
97 			RestrictedXml,
98 			SeeOtherHost,
99 			SystemShutdown,
100 			UndefinedCondition,
101 			UnsupportedEncoding,
102 			UnsupportedStanzaType,
103 			UnsupportedVersion,
104 			XmlNotWellFormed
105 		};
106 		enum BindCond {
107 			BindBadRequest,
108 			BindNotAllowed,
109 			BindConflict
110 		};
111 
112 		// extend the XmlProtocol enums
113 		enum Need {
114 			NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist
115 			NStartTLS,  // need to switch on TLS layer
116 			NCompress,  // need to switch on compression layer
117 			NSASLFirst, // need SASL first step
118 			NSASLNext,  // need SASL next step
119 			NSASLLayer, // need to switch on SASL layer
120 			NCustom = XmlProtocol::NCustom+10
121 		};
122 		enum Event {
123 			EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received
124 			ESASLSuccess, // breakpoint after successful sasl auth
125 			EStanzaReady, // a stanza was received
126 			EStanzaSent,  // a stanza was sent
127 			EReady,       // stream is ready for stanza use
128 			ECustom = XmlProtocol::ECustom+10
129 		};
130 		enum Error {
131 			ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange
132 			ErrStream,   // <stream:error>, see errCond, errText, and errAppSpec for details
133 			ErrStartTLS, // server refused starttls
134 			ErrCompress, // server refused compression
135 			ErrAuth,     // authorization error.  errCond holds sasl condition (or numeric code for old-protocol)
136 			ErrBind,     // server refused resource bind
137 			ErrCustom = XmlProtocol::ErrCustom+10
138 		};
139 
140 		BasicProtocol();
141 		~BasicProtocol() override;
142 
143 		void reset() override;
144 
145 		// for outgoing xml
146 		QDomDocument doc;
147 
148 		// sasl-related
149 		QString saslMech() const;
150 		QByteArray saslStep() const;
151 		void setSASLMechList(const QStringList &list);
152 		void setSASLFirst(const QString &mech, const QByteArray &step);
153 		void setSASLNext(const QByteArray &step);
154 		void setSASLAuthed();
155 
156 		// send / recv
157 		void sendStanza(const QDomElement &e);
158 		void sendDirect(const QString &s);
159 		void sendWhitespace();
160 		QDomElement recvStanza();
161 
162 		// shutdown
163 		void shutdown();
164 		void shutdownWithError(int cond, const QString &otherHost="");
165 
166 		// <stream> information
167 		QString to, from, id, lang;
168 		Version version;
169 
170 		// error output
171 		int errCond;
172 		QString errText;
173 		QDomElement errAppSpec;
174 		QString otherHost;
175 
176 		QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer
177 
178 		bool isReady() const;
179 
180 		enum { TypeElement, TypeStanza, TypeDirect, TypePing };
181 
182 	protected:
183 		static int stringToSASLCond(const QString &s);
184 		static int stringToStreamCond(const QString &s);
185 		static QString saslCondToString(int);
186 		static QString streamCondToString(int);
187 
188 		void send(const QDomElement &e, bool clip=false);
189 		void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
190 		void sendStreamError(const QString &text); // old-style
191 
192 		bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
193 		bool error(int code);
194 		void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
195 		void delayError(int code);
196 
197 		// reimplemented
198 		QDomElement docElement() override;
199 		void handleDocOpen(const Parser::Event &pe) override;
200 		bool handleError() override;
201 		bool handleCloseFinished() override;
202 		bool doStep(const QDomElement &e) override;
203 		void itemWritten(int id, int size) override;
204 
205 		virtual QString defaultNamespace();
206 		virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...]
207 		virtual void handleStreamOpen(const Parser::Event &pe);
208 		virtual bool doStep2(const QDomElement &e)=0;
209 
210 		void setReady(bool b);
211 
212 		QString sasl_mech;
213 		QStringList sasl_mechlist;
214 		QByteArray sasl_step;
215 		bool sasl_authed;
216 
217 		QDomElement stanzaToRecv;
218 
219 	private:
220 		struct SASLCondEntry
221 		{
222 			const char *str;
223 			int cond;
224 		};
225 		static SASLCondEntry saslCondTable[];
226 
227 		struct StreamCondEntry
228 		{
229 			const char *str;
230 			int cond;
231 		};
232 		static StreamCondEntry streamCondTable[];
233 
234 		struct SendItem
235 		{
236 			QDomElement stanzaToSend;
237 			QString stringToSend;
238 			bool doWhitespace;
239 		};
240 		QList<SendItem> sendList;
241 
242 		bool doShutdown, delayedError, closeError, ready;
243 		int stanzasPending, stanzasWritten;
244 
245 		void init();
246 		void extractStreamError(const QDomElement &e);
247 	};
248 
249 	class CoreProtocol : public BasicProtocol
250 	{
251 	public:
252 		enum {
253 			NPassword = NCustom,  // need password for old-mode
254 			EDBVerify = ECustom,  // breakpoint after db:verify request
255 			ErrPlain = ErrCustom  // server only supports plain, but allowPlain is false locally
256 		};
257 
258 		CoreProtocol();
259 		~CoreProtocol() override;
260 
261 		void reset() override;
262 
263 		void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth, bool doCompression);
264 		void startServerOut(const QString &to);
265 		void startDialbackOut(const QString &to, const QString &from);
266 		void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key);
267 		void startClientIn(const QString &id);
268 		void startServerIn(const QString &id);
269 
270 		void setLang(const QString &s);
271 		void setAllowTLS(bool b);
272 		void setAllowBind(bool b);
273 		void setAllowPlain(bool b); // old-mode
274 		const Jid& jid() const;
275 
276 		void setPassword(const QString &s);
277 		void setFrom(const QString &s);
278 		void setDialbackKey(const QString &s);
279 
280 		// input
281 		QString user, host;
282 
283 		// status
284 		bool old;
285 
286 		StreamFeatures features;
287 		QStringList hosts;
288 
289 		//static QString xmlToString(const QDomElement &e, bool clip=false);
290 
291 		class DBItem
292 		{
293 		public:
294 			enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated };
295 			int type;
296 			Jid to, from;
297 			QString key, id;
298 			bool ok;
299 		};
300 
301 	private:
302 		enum Step {
303 			Start,
304 			Done,
305 			SendFeatures,
306 			GetRequest,
307 			HandleTLS,
308 			GetSASLResponse,
309 			IncHandleSASLSuccess,
310 			GetFeatures,        // read features packet
311 			HandleFeatures,     // act on features, by initiating tls, sasl, or bind
312 			GetTLSProceed,      // read <proceed/> tls response
313 			GetCompressProceed, // read <compressed/> compression response
314 			GetSASLFirst,       // perform sasl first step using provided data
315 			GetSASLChallenge,   // read server sasl challenge
316 			GetSASLNext,        // perform sasl next step using provided data
317 			HandleSASLSuccess,  // handle what must be done after reporting sasl success
318 			GetBindResponse,    // read bind response
319 			HandleAuthGet,      // send old-protocol auth-get
320 			GetAuthGetResponse, // read auth-get response
321 			HandleAuthSet,      // send old-protocol auth-set
322 			GetAuthSetResponse  // read auth-set response
323 		};
324 
325 		QList<DBItem> dbrequests, dbpending, dbvalidated;
326 
327 		bool server, dialback, dialback_verify;
328 		int step;
329 
330 		bool digest;
331 		bool tls_started, sasl_started, compress_started;
332 
333 		Jid jid_;
334 		bool oldOnly;
335 		bool allowPlain;
336 		bool doTLS, doAuth, doBinding, doCompress;
337 		QString password;
338 
339 		QString dialback_id, dialback_key;
340 		QString self_from;
341 
342 		void init();
343 		static int getOldErrorCode(const QDomElement &e);
344 		bool loginComplete();
345 
346 		bool isValidStanza(const QDomElement &e) const;
347 		bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item);
348 		bool normalStep(const QDomElement &e);
349 		bool dialbackStep(const QDomElement &e);
350 
351 		// reimplemented
352 		bool stepAdvancesParser() const override;
353 		bool stepRequiresElement() const override;
354 		void stringSend(const QString &s) override;
355 		void stringRecv(const QString &s) override;
356 		QString defaultNamespace() override;
357 		QStringList extraNamespaces() override;
358 		void handleStreamOpen(const Parser::Event &pe) override;
359 		bool doStep2(const QDomElement &e) override;
360 		void elementSend(const QDomElement &e) override;
361 		void elementRecv(const QDomElement &e) override;
362 	};
363 }
364 
365 #endif
366