~ubuntu-branches/ubuntu/karmic/psi/karmic

« back to all changes in this revision

Viewing changes to src/wbmanager.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2008-04-14 18:57:30 UTC
  • mfrom: (2.1.9 hardy)
  • Revision ID: james.westby@ubuntu.com-20080414185730-528re3zp0m2hdlhi
Tags: 0.11-8
* added CONFIG -= link_prl to .pro files and removed dependencies
  which are made unnecessary by this change
* Fix segfault when closing last chat tab with qt4.4
  (This is from upstream svn, rev. 1101) (Closes: Bug#476122)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * wbmanager.cpp - Whiteboard manager
 
3
 * Copyright (C) 2006  Joonas Govenius
 
4
 *
 
5
 * Influenced by:
 
6
 * pepmanager.cpp - Classes for PEP
 
7
 * Copyright (C) 2006  Remko Troncon
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or
 
10
 * modify it under the terms of the GNU General Public License
 
11
 * as published by the Free Software Foundation; either version 2
 
12
 * of the License, or (at your option) any later version.
 
13
 *
 
14
 * This program is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 * GNU General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU General Public License
 
20
 * along with this library; if not, write to the Free Software
 
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
22
 *
 
23
 */
 
24
 
 
25
#include "wbmanager.h"
 
26
#include "psipopup.h"
 
27
#include <QDebug>
 
28
 
 
29
using namespace XMPP;
 
30
 
 
31
// -----------------------------------------------------------------------------
 
32
 
 
33
WbManager::WbManager(Client* client, PsiAccount* pa) : client_(client) {
 
34
        wbHash_ = QTime::currentTime().toString("z").toInt();
 
35
 
 
36
        // Supported SVG features
 
37
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#CoreAttribute");
 
38
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#BasicStructure");
 
39
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute");
 
40
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#Shape");
 
41
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#BasicText");
 
42
        supportedFeatures_.append("http://www.w3.org/TR/SVG11/feature#Image");
 
43
 
 
44
        pa_ = pa;
 
45
        connect(client_, SIGNAL(messageReceived(const Message &)), SLOT(messageReceived(const Message &)));
 
46
        connect(client_, SIGNAL(groupChatLeft(const Jid &)), SLOT(groupChatLeft(const Jid &)));
 
47
        connect(client_, SIGNAL(groupChatJoined(const Jid &)), SLOT(groupChatJoined(const Jid &)));
 
48
 
 
49
        negotiationTimer_.setSingleShot(true);
 
50
        negotiationTimer_.setInterval(120000);
 
51
        connect(&negotiationTimer_, SIGNAL(timeout()), SLOT(negotiationTimeout()));
 
52
}
 
53
 
 
54
void WbManager::openWhiteboard(const Jid &target, const Jid &ownJid, bool groupChat) {
 
55
        // See if we have a session for the JID
 
56
        WbDlg* w = findWbDlg(target);
 
57
        // else negotiate a new session and return null
 
58
        if(!w)
 
59
                startNegotiation(target, ownJid, groupChat);
 
60
        else
 
61
                bringToFront(w);
 
62
}
 
63
 
 
64
void WbManager::messageReceived(const Message &message) {
 
65
        // only process messages that contain a <wb/> with a nonempty 'session' attribute and that are addressed to this particular account
 
66
        if(!message.whiteboard().attribute("session").isEmpty()) {
 
67
                // skip messages from self
 
68
                if(ownJids_.contains(message.from().full())) {
 
69
                        qDebug("from self");
 
70
                        return;
 
71
                }
 
72
                bool recordSessionId = false;
 
73
                // Don't process delayed messages (chat history) but remember the session id
 
74
                if(!message.spooled()) {
 
75
        //              qDebug() << (QString("<wb/> to session %1 from %2").arg(message.whiteboard().attribute("session")).arg(message.from().full()).toAscii());
 
76
                        WbDlg* w = 0;
 
77
                        // Check if the <wb/> contains a <protocol/>
 
78
                        QDomNodeList children = message.whiteboard().childNodes();
 
79
                        for(uint i=0; i < children.length(); i++) {
 
80
                                if(children.item(i).nodeName() == "protocol") {
 
81
                                        w = negotiateSession(message);
 
82
                                }
 
83
                        }
 
84
                        // try finding a matching dialog for the session if new one not negotiated
 
85
                        if(!w) {
 
86
                                w = findWbDlg(message.whiteboard().attribute("session"));
 
87
                                if(w) {
 
88
                                        // only pass the message to existing sessions, not to newly negotiated ones.
 
89
                                        w->incomingWbElement(message.whiteboard(), message.from());
 
90
                                } else
 
91
                                        recordSessionId = true;
 
92
                        }
 
93
                        if (w) {
 
94
        //                      PsiPopup::PopupType popupType = PsiPopup::AlertNone;
 
95
        //                      bool doPopup = false;
 
96
                                pa_->playSound(option.onevent[eChat2]);
 
97
        //                      if(!w->isActiveWindow() || option.alertOpenChats) {
 
98
        //                              popupType = PsiPopup::AlertChat;
 
99
        //                              if(option.popupChats) {
 
100
        //                                      if(!option.noUnlistedPopup && message.type() != "groupchat") {
 
101
        //                                              // don't popup wb's from unlisted contacts
 
102
        //                                              if(!pa_->find(message.from()))
 
103
        //                                                      return;
 
104
        //                                      }
 
105
                                                w->show();
 
106
        //                              }
 
107
        //                      }
 
108
                                if(option.raiseChatWindow) {
 
109
                                        bringToFront(w);
 
110
                                }
 
111
        //                      if ((popupType == PsiPopup::AlertChat && option.ppChat) /*&& makeSTATUS(status()) != STATUS_DND*/) {
 
112
        //                              PsiPopup *popup = new PsiPopup(popupType, pa_);
 
113
        //                              popup->setData(j, r, u, e);
 
114
        //                      }
 
115
        // #if defined(Q_WS_MAC) && defined(HAVE_GROWL)
 
116
        //                      PsiGrowlNotifier::instance()->popup(this, popupType, j, r, u, e);
 
117
        // #endif
 
118
                        }
 
119
                } else
 
120
                        recordSessionId = true;
 
121
                if(recordSessionId) {
 
122
                        // check if a record of the session exists
 
123
                        bool alreadyRecorded = false;
 
124
                        foreach(detectedSession d, detectedSessions_) {
 
125
                                if(d.session == message.whiteboard().attribute("session")) {
 
126
                                        alreadyRecorded = true;
 
127
                                        break;
 
128
                                }
 
129
                        }
 
130
                        if(!alreadyRecorded) {
 
131
                                // store a record of a detected session
 
132
                                detectedSession detected;
 
133
                                detected.session = message.whiteboard().attribute("session");
 
134
                                if(message.type() == "groupchat")
 
135
                                        detected.jid = message.from().bare();
 
136
                                else
 
137
                                        detected.jid = message.from();
 
138
                                detected.time = QTime::currentTime();
 
139
                                detectedSessions_.append(detected);
 
140
                        }
 
141
                }
 
142
        }
 
143
}
 
144
 
 
145
void WbManager::removeSession(const QString &session)
 
146
{
 
147
        WbDlg* d = 0;
 
148
        for(int i = 0; i < dialogs_.size(); i++) {
 
149
                // does the session match?
 
150
                if(dialogs_.at(i)->session() == session) {
 
151
                        d = dialogs_.takeAt(i);
 
152
 
 
153
                        QDomDocument doc;
 
154
                        QDomElement wb = doc.createElementNS("http://jabber.org/protocol/svgwb", "wb");
 
155
                        wb.setAttribute("session", session);
 
156
                        QDomElement protocol = doc.createElement("protocol");
 
157
                        protocol.appendChild(doc.createElement("left-session"));
 
158
                        wb.appendChild(protocol);
 
159
                        sendMessage(wb, d->target(), d->groupChat());
 
160
                        break;
 
161
                }
 
162
        }
 
163
        if(d) {
 
164
                if(negotiations_.contains(session)) {
 
165
                        sendAbortNegotiation(session, negotiations_[session]->peer, d->groupChat());
 
166
                        negotiations_.remove(session);
 
167
                }
 
168
                // FIXME: Delete the dialog
 
169
                d->setAttribute(Qt::WA_DeleteOnClose);
 
170
                d->close();
 
171
//              d->deleteLater();
 
172
 
 
173
        }
 
174
}
 
175
 
 
176
WbDlg* WbManager::negotiateSession(const Message &message) {
 
177
        QString session = message.whiteboard().attribute("session");
 
178
        if(session.isEmpty())
 
179
                return 0;
 
180
 
 
181
        if(option.ignoreNonRoster && message.type() != "groupchat") {
 
182
                // Ignore the message if contact not in roster
 
183
                if(!pa_->find(message.from())) {
 
184
                        qDebug("Whiteboard invitation received from contact that is not in roster.");
 
185
                        return 0;
 
186
                }
 
187
        }
 
188
 
 
189
        QDomDocument doc;
 
190
        QDomElement wb = doc.createElementNS("http://jabber.org/protocol/svgwb", "wb");
 
191
        wb.setAttribute("session", session);
 
192
        QDomElement protocol = doc.createElement("protocol");
 
193
        wb.appendChild(protocol);
 
194
 
 
195
        // Find or create a negotiation process
 
196
qDebug("1");
 
197
        WbNegotiation* negotiation = negotiations_[session];
 
198
        if(negotiation) {
 
199
                // Only accept further negotiation messages from the source we are already negotiationing with or if we've requested connection to a groupchat session
 
200
                if(!negotiation->peer.compare(message.from()) && !(negotiation->groupChat && negotiation->peer.resource().isEmpty())) {
 
201
                        qDebug("1.1");
 
202
//                      if(negotiation->groupChat) {
 
203
//                              // If it is a history offer, turn it down
 
204
//                              for(uint i = 0; i < message.whiteboard().childNodes().length(); i++) {
 
205
//                                      if(message.whiteboard().childNodes().at(i).nodeName() == "protocol") {
 
206
//                                              for(uint j = 0; j < message.whiteboard().childNodes().at(i).childNodes().length(); j++) {
 
207
//                                                      if(message.whiteboard().childNodes().at(i).childNodes().at(j).nodeName() == "history-offer") {
 
208
                                                                sendAbortNegotiation(session, message.from(), true);
 
209
//                                                      }
 
210
//                                              }
 
211
//                                      }
 
212
//                              }
 
213
//                      }
 
214
                        return 0;
 
215
                }
 
216
        } else {
 
217
// qDebug("2");
 
218
                negotiation = new WbNegotiation;
 
219
                negotiation->dialog = findWbDlg(session);
 
220
                negotiation->peer = message.from();
 
221
                if(negotiation->dialog) {
 
222
// qDebug("3");
 
223
                        negotiation->role = WbNegotiation::Participant;
 
224
                        negotiation->state = WbNegotiation::Finished;
 
225
                        negotiation->target = negotiation->dialog->target();
 
226
                        negotiation->groupChat = negotiation->dialog->groupChat();
 
227
                        negotiation->ownJid = negotiation->dialog->ownJid();
 
228
                } else {
 
229
                        if(message.type() == "groupchat") {
 
230
                                negotiation->groupChat = true;
 
231
                                // If we're being invited from a groupchat,
 
232
                                // ownJid is determined based on the "bare" part of ownJids_
 
233
                                foreach(QString j, ownJids_) {
 
234
                                        if(message.from().bare() == j.left(j.indexOf("/"))) {
 
235
                                                negotiation->ownJid = j;
 
236
                                                break;
 
237
                                        }
 
238
                                }
 
239
                                // Also, the target is just the "bare" JID in a groupchat
 
240
                                negotiation->target = message.from().bare();
 
241
                        } else {
 
242
                                negotiation->groupChat = false;
 
243
                                negotiation->target = negotiation->peer;
 
244
                        }
 
245
                        if(negotiation->ownJid.isEmpty())
 
246
                                negotiation->ownJid = message.to();
 
247
                        negotiation->role = WbNegotiation::Joiner;
 
248
                        negotiation->state = WbNegotiation::NotStarted;
 
249
                }
 
250
                // Reset the timeout
 
251
                negotiationTimer_.start();
 
252
                // Store the session
 
253
                negotiations_[session] = negotiation;
 
254
        }
 
255
 
 
256
        // Process the children of the <wb/>
 
257
        QDomNode n, m;
 
258
        for(int i = 0; i < message.whiteboard().childNodes().count(); i++) {
 
259
                n = message.whiteboard().childNodes().at(i);
 
260
// qDebug("4.121");
 
261
                if(!n.isElement())
 
262
                        continue;
 
263
// qDebug("4.12");
 
264
                if(n.nodeName() == "protocol") {
 
265
// qDebug("4.123");
 
266
                        for(int j = 0; j < n.childNodes().count(); j++) {
 
267
                                m = n.childNodes().at(j);
 
268
// qDebug("4.1234");
 
269
//                              protocol = protocol.cloneNode(false).toElement();
 
270
                                while(protocol.hasChildNodes())
 
271
                                        protocol.removeChild(protocol.firstChild());
 
272
                                // Check if <protocol/> contains <left-session/>
 
273
                                if(m.nodeName() == "left-session") {
 
274
                                        if(negotiation->state == WbNegotiation::Finished) {
 
275
                                                // Only end session if it's a 1-to-1 session
 
276
                                                if(!negotiation->groupChat) {
 
277
                                                        negotiation->dialog->peerLeftSession();
 
278
                                                }
 
279
                                        }
 
280
                                } else if(m.nodeName() == "abort-negotiation") {
 
281
                                        if(negotiation->role == WbNegotiation::Participant && negotiation->state < WbNegotiation::HistoryOffered && negotiation->state != WbNegotiation::DocumentBegan) {
 
282
                                                // Abort, as in delete session, if still establishing it and not trying to create a new groupchat session
 
283
                                                if(!(negotiation->groupChat && negotiation->peer.resource().isEmpty()))
 
284
                                                        negotiation->state = WbNegotiation::Aborted;
 
285
                                        } else {
 
286
                                                // Just remove the "negotation" but keep the dialog if sending history was cancelled
 
287
                                                negotiation->state = WbNegotiation::Finished;
 
288
                                        }
 
289
                                } else if(m.nodeName() == "invitation" && negotiation->state == WbNegotiation::NotStarted) {
 
290
// qDebug("4.1234567");
 
291
                                        bool accept = true;
 
292
                                        // Reject invitations from a source that we already have a session to
 
293
                                        if(findWbDlg(negotiation->target))
 
294
                                                accept = false;
 
295
                                        else {
 
296
                                                // Check if the proposed features are supported
 
297
                                                for(uint k = 0; k < m.childNodes().length(); k++) {
 
298
                                                        if(m.childNodes().at(k).nodeName() == "feature") {
 
299
                                                                if(!supportedFeatures_.contains(m.childNodes().at(k).toElement().text()))
 
300
                                                                        accept = false;
 
301
                                                        }
 
302
                                                }
 
303
                                        }
 
304
                                        if(accept) {
 
305
                                                protocol.appendChild(doc.createElement("accept-invitation"));
 
306
                                                negotiation->state = WbNegotiation::InvitationAccepted;
 
307
                                        } else {
 
308
                                                protocol.appendChild(doc.createElement("abort-negotiation"));
 
309
                                                negotiation->state = WbNegotiation::Aborted;
 
310
                                        }
 
311
                                } else if(m.nodeName() == "connect-request" && negotiation->state == WbNegotiation::Finished) {
 
312
                                        negotiation->state = WbNegotiation::HistoryOffered;
 
313
                                        protocol.appendChild(doc.createElement("history-offer"));
 
314
// qDebug("7");
 
315
                                } else if(m.nodeName() == "history-offer" && negotiation->state == WbNegotiation::ConnectionRequested) {
 
316
// qDebug("5");
 
317
                                        negotiation->state = WbNegotiation::HistoryAccepted;
 
318
                                        negotiation->peer = message.from();
 
319
                                        protocol.appendChild(doc.createElement("accept-history"));
 
320
                                } else if(m.nodeName() == "document-begin" && (negotiation->state == WbNegotiation::HistoryAccepted || negotiation->state == WbNegotiation::InvitationAccepted)) {
 
321
                                        if(!negotiation->dialog) {
 
322
//                                              qDebug() << QString("%1  %2  %3  %4").arg(negotiation->target.full()).arg(session).arg(negotiation->ownJid.full());
 
323
                                                negotiation->dialog = createWbDlg(negotiation->target, session, negotiation->ownJid, negotiation->groupChat);
 
324
                                        }
 
325
//                                      negotiation->dialog->setAllowEdits(false);
 
326
                                        if(negotiation->dialog) {
 
327
                                                negotiation->dialog->setImporting(true);
 
328
                                                negotiation->state = WbNegotiation::DocumentBegan;
 
329
                                        } else
 
330
                                                negotiation->state = WbNegotiation::Aborted;
 
331
                                } else if(m.nodeName() == "document-end" && negotiation->state == WbNegotiation::DocumentBegan) {
 
332
                                        negotiation->state = WbNegotiation::Finished;
 
333
                                        for(int k = 0; k < m.childNodes().count(); k++) {
 
334
                                                if(m.childNodes().at(k).nodeName() == "last-wb") {
 
335
                                                        QDomElement last = m.childNodes().at(k).toElement();
 
336
                                                        negotiation->dialog->eraseQueueUntil(last.attribute("sender"), last.attribute("hash"));
 
337
                                                        break;
 
338
                                                }
 
339
                                        }
 
340
                                        negotiation->dialog->setImporting(false);
 
341
//                                              negotiation->dialog->setAllowEdits(true);
 
342
                                } else if ((m.nodeName() == "accept-history" && negotiation->state == WbNegotiation::HistoryOffered) || (m.nodeName() == "accept-invitation" && negotiation->state == WbNegotiation::InvitationSent)) {
 
343
// qDebug("8");
 
344
                                        if(!negotiation->dialog) {
 
345
                                                negotiation->dialog = createWbDlg(negotiation->target, session, negotiation->ownJid, negotiation->groupChat);
 
346
                                                if(!negotiation->dialog) {
 
347
                                                        sendAbortNegotiation(session, negotiation->peer, negotiation->groupChat);
 
348
                                                        return 0;
 
349
                                                }
 
350
                                        }
 
351
                                        negotiation->dialog->setQueueing(true);
 
352
                                        // create the wb element with the history of the whiteboard
 
353
                                        QDomElement history = doc.createElementNS("http://jabber.org/protocol/svgwb", "wb");
 
354
                                        history.setAttribute("session", session);
 
355
                                        QDomElement documentBegin = doc.createElement("protocol");
 
356
                                        documentBegin.appendChild(doc.createElement("document-begin"));
 
357
                                        QDomElement documentEnd = doc.createElement("protocol");
 
358
                                        documentEnd.appendChild(doc.createElement("document-end"));
 
359
                                        QDomElement lastWb = doc.createElement("last-wb");
 
360
                                        lastWb.setAttribute("sender", negotiation->dialog->lastWb()["sender"]);
 
361
                                        lastWb.setAttribute("hash", negotiation->dialog->lastWb()["hash"]);
 
362
                                        documentEnd.appendChild(lastWb);
 
363
// qDebug("9");
 
364
                                        // append <document-begin/>
 
365
                                        history.appendChild(documentBegin);
 
366
                                        // append <new/> and <configure/>s or each element
 
367
                                        foreach(WbItem* item, negotiation->dialog->snapshot()) {
 
368
// qDebug("10");
 
369
                                                QString oldParent = item->parentWbItem();
 
370
                                                QDomNode insertReference = history.lastChild();
 
371
                                                QDomElement newElement = item->svg();
 
372
                                                QDomElement configure;
 
373
                                                int version = -1;
 
374
                                                // do each of the undos and simultaneously create <configure/>'s that represent the edits
 
375
                                                for(int k = item->undos.size() - 1; k >= 0; k--) {
 
376
// qDebug("11");
 
377
                                                        // Note: don't remove the undos
 
378
                                                        EditUndo u = item->undos.at(k);
 
379
                                                        // only create one <configure/> per version
 
380
                                                        if(version != u.version) {
 
381
// qDebug("12");
 
382
                                                                // append the <configure/> for previous version
 
383
                                                                if(configure.hasChildNodes()) {
 
384
// qDebug("13");
 
385
                                                                        if(insertReference.isNull()) {
 
386
// qDebug("14");
 
387
                                                                                history.insertBefore(configure.cloneNode(), insertReference);
 
388
                                                                        } else
 
389
                                                                                history.insertAfter(configure.cloneNode(), insertReference);
 
390
                                                                }
 
391
                                                                // create the <configure/> for the older version
 
392
                                                                version = u.version;
 
393
                                                                configure = doc.createElement("configure");
 
394
                                                                configure.setAttribute("target", item->id());
 
395
                                                                configure.setAttribute("version", version);
 
396
                                                        }
 
397
                                                        if(u.type == Edit::AttributeEdit) {
 
398
// qDebug("15");
 
399
                                                                // Create the edit
 
400
                                                                QDomElement attributeEdit = doc.createElement("attribute");
 
401
                                                                attributeEdit.setAttribute("name", u.attribute);
 
402
                                                                attributeEdit.appendChild(doc.createTextNode(newElement.attribute(u.attribute)));
 
403
                                                                configure.insertBefore(attributeEdit, QDomNode());
 
404
                                                                // Do the undo
 
405
                                                                if(u.oldValue.isNull()) {
 
406
// qDebug("16");
 
407
                                                                        newElement.removeAttribute(u.attribute);
 
408
                                                                } else
 
409
                                                                        newElement.setAttribute(u.attribute, u.oldValue);
 
410
                                                        } else if(u.type == Edit::ContentEdit) {
 
411
// qDebug("17");
 
412
                                                                // Create the edit and do the undo
 
413
                                                                QDomElement contentEdit = doc.createElement("content");
 
414
                                                                while(newElement.hasChildNodes()) {
 
415
// qDebug("18");
 
416
                                                                        contentEdit.appendChild(newElement.firstChild());
 
417
                                                                        newElement.removeChild(newElement.firstChild());
 
418
                                                                }
 
419
                                                                configure.insertBefore(contentEdit, QDomNode());
 
420
                                                                for(uint j=0; j < u.oldContent.length(); j++) {
 
421
// qDebug("19");
 
422
                                                                        newElement.appendChild(u.oldContent.at(j));
 
423
                                                                }
 
424
                                                        } else if(u.type == Edit::ParentEdit) {
 
425
// qDebug("20");
 
426
                                                                // Create the edit
 
427
                                                                QDomElement parentEdit = doc.createElement("parent");
 
428
                                                                parentEdit.appendChild(doc.createTextNode(oldParent));
 
429
                                                                configure.insertBefore(parentEdit, QDomNode());
 
430
                                                                // Do the undo
 
431
                                                                oldParent = u.oldParent;
 
432
                                                        }
 
433
                                                }
 
434
// qDebug("21");
 
435
                                                // Append the last <configure/>
 
436
                                                if(configure.hasChildNodes()) {
 
437
// qDebug("22");
 
438
                                                        if(insertReference.isNull()) {
 
439
                                                                history.insertBefore(configure.cloneNode(), insertReference);
 
440
                                                        } else
 
441
                                                                history.insertAfter(configure.cloneNode(), insertReference);
 
442
                                                }
 
443
// qDebug("23");
 
444
                                                // Create and insert the <new/> element before the <configure/>s created above.
 
445
                                                if(item->id() != "root") {
 
446
                                                        QDomElement newEdit = doc.createElement("new");
 
447
                                                        newEdit.setAttribute("id", item->id());
 
448
                                                        newEdit.setAttribute("parent", oldParent);
 
449
                                                        newEdit.setAttribute("index", item->index());
 
450
                                                        newEdit.appendChild(newElement);
 
451
                                                        if(insertReference.isNull()) {
 
452
// qDebug("24");
 
453
                                                                history.insertBefore(newEdit, insertReference);
 
454
                                                        } else
 
455
                                                                history.insertAfter(newEdit, insertReference);
 
456
                                                }
 
457
                                        }
 
458
// qDebug("25");
 
459
                                        // append <protocol><documend-end/><last-edit/></protocol>
 
460
                                        history.appendChild(documentEnd);
 
461
 
 
462
                                        sendMessage(history, negotiation->peer, negotiation->groupChat);
 
463
                                        negotiation->state = WbNegotiation::Finished;
 
464
                                        negotiation->dialog->setQueueing(false);
 
465
                                } /*else if(m.nodeName() == "decline-invitation" && negotiation->state == WbNegotiation::ConnectionRequested) {
 
466
                                        // Create the WbDlg
 
467
                                        negotiation->state = WbNegotiation::Aborted;
 
468
                                }*/
 
469
                                if(protocol.hasChildNodes()) {
 
470
                                        sendMessage(wb.cloneNode().toElement(), negotiation->peer, negotiation->groupChat);
 
471
                                }
 
472
                        }
 
473
                } else if((negotiation->state == WbNegotiation::DocumentBegan || negotiation->state == WbNegotiation::Finished) && negotiation->dialog) {
 
474
                        // If state after <document-begin/>,
 
475
                        // pass the edits to the dialog
 
476
                        wb.removeChild(protocol);
 
477
                        wb.appendChild(n);
 
478
                        negotiation->dialog->incomingWbElement(wb, negotiation->peer);
 
479
                        wb.removeChild(n);
 
480
                        wb.appendChild(protocol);
 
481
                }
 
482
        }
 
483
        // Delete erroneous attempts and finished negotiations
 
484
        if(negotiation) {
 
485
                if(negotiation->state == WbNegotiation::NotStarted) {
 
486
                        negotiations_.remove(session);
 
487
                        delete negotiation;
 
488
                } else if(negotiation->state == WbNegotiation::Finished) {
 
489
                        WbDlg* w = 0;
 
490
                        if(negotiation->dialog) {
 
491
                                w = negotiation->dialog;
 
492
                                if(!dialogs_.contains(negotiation->dialog))
 
493
                                        dialogs_.append(negotiation->dialog);
 
494
                        }
 
495
                        negotiations_.remove(session);
 
496
                        delete negotiation;
 
497
                        return w;
 
498
                } else if(negotiation->state == WbNegotiation::Aborted) {
 
499
                        if(negotiation->dialog)
 
500
                                negotiation->dialog->endSession();
 
501
                        negotiations_.remove(session);
 
502
                        delete negotiation;
 
503
                        return 0;
 
504
                }
 
505
        }
 
506
        return 0;
 
507
}
 
508
 
 
509
void WbManager::startNegotiation(const Jid &target, const Jid &ownJid, bool groupChat, QList<QString> features) {
 
510
        // generate a session identifier
 
511
        QString session;
 
512
        // Check if there's detected activity of a session to the specified jid
 
513
        // TODO: Make a custom dialog.
 
514
        QList<QString> potentialSessions;
 
515
        // Remove old detected sessions
 
516
        removeDetectedSession(QString());
 
517
//      potentialSessions.append(tr("New session"));
 
518
//      potentialSessions.append(tr("Manual..."));
 
519
        foreach(detectedSession detected, detectedSessions_) {
 
520
                if(detected.jid.compare(target))
 
521
                        potentialSessions.append(detected.session);
 
522
        }
 
523
        bool ok;
 
524
        session = QInputDialog::getItem(0, tr("Specify Session"),tr("If you wish to join and existing session,\nchoose from one of the recently detected sessions\nor type in your own. Else leave the field empty."), potentialSessions, 0, true, &ok);
 
525
        if(!ok)
 
526
                return;
 
527
        // else, generate an arbitrary session
 
528
        // TODO: this could conflict with other clients starting a session at the same time though it's extremely unlikely
 
529
        bool existing = true;
 
530
        if(session.isEmpty()) {
 
531
                existing = false;
 
532
                do {
 
533
                        session = QTime::currentTime().toString("msz");
 
534
                } while (findWbDlg(session));
 
535
        }
 
536
        // Prepare the list of features
 
537
        if(!features.size()) {
 
538
                // If none is specified, try all supported features;
 
539
                features = supportedFeatures_;
 
540
        } else foreach(QString f, features) {
 
541
                // Check that all features are actually supported
 
542
                if(!supportedFeatures_.contains(f))
 
543
                        features.removeAll(f);
 
544
        }
 
545
        // Prepare the connect request
 
546
        QDomDocument doc;
 
547
        QDomElement wb = doc.createElementNS("http://jabber.org/protocol/svgwb", "wb");
 
548
        wb.setAttribute("session", session);
 
549
        QDomElement protocol = doc.createElement("protocol");
 
550
        QDomElement request;
 
551
        if(existing)
 
552
                request = doc.createElement("connect-request");
 
553
        else {
 
554
                request = doc.createElement("invitation");
 
555
                QDomElement feature = doc.createElement("feature");
 
556
                foreach(QString f, features) {
 
557
                        feature = feature.cloneNode(false).toElement();
 
558
                        feature.appendChild(doc.createTextNode(f));
 
559
                        request.appendChild(feature);
 
560
                }
 
561
        }
 
562
        protocol.appendChild(request);
 
563
        wb.appendChild(protocol);
 
564
        // Create the negotiation object
 
565
        WbNegotiation* negotiation = new WbNegotiation;
 
566
        if(existing) {
 
567
                negotiation->role = WbNegotiation::Participant;
 
568
                negotiation->state = WbNegotiation::ConnectionRequested;
 
569
        } else {
 
570
                negotiation->role = WbNegotiation::Joiner;
 
571
                negotiation->state = WbNegotiation::InvitationSent;
 
572
        }
 
573
        negotiation->target = target;
 
574
        negotiation->peer = target;
 
575
        negotiation->ownJid = ownJid;
 
576
        negotiation->groupChat = groupChat;
 
577
        negotiation->dialog = 0;
 
578
        negotiations_[session] = negotiation;
 
579
 
 
580
        sendMessage(wb, target, groupChat);
 
581
 
 
582
        // Reset the timeout for negotiations
 
583
        negotiationTimer_.start();
 
584
 
 
585
        return;
 
586
}
 
587
 
 
588
void WbManager::negotiationTimeout() {
 
589
        WbNegotiation* negotiation;
 
590
        foreach(QString session, negotiations_.keys()){
 
591
                negotiation = negotiations_.take(session);
 
592
                if(negotiation->role == WbNegotiation::Participant && negotiation->state < WbNegotiation::HistoryOffered && negotiation->state != WbNegotiation::DocumentBegan) {               
 
593
                        if(negotiation->dialog)
 
594
                                negotiation->dialog->endSession();
 
595
                        sendAbortNegotiation(session, negotiation->peer, negotiation->groupChat);
 
596
                }
 
597
                delete negotiation;
 
598
        }
 
599
}
 
600
 
 
601
// #include <QTextStream>
 
602
void WbManager::sendMessage(QDomElement wb, const Jid & receiver, bool groupChat) {
 
603
        // DEBUG:
 
604
//      QString debug;
 
605
//      QTextStream s(&debug);
 
606
//      wb.save(s, 1);
 
607
//      qDebug() << (debug.toAscii());
 
608
 
 
609
//      // Split large wb elements to smaller ones if possible
 
610
//      QString str;
 
611
//      QTextStream stream(&debug);
 
612
//      wb.save(stream, 0);
 
613
//      QList<QDomElement> wbs;
 
614
//      while(str > 6000) {
 
615
//              QDomElement
 
616
//              wb.save(stream, 0);
 
617
//      }
 
618
        wbHash_--;
 
619
        WbDlg* dialog = qobject_cast<WbDlg*>(sender());
 
620
        if(dialog)
 
621
                dialog->setLastWb(dialog->ownJid().full(), QString("%1").arg(wbHash_));
 
622
        // Add a unique hash to each sent wb element
 
623
        wb.setAttribute("hash", wbHash_);
 
624
        Message m(receiver);
 
625
        m.setWhiteboard(wb);
 
626
        if(groupChat && receiver.resource().isEmpty())
 
627
                m.setType("groupchat");
 
628
        if(client_->isActive())
 
629
                client_->sendMessage(m);
 
630
        else {
 
631
                //TODO: queue the message
 
632
        }
 
633
}
 
634
 
 
635
WbDlg* WbManager::findWbDlg(const Jid &jid) {
 
636
        // find if a dialog for the jid already exists
 
637
        foreach(WbDlg* w, dialogs_) {
 
638
                // does the jid match?
 
639
                if(w->target().compare(jid)) {
 
640
                        return w;
 
641
                }
 
642
        }
 
643
        return 0;
 
644
}
 
645
 
 
646
WbDlg* WbManager::findWbDlg(const QString &session) {
 
647
        // find if a dialog for the session already exists
 
648
        foreach(WbDlg* w, dialogs_) {
 
649
                // does the session match?
 
650
                if(w->session() == session)
 
651
                        return w;
 
652
        }
 
653
        return 0;
 
654
}
 
655
 
 
656
WbDlg* WbManager::createWbDlg(const Jid &target, QString session, const Jid &ownJid, bool groupChat) {
 
657
        if(session.isEmpty() || !target.isValid())
 
658
                return 0;
 
659
        if(!ownJids_.contains(ownJid.full()))
 
660
                ownJids_.append(ownJid.full());
 
661
        // create the WbDlg
 
662
        WbDlg* w = new WbDlg(target, session, ownJid, groupChat, pa_);
 
663
        // connect the signals
 
664
        connect(w, SIGNAL(newWbElement(QDomElement, Jid, bool)), SLOT(sendMessage(const QDomElement &, const Jid &, bool)));
 
665
        connect(w, SIGNAL(sessionEnded(QString)), SLOT(removeSession(const QString &)));
 
666
        removeDetectedSession(session);
 
667
        return w;
 
668
        // Note: the dialog should be added to dialogs_ once negotiation is finished
 
669
}
 
670
 
 
671
void WbManager::sendAbortNegotiation(QString session, const Jid &peer, bool groupChat) {
 
672
        QDomDocument doc = QDomDocument();
 
673
        QDomElement wb = doc.createElementNS("http://jabber.org/protocol/svgwb", "wb");
 
674
        wb.setAttribute("session", session);
 
675
        QDomElement protocol = doc.createElement("protocol");
 
676
        protocol.appendChild(doc.createElement("abort-negotiation"));
 
677
        wb.appendChild(protocol);
 
678
        sendMessage(wb, peer, groupChat);
 
679
}
 
680
 
 
681
void WbManager::removeDetectedSession(const QString &session) {
 
682
        for(int i = 0; i < detectedSessions_.size(); i++) {
 
683
                // Remove the specified session from the list
 
684
                if(detectedSessions_.at(i).session == session)
 
685
                        detectedSessions_.removeAt(i);
 
686
                else if(detectedSessions_.at(i).time.secsTo(QTime::currentTime()) > 1800)
 
687
                        // Remove detected session that are old
 
688
                        detectedSessions_.removeAt(i);
 
689
        }
 
690
}
 
691
 
 
692
void WbManager::groupChatLeft(const Jid &jid) {
 
693
        for(int i = 0; i < ownJids_.size(); i++) {
 
694
                if(jid.bare() == ownJids_.at(i).left(ownJids_.at(i).indexOf("/")))
 
695
                        ownJids_.removeAt(i);
 
696
        }
 
697
        WbDlg* w = findWbDlg(jid);
 
698
        if(w)
 
699
                w->endSession();
 
700
}
 
701
 
 
702
void WbManager::groupChatJoined(const Jid &ownJid) {
 
703
        if(!ownJids_.contains(ownJid.full()))
 
704
                ownJids_.append(ownJid.full());
 
705
}