1 /*
2  * sxesession.h - Sxe Session
3  * Copyright (C) 2007  Joonas Govenius
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  */
20 
21 #ifndef SXDESESSION_H
22 #define SXDESESSION_H
23 
24 #define SXENS "http://jabber.org/protocol/sxe"
25 /*  ^^^^ make sure corresponds to NS used for parsing in iris/src/xmpp/xmpp-im/types.cpp ^^^^ */
26 
27 #include <QObject>
28 #include <QList>
29 #include <QPointer>
30 #include <QDomNode>
31 
32 #include "im.h"
33 #include "psiaccount.h"
34 #include "sxerecord.h"
35 
36 namespace XMPP {
37 	class Client;
38 	class Jid;
39 	class Message;
40 }
41 
42 using namespace XMPP;
43 
44 class SxeManager;
45 
46 /*! \brief Class for storing the record and the XML document for an established SXE session.*/
47 class SxeSession : public QObject {
48 	Q_OBJECT
49 
50 		// Make SxeManager a friend class so it can emit peerJoined/Left session.
51 		friend class SxeManager;
52 
53 	private:
54 		struct IncomingEdit {
55 			QString id;
56 			QDomElement xml;
57 		};
58 
59 	public:
60 		/*! \brief Constructor.
61 		*  Creates a new session for the specified jid and session identifier.
62 		*/
63 		SxeSession(SxeManager *manager, const Jid &target, const QString &session, const Jid &ownJid, bool groupChat, bool serverSupport, const QList<QString> &features);
64 		/*! \brief Destructor.
65 		*  Emits sessionEnded()
66 		*/
67 		~SxeSession();
68 
69 		/*! \brief Initializes the shared document. Only used if starting a new session; not when joining one. */
70 		void initializeDocument(const QDomDocument &doc);
71 		/*! \brief Processes the incoming SXE element and remembers its identifying information.*/
72 		void processIncomingSxeElement(const QDomElement &, const QString &id);
73 		/*! \brief Returns a const reference to the target document.*/
74 		const QDomDocument& document() const;
75 		/*! \brief Returns true if the target is a groupchat.*/
76 		bool groupChat() const;
77 		/*! \brief Returns true if the target is a groupchat.*/
78 		bool serverSupport() const;
79 		/*! \brief Returns the target contact's JID.*/
80 		const Jid target() const;
81 		/*! \brief Returns the session identifier.*/
82 		const QString session() const;
83 		/*! \brief Returns the JID used by the user in the session.*/
84 		const Jid ownJid() const;
85 		/*! \brief Returns the session identifier.*/
86 		const QList<QString> features() const;
87 
88 		/*! \brief Starts queueing new edits to the document.
89 		 *  Queueing should be started just before sending <document-begin/>.
90 		 */
91 		QList<const SxeEdit*> startQueueing();
92 		/*! \brief Stop queueing new edits to the document and process the queued ones.
93 		 *  Queueing should be stopped after sending <document-end/>.
94 		 */
95 		void stopQueueing();
96 
97 		/*! \brief Initializes the document with the prolog of \a doc if provided.
98 		 * Should be used when <document-begin/> is received when joining.
99 		 */
100 		void startImporting(const QDomDocument &doc = QDomDocument());
101 		/*! \brief Enters the normal editing mode.
102 		 * Should be used when <document-end/> is received when joining.
103 		 */
104 		void stopImporting();
105 
106 		/*! \brief Add the given ID to the list of used IDs for <sxe/> elements.*/
107 		void addUsedSxeId(QString id);
108 		/*! \brief Return the list of used IDs for <sxe/> elements.*/
109 		QList<QString> usedSxeIds();
110 
111 		void setUUIDPrefix(const QString uuidPrefix = QString());
112 		/*! \brief Returns a random UUID without enclosing { }. */
113 		static QString generateUUID();
114 		/*! \brief Returns the prolog of the document as a string. */
115 		static QString parseProlog(const QDomDocument &doc);
116 
117 	public slots:
118 		/*! \brief Ends the session.*/
119 		void endSession();
120 		/*! \brief Inserts or moves the given node so that it is before the reference element.
121 		 *  If the reference element is the null element the element is inserted as the first child of the parent.
122 		 *  Returns \a node if the node was already in the document. Otherwise returns the created node.
123 		 */
124 		const QDomNode insertNodeBefore(const QDomNode &node, const QDomNode &parent, const QDomNode &referenceNode = QDomNode());
125 		/*! \brief Inserts or moves the given node so that it is after the reference element.
126 		 *  If the reference element is the null element the element is inserted as the last child of the parent.
127 		 *  Returns \a node if the node was already in the document. Otherwise returns the created node.
128 		 */
129 		const QDomNode insertNodeAfter(const QDomNode &node, const QDomNode &parent, const QDomNode &referenceNode = QDomNode());
130 		/*! \brief Removes the given element.*/
131 		void removeNode(const QDomNode &node);
132 		/*! \brief Sets the value of \a attribute of \a node to \a value. */
133 		void setAttribute(const QDomNode &node, const QString &attribute, const QString &value, int from = -1, int n = 0);
134 		/*! \brief Sets the value of \a node to \a value. */
135 		void setNodeValue(const QDomNode &node, const QString &value, int from = -1, int n = 0);
136 
137 		/*! \brief Sends all queued edits.*/
138 		void flush();
139 
140 	signals:
141 		/*! \brief used to pass the new <sxe/> elements to sxemanager.*/
142 		void newSxeElement(const QDomElement &element, const Jid &, bool groupChat);
143 
144 		/*! \brief Emitted after each processed SXE element.*/
145 		void documentUpdated(bool remote);
146 		/*! \brief Emitted just before \a node is inserted.*/
147 		void nodeToBeAdded(const QDomNode &node, bool remote);
148 		/*! \brief Emitted after \a node is inserted.*/
149 		void nodeAdded(const QDomNode &node, bool remote);
150 		/*! \brief Emitted just before \a node is moved in the document tree due to processing of an SXE element.*/
151 		void nodeToBeMoved(const QDomNode &node, bool remote);
152 		/*! \brief Emitted after \a node has been moved in the document tree due to processing of an SXE element.*/
153 		void nodeMoved(const QDomNode &node, bool remote);
154 		/*! \brief Emitted just before \a node is removed from the document tree due to processing of an SXE element.*/
155 		void nodeToBeRemoved(const QDomNode &node, bool remote);
156 		/*! \brief Emitted after \a node has been removed from the document tree due to processing of an SXE element.*/
157 		void nodeRemoved(const QDomNode &node, bool remote);
158 		/*! \brief Emitted when the name of \a node has been changed. */
159 		// void nameChanged(const QDomNode &node, bool remote);
160 		/*! \brief Emitted just before the chdata of \a node is changed. */
161 		void chdataToBeChanged(const QDomNode &node, bool remote);
162 		/*! \brief Emitted when the chdata of \a node has been changed. */
163 		void chdataChanged(const QDomNode &node, bool remote);
164 
165 		/*! \brief Signals that a peer joined the session.*/
166 		void peerJoinedSession(const Jid &);
167 		/*! \brief Signals that a peer left the session.*/
168 		void peerLeftSession(const Jid &);
169 		/*! \brief Signals that the session ended and the session is to be deleted.*/
170 		void sessionEnded(SxeSession*);
171 
172 	private slots:
173 		/*! \brief Adds \a node to the document tree and emits the appropriate public signals. */
174 		void handleNodeToBeAdded(const QDomNode &node, bool remote);
175 		/*! \brief Moves \a node in the document tree and emits the appropriate public signals. */
176 		void handleNodeToBeMoved(const QDomNode &node, bool remote);
177 		/*! \brief Remove the record entry from the lookup tables and emit the appropriate public signals. */
178 		void handleNodeToBeRemoved(const QDomNode &node, bool remote);
179 		/*! \brief Add a node node to the lookup table. */
180 		// void addToLookup(const QDomNode &node, bool, const QString &rid);
181 
182 	private:
183 		/*! \brief Inserts or moves a node according to it's record (parent and primary-weight). */
184 		void reposition(const QDomNode &node, bool remote);
185 		/*! \brief Remove the record associated with \a node from the lookup tables. */
186 		void removeRecord(const QDomNode &node);
187 		/*! \brief Remove the item with smaller secondary weight.
188 			Returns true iff \a meta1 was removed. */
189 		bool removeSmaller(SxeRecord* meta1, SxeRecord* meta2);
190 		/*! \brief Processes an incoming sxe element.*/
191 		bool processSxe(const QDomElement &sxe, const QString &id);
192 		/*! \brief Queues an outgoing edit to be sent when flushed.*/
193 		void queueOutgoingEdit(SxeEdit* edit);
194 		/*! \brief Creates the record of node with rid \a id. Returns a pointer to it. */
195 		SxeRecord* createRecord(const QString &id);
196 		/*! \brief Returns a pointer to the record of node with rid \a id. */
197 		SxeRecord* record(const QString &id);
198 		/*! \brief Returns a pointer to the record of \a node. */
199 		SxeRecord* record(const QDomNode &node) const;
200 		/*! \brief Generates SxeNewEdits for \a node and its children.
201 		 *  Returns the created node. */
202 		QDomNode generateNewNode(const QDomNode node, const QString &parent, double primaryWeight);
203 		/*! \brief Generates SxeRemoveEdits for \a node and its children. */
204 		void generateRemoves(const QDomNode &node);
205 		/*! \brief Recursive helper method for arranging edits for the snapshot. */
206 		void arrangeEdits(QHash<QString, QString> &ridByParent, QList<const SxeEdit*> &output, const QString &iterator);
207 		/*! \brief Insert node with the given primaryWeight.
208 		 *  Returns node if the node was already in the document. Otherwise returns the created node. */
209 		const QDomNode insertNode(const QDomNode &node, const QString &parentId, double primaryWeight);
210 		/*! \brief Returns a random UUID without enclosing { } and checks that it's not used as a rid.
211 			Necessary because you often generate the same UUID on two different processes on the same computer (non-windows). */
212 		QString generateUUIDForSession();
213 
214 		/*! \brief The string identifying the session.*/
215 		QString session_;
216 		/*! \brief The target JID.*/
217 		Jid target_;
218 		/*! \brief The target JID.*/
219 		Jid ownJid_;
220 		/*! \brief The target XML document.*/
221 		QDomDocument document_;
222 
223 		/*! \brief Hash used for rid -> SxeRecord* lookups.*/
224 		QHash<
225 				QString,
226 				SxeRecord*
227 			 > recordByNodeId_;
228 		/*! \brief List of queued incoming sxe elements.*/
229 		QList<IncomingEdit> queuedIncomingEdits_;
230 		/*! \brief List of queued outgoing sxe elements.*/
231 		QList<QDomNode> queuedOutgoingEdits_;
232 		/*! \brief QDomDocument representing the the contents when queueing_ was set true.*/
233 		QList<SxeEdit*> snapshot_;
234 		/*! \brief True if the target is a groupchat.*/
235 		bool groupChat_;
236 		/*! \brief True if the session was not established with a server supporting SXE.*/
237 		bool serverSupport_;
238 		/*! \brief If true, new sxe elements are queued rather than processed.*/
239 		bool queueing_;
240 		/*! \brief True while initial contents of the document are being imported.*/
241 		bool importing_;
242 		/*! \brief A list of supported features for the session.*/
243 		QList<QString> features_;
244 		 /*! \brief Identifiers for the <sxe/> elements that have been processed already.*/
245 		QList<QString> usedSxeIds_;
246 		/*! \brief A unique id is generated as "uuidPrefix.counter".*/
247 		QString uuidPrefix_;
248 		int uuidMaxPostfix_;
249 		/*! \brief The main DOM document.*/
250 		QDomDocument doc_;
251 };
252 
253 #endif
254