source: psi/trunk/iris/xmpp-im/client.cpp@ 82

Last change on this file since 82 was 17, checked in by dmik, 19 years ago

iris/xmpp-im: Added todo regarding the 100% CPU load when discovering services on some jabber servers.

File size: 29.2 KB
Line 
1/*
2 * client.cpp - IM Client
3 * Copyright (C) 2003 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 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"im.h"
22#include"safedelete.h"
23
24//! \class Client client.h
25//! \brief Communicates with the Jabber network. Start here.
26//!
27//! Client controls an active Jabber connection. It allows you to connect,
28//! authenticate, manipulate the roster, and send / receive messages and
29//! presence. It is the centerpiece of this library, and all Tasks must pass
30//! through it.
31//!
32//! For convenience, many Tasks are handled internally to Client (such as
33//! JT_Auth). However, for accessing features beyond the basics provided by
34//! Client, you will need to manually invoke Tasks. Fortunately, the
35//! process is very simple.
36//!
37//! The entire Task system is heavily founded on Qt. All Tasks have a parent,
38//! except for the root Task, and are considered QObjects. By using Qt's RTTI
39//! facilities (QObject::sender(), QObject::isA(), etc), you can use a
40//! "fire and forget" approach with Tasks.
41//!
42//! \code
43//! #include "client.h"
44//! using namespace Jabber;
45//!
46//! ...
47//!
48//! Client *client;
49//!
50//! Session::Session()
51//! {
52//! client = new Client;
53//! connect(client, SIGNAL(handshaken()), SLOT(clientHandshaken()));
54//! connect(client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(authFinished(bool, int, const QString &)));
55//! client->connectToHost("jabber.org");
56//! }
57//!
58//! void Session::clientHandshaken()
59//! {
60//! client->authDigest("jabtest", "12345", "Psi");
61//! }
62//!
63//! void Session::authFinished(bool success, int, const QString &err)
64//! {
65//! if(success)
66//! printf("Login success!");
67//! else
68//! printf("Login failed. Here's why: %s\n", err.latin1());
69//! }
70//! \endcode
71
72#include<stdarg.h>
73#include<qobjectlist.h>
74#include<qtimer.h>
75#include<qguardedptr.h>
76#include"xmpp_tasks.h"
77#include"xmpp_xmlcommon.h"
78#include"s5b.h"
79#include"xmpp_ibb.h"
80#include"xmpp_jidlink.h"
81#include"filetransfer.h"
82
83/*#include<stdio.h>
84#include<stdarg.h>
85#include<qstring.h>
86#include<qdom.h>
87#include<qobjectlist.h>
88#include<qtimer.h>
89#include"xmpp_stream.h"
90#include"xmpp_tasks.h"
91#include"xmpp_xmlcommon.h"
92#include"xmpp_dtcp.h"
93#include"xmpp_ibb.h"
94#include"xmpp_jidlink.h"
95
96using namespace Jabber;*/
97
98#ifdef Q_WS_WIN
99#define vsnprintf _vsnprintf
100#endif
101
102namespace XMPP
103{
104
105//----------------------------------------------------------------------------
106// Client
107//----------------------------------------------------------------------------
108class Client::GroupChat
109{
110public:
111 enum { Connecting, Connected, Closing };
112 GroupChat() {}
113
114 Jid j;
115 int status;
116};
117
118class Client::ClientPrivate
119{
120public:
121 ClientPrivate() {}
122
123 ClientStream *stream;
124 QDomDocument doc;
125 int id_seed;
126 Task *root;
127 QString host, user, pass, resource;
128 QString osname, tzname, clientName, clientVersion;
129 int tzoffset;
130 bool active;
131
132 LiveRoster roster;
133 ResourceList resourceList;
134 S5BManager *s5bman;
135 IBBManager *ibbman;
136 JidLinkManager *jlman;
137 FileTransferManager *ftman;
138 bool ftEnabled;
139 QValueList<GroupChat> groupChatList;
140};
141
142
143Client::Client(QObject *par)
144:QObject(par)
145{
146 d = new ClientPrivate;
147 d->tzoffset = 0;
148 d->active = false;
149 d->osname = "N/A";
150 d->clientName = "N/A";
151 d->clientVersion = "0.0";
152
153 d->id_seed = 0xaaaa;
154 d->root = new Task(this, true);
155
156 d->stream = 0;
157
158 d->s5bman = new S5BManager(this);
159 connect(d->s5bman, SIGNAL(incomingReady()), SLOT(s5b_incomingReady()));
160
161 d->ibbman = new IBBManager(this);
162 connect(d->ibbman, SIGNAL(incomingReady()), SLOT(ibb_incomingReady()));
163
164 d->jlman = new JidLinkManager(this);
165
166 d->ftman = 0;
167}
168
169Client::~Client()
170{
171 close(true);
172
173 delete d->ftman;
174 delete d->jlman;
175 delete d->ibbman;
176 delete d->s5bman;
177 delete d->root;
178 //delete d->stream;
179 delete d;
180}
181
182void Client::connectToServer(ClientStream *s, const Jid &j, bool auth)
183{
184 d->stream = s;
185 //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected()));
186 //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken()));
187 connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int)));
188 //connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &)));
189 connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead()));
190 //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished()));
191 connect(d->stream, SIGNAL(incomingXml(const QString &)), SLOT(streamIncomingXml(const QString &)));
192 connect(d->stream, SIGNAL(outgoingXml(const QString &)), SLOT(streamOutgoingXml(const QString &)));
193
194 d->stream->connectToServer(j, auth);
195}
196
197void Client::start(const QString &host, const QString &user, const QString &pass, const QString &_resource)
198{
199 // TODO
200 d->host = host;
201 d->user = user;
202 d->pass = pass;
203 d->resource = _resource;
204
205 Status stat;
206 stat.setIsAvailable(false);
207 d->resourceList += Resource(resource(), stat);
208
209 JT_PushPresence *pp = new JT_PushPresence(rootTask());
210 connect(pp, SIGNAL(subscription(const Jid &, const QString &)), SLOT(ppSubscription(const Jid &, const QString &)));
211 connect(pp, SIGNAL(presence(const Jid &, const Status &)), SLOT(ppPresence(const Jid &, const Status &)));
212
213 JT_PushMessage *pm = new JT_PushMessage(rootTask());
214 connect(pm, SIGNAL(message(const Message &)), SLOT(pmMessage(const Message &)));
215
216 JT_PushRoster *pr = new JT_PushRoster(rootTask());
217 connect(pr, SIGNAL(roster(const Roster &)), SLOT(prRoster(const Roster &)));
218
219 new JT_ServInfo(rootTask());
220
221 d->active = true;
222}
223
224void Client::setFileTransferEnabled(bool b)
225{
226 if(b) {
227 if(!d->ftman)
228 d->ftman = new FileTransferManager(this);
229 }
230 else {
231 if(d->ftman) {
232 delete d->ftman;
233 d->ftman = 0;
234 }
235 }
236}
237
238FileTransferManager *Client::fileTransferManager() const
239{
240 return d->ftman;
241}
242
243JidLinkManager *Client::jidLinkManager() const
244{
245 return d->jlman;
246}
247
248S5BManager *Client::s5bManager() const
249{
250 return d->s5bman;
251}
252
253IBBManager *Client::ibbManager() const
254{
255 return d->ibbman;
256}
257
258bool Client::isActive() const
259{
260 return d->active;
261}
262
263void Client::groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &_s)
264{
265 Jid jid(room + "@" + host + "/" + nick);
266 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
267 GroupChat &i = *it;
268 if(i.j.compare(jid, false)) {
269 i.j = jid;
270
271 Status s = _s;
272 s.setIsAvailable(true);
273
274 JT_Presence *j = new JT_Presence(rootTask());
275 j->pres(jid, s);
276 j->go(true);
277
278 break;
279 }
280 }
281}
282
283bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick)
284{
285 Jid jid(room + "@" + host + "/" + nick);
286 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
287 GroupChat &i = *it;
288 if(i.j.compare(jid, false)) {
289 // if this room is shutting down, then free it up
290 if(i.status == GroupChat::Closing)
291 it = d->groupChatList.remove(it);
292 else
293 return false;
294 }
295 else
296 ++it;
297 }
298
299 debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
300 GroupChat i;
301 i.j = jid;
302 i.status = GroupChat::Connecting;
303 d->groupChatList += i;
304
305 JT_Presence *j = new JT_Presence(rootTask());
306 j->pres(jid, Status());
307 j->go(true);
308
309 return true;
310}
311
312void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s)
313{
314 Jid jid(room + "@" + host);
315 bool found = false;
316 for(QValueList<GroupChat>::ConstIterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
317 const GroupChat &i = *it;
318 if(i.j.compare(jid, false)) {
319 found = true;
320 jid = i.j;
321 break;
322 }
323 }
324 if(!found)
325 return;
326
327 Status s = _s;
328 s.setIsAvailable(true);
329
330 JT_Presence *j = new JT_Presence(rootTask());
331 j->pres(jid, s);
332 j->go(true);
333}
334
335void Client::groupChatLeave(const QString &host, const QString &room)
336{
337 Jid jid(room + "@" + host);
338 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
339 GroupChat &i = *it;
340
341 if(!i.j.compare(jid, false))
342 continue;
343
344 i.status = GroupChat::Closing;
345 debug(QString("Client: Leaving: [%1]\n").arg(i.j.full()));
346
347 JT_Presence *j = new JT_Presence(rootTask());
348 Status s;
349 s.setIsAvailable(false);
350 j->pres(i.j, s);
351 j->go(true);
352 }
353}
354
355/*void Client::start()
356{
357 if(d->stream->old()) {
358 // old has no activation step
359 d->active = true;
360 activated();
361 }
362 else {
363 // TODO: IM session
364 }
365}*/
366
367// TODO: fast close
368void Client::close(bool)
369{
370 if(d->stream) {
371 if(d->active) {
372 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
373 GroupChat &i = *it;
374 i.status = GroupChat::Closing;
375
376 JT_Presence *j = new JT_Presence(rootTask());
377 Status s;
378 s.setIsAvailable(false);
379 j->pres(i.j, s);
380 j->go(true);
381 }
382 }
383
384 d->stream->disconnect(this);
385 d->stream->close();
386 d->stream = 0;
387 }
388 disconnected();
389 cleanup();
390}
391
392void Client::cleanup()
393{
394 d->active = false;
395 //d->authed = false;
396 d->groupChatList.clear();
397}
398
399/*void Client::continueAfterCert()
400{
401 d->stream->continueAfterCert();
402}
403
404void Client::streamConnected()
405{
406 connected();
407}
408
409void Client::streamHandshaken()
410{
411 handshaken();
412}*/
413
414void Client::streamError(int)
415{
416 //StreamError e = err;
417 //error(e);
418
419 //if(!e.isWarning()) {
420 disconnected();
421 cleanup();
422 //}
423}
424
425/*void Client::streamSSLCertificateReady(const QSSLCert &cert)
426{
427 sslCertReady(cert);
428}
429
430void Client::streamCloseFinished()
431{
432 closeFinished();
433}*/
434
435static QDomElement oldStyleNS(const QDomElement &e)
436{
437 // find closest parent with a namespace
438 QDomNode par = e.parentNode();
439 while(!par.isNull() && par.namespaceURI().isNull())
440 par = par.parentNode();
441 bool noShowNS = false;
442 if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
443 noShowNS = true;
444
445 QDomElement i;
446 uint x;
447 //if(noShowNS)
448 i = e.ownerDocument().createElement(e.tagName());
449 //else
450 // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
451
452 // copy attributes
453 QDomNamedNodeMap al = e.attributes();
454 for(x = 0; x < al.count(); ++x)
455 i.setAttributeNode(al.item(x).cloneNode().toAttr());
456
457 if(!noShowNS)
458 i.setAttribute("xmlns", e.namespaceURI());
459
460 // copy children
461 QDomNodeList nl = e.childNodes();
462 for(x = 0; x < nl.count(); ++x) {
463 QDomNode n = nl.item(x);
464 if(n.isElement())
465 i.appendChild(oldStyleNS(n.toElement()));
466 else
467 i.appendChild(n.cloneNode());
468 }
469 return i;
470}
471
472void Client::streamReadyRead()
473{
474 /// @todo (dmik) when doing service discovery on some servers
475 // (i.e. jabber.ru), this method's execution time can exceed up to
476 // 2,5 sec (on my Pentium D 820) that is obviously too much because
477 // gives quite a noticeable GUI thread freeze (so that the Psi app
478 // doesn't respond to system/input messages). Approx. 0.8 sec is taken by
479 // s.toString(), and the rest is by QDomElement x = oldStyleNS(s.element()).
480 // and distribute(). Ideally, parsing and distribution of lagre pieces of
481 // data should be somehow done on a separate thread.
482
483 // HACK HACK HACK
484 QGuardedPtr<ClientStream> pstream = d->stream;
485
486 while(pstream && d->stream->stanzaAvailable()) {
487 Stanza s = d->stream->read();
488
489 QString out = s.toString();
490 debug(QString("Client: incoming: [\n%1]\n").arg(out));
491 xmlIncoming(out);
492
493 QDomElement x = oldStyleNS(s.element());
494 distribute(x);
495 }
496}
497
498void Client::streamIncomingXml(const QString &s)
499{
500 QString str = s;
501 if(str.at(str.length()-1) != '\n')
502 str += '\n';
503 xmlIncoming(str);
504}
505
506void Client::streamOutgoingXml(const QString &s)
507{
508 QString str = s;
509 if(str.at(str.length()-1) != '\n')
510 str += '\n';
511 xmlOutgoing(str);
512}
513
514void Client::debug(const QString &str)
515{
516 debugText(str);
517}
518
519QString Client::genUniqueId()
520{
521 QString s;
522 s.sprintf("a%x", d->id_seed);
523 d->id_seed += 0x10;
524 return s;
525}
526
527Task *Client::rootTask()
528{
529 return d->root;
530}
531
532QDomDocument *Client::doc() const
533{
534 return &d->doc;
535}
536
537void Client::distribute(const QDomElement &x)
538{
539 if(x.hasAttribute("from")) {
540 Jid j(x.attribute("from"));
541 if(!j.isValid()) {
542 debug("Client: bad 'from' JID\n");
543 return;
544 }
545 }
546
547 if(!rootTask()->take(x)) {
548 debug("Client: packet was ignored.\n");
549 }
550}
551
552static QDomElement addCorrectNS(const QDomElement &e)
553{
554 uint x;
555
556 // grab child nodes
557 /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
558 QDomNodeList nl = e.childNodes();
559 for(x = 0; x < nl.count(); ++x)
560 frag.appendChild(nl.item(x).cloneNode());*/
561
562 // find closest xmlns
563 QDomNode n = e;
564 while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
565 n = n.parentNode();
566 QString ns;
567 if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
568 ns = "jabber:client";
569 else
570 ns = n.toElement().attribute("xmlns");
571
572 // make a new node
573 QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
574
575 // copy attributes
576 QDomNamedNodeMap al = e.attributes();
577 for(x = 0; x < al.count(); ++x) {
578 QDomAttr a = al.item(x).toAttr();
579 if(a.name() != "xmlns")
580 i.setAttributeNodeNS(a.cloneNode().toAttr());
581 }
582
583 // copy children
584 QDomNodeList nl = e.childNodes();
585 for(x = 0; x < nl.count(); ++x) {
586 QDomNode n = nl.item(x);
587 if(n.isElement())
588 i.appendChild(addCorrectNS(n.toElement()));
589 else
590 i.appendChild(n.cloneNode());
591 }
592
593 //i.appendChild(frag);
594 return i;
595}
596
597void Client::send(const QDomElement &x)
598{
599 if(!d->stream)
600 return;
601
602 //QString out;
603 //QTextStream ts(&out, IO_WriteOnly);
604 //x.save(ts, 0);
605
606 //QString out = Stream::xmlToString(x);
607 //debug(QString("Client: outgoing: [\n%1]\n").arg(out));
608 //xmlOutgoing(out);
609
610 QDomElement e = addCorrectNS(x);
611 Stanza s = d->stream->createStanza(e);
612 if(s.isNull()) {
613 //printf("bad stanza??\n");
614 return;
615 }
616
617 QString out = s.toString();
618 debug(QString("Client: outgoing: [\n%1]\n").arg(out));
619 xmlOutgoing(out);
620
621 //printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).latin1(), Stream::xmlToString(e).latin1(), s.toString().latin1());
622 d->stream->write(s);
623}
624
625void Client::send(const QString &str)
626{
627 if(!d->stream)
628 return;
629
630 debug(QString("Client: outgoing: [\n%1]\n").arg(str));
631 xmlOutgoing(str);
632 static_cast<ClientStream*>(d->stream)->writeDirect(str);
633}
634
635Stream & Client::stream()
636{
637 return *d->stream;
638}
639
640const LiveRoster & Client::roster() const
641{
642 return d->roster;
643}
644
645const ResourceList & Client::resourceList() const
646{
647 return d->resourceList;
648}
649
650QString Client::host() const
651{
652 return d->host;
653}
654
655QString Client::user() const
656{
657 return d->user;
658}
659
660QString Client::pass() const
661{
662 return d->pass;
663}
664
665QString Client::resource() const
666{
667 return d->resource;
668}
669
670Jid Client::jid() const
671{
672 QString s;
673 if(!d->user.isEmpty())
674 s += d->user + '@';
675 s += d->host;
676 if(!d->resource.isEmpty()) {
677 s += '/';
678 s += d->resource;
679 }
680
681 return Jid(s);
682}
683
684void Client::ppSubscription(const Jid &j, const QString &s)
685{
686 subscription(j, s);
687}
688
689void Client::ppPresence(const Jid &j, const Status &s)
690{
691 if(s.isAvailable())
692 debug(QString("Client: %1 is available.\n").arg(j.full()));
693 else
694 debug(QString("Client: %1 is unavailable.\n").arg(j.full()));
695
696 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
697 GroupChat &i = *it;
698
699 if(i.j.compare(j, false)) {
700 bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false;
701
702 debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us));
703 switch(i.status) {
704 case GroupChat::Connecting:
705 if(us && s.hasError()) {
706 Jid j = i.j;
707 d->groupChatList.remove(it);
708 groupChatError(j, s.errorCode(), s.errorString());
709 }
710 else {
711 // don't signal success unless it is a non-error presence
712 if(!s.hasError()) {
713 i.status = GroupChat::Connected;
714 groupChatJoined(i.j);
715 }
716 groupChatPresence(j, s);
717 }
718 break;
719 case GroupChat::Connected:
720 groupChatPresence(j, s);
721 break;
722 case GroupChat::Closing:
723 if(us && !s.isAvailable()) {
724 Jid j = i.j;
725 d->groupChatList.remove(it);
726 groupChatLeft(j);
727 }
728 break;
729 default:
730 break;
731 }
732
733 return;
734 }
735 }
736
737 if(s.hasError()) {
738 presenceError(j, s.errorCode(), s.errorString());
739 return;
740 }
741
742 // is it me?
743 if(j.compare(jid(), false)) {
744 updateSelfPresence(j, s);
745 }
746 else {
747 // update all relavent roster entries
748 for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) {
749 LiveRosterItem &i = *it;
750
751 if(!i.jid().compare(j, false))
752 continue;
753
754 // roster item has its own resource?
755 if(!i.jid().resource().isEmpty()) {
756 if(i.jid().resource() != j.resource())
757 continue;
758 }
759
760 updatePresence(&i, j, s);
761 }
762 }
763}
764
765void Client::updateSelfPresence(const Jid &j, const Status &s)
766{
767 ResourceList::Iterator rit = d->resourceList.find(j.resource());
768 bool found = (rit == d->resourceList.end()) ? false: true;
769
770 // unavailable? remove the resource
771 if(!s.isAvailable()) {
772 if(found) {
773 debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource()));
774 (*rit).setStatus(s);
775 resourceUnavailable(j, *rit);
776 d->resourceList.remove(rit);
777 }
778 }
779 // available? add/update the resource
780 else {
781 Resource r;
782 if(!found) {
783 r = Resource(j.resource(), s);
784 d->resourceList += r;
785 debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource()));
786 }
787 else {
788 (*rit).setStatus(s);
789 r = *rit;
790 debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource()));
791 }
792
793 resourceAvailable(j, r);
794 }
795}
796
797void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s)
798{
799 ResourceList::Iterator rit = i->resourceList().find(j.resource());
800 bool found = (rit == i->resourceList().end()) ? false: true;
801
802 // unavailable? remove the resource
803 if(!s.isAvailable()) {
804 if(found) {
805 (*rit).setStatus(s);
806 debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
807 resourceUnavailable(j, *rit);
808 i->resourceList().remove(rit);
809 i->setLastUnavailableStatus(s);
810 }
811 }
812 // available? add/update the resource
813 else {
814 Resource r;
815 if(!found) {
816 r = Resource(j.resource(), s);
817 i->resourceList() += r;
818 debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
819 }
820 else {
821 (*rit).setStatus(s);
822 r = *rit;
823 debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
824 }
825
826 resourceAvailable(j, r);
827 }
828}
829
830void Client::pmMessage(const Message &m)
831{
832 debug(QString("Client: Message from %1\n").arg(m.from().full()));
833
834 if(m.type() == "groupchat") {
835 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
836 const GroupChat &i = *it;
837
838 if(!i.j.compare(m.from(), false))
839 continue;
840
841 if(i.status == GroupChat::Connected)
842 messageReceived(m);
843 }
844 }
845 else
846 messageReceived(m);
847}
848
849void Client::prRoster(const Roster &r)
850{
851 importRoster(r);
852}
853
854void Client::rosterRequest()
855{
856 if(!d->active)
857 return;
858
859 JT_Roster *r = new JT_Roster(rootTask());
860 connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished()));
861 r->get();
862 d->roster.flagAllForDelete(); // mod_groups patch
863 r->go(true);
864}
865
866void Client::slotRosterRequestFinished()
867{
868 JT_Roster *r = (JT_Roster *)sender();
869 // on success, let's take it
870 if(r->success()) {
871 //d->roster.flagAllForDelete(); // mod_groups patch
872
873 importRoster(r->roster());
874
875 for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) {
876 LiveRosterItem &i = *it;
877 if(i.flagForDelete()) {
878 rosterItemRemoved(i);
879 it = d->roster.remove(it);
880 }
881 else
882 ++it;
883 }
884 }
885 else {
886 // don't report a disconnect. Client::error() will do that.
887 if(r->statusCode() == Task::ErrDisc)
888 return;
889 }
890
891 // report success / fail
892 rosterRequestFinished(r->success(), r->statusCode(), r->statusString());
893}
894
895void Client::importRoster(const Roster &r)
896{
897 for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) {
898 importRosterItem(*it);
899 }
900}
901
902void Client::importRosterItem(const RosterItem &item)
903{
904 QString substr;
905 switch(item.subscription().type()) {
906 case Subscription::Both:
907 substr = "<-->"; break;
908 case Subscription::From:
909 substr = " ->"; break;
910 case Subscription::To:
911 substr = "<- "; break;
912 case Subscription::Remove:
913 substr = "xxxx"; break;
914 case Subscription::None:
915 default:
916 substr = "----"; break;
917 }
918
919 QString dstr, str;
920 str.sprintf(" %s %-32s", substr.latin1(), item.jid().full().latin1());
921 if(!item.name().isEmpty())
922 str += QString(" [") + item.name() + "]";
923 str += '\n';
924
925 // Remove
926 if(item.subscription().type() == Subscription::Remove) {
927 LiveRoster::Iterator it = d->roster.find(item.jid());
928 if(it != d->roster.end()) {
929 rosterItemRemoved(*it);
930 d->roster.remove(it);
931 }
932 dstr = "Client: (Removed) ";
933 }
934 // Add/Update
935 else {
936 LiveRoster::Iterator it = d->roster.find(item.jid());
937 if(it != d->roster.end()) {
938 LiveRosterItem &i = *it;
939 i.setFlagForDelete(false);
940 i.setRosterItem(item);
941 rosterItemUpdated(i);
942 dstr = "Client: (Updated) ";
943 }
944 else {
945 LiveRosterItem i(item);
946 d->roster += i;
947
948 // signal it
949 rosterItemAdded(i);
950 dstr = "Client: (Added) ";
951 }
952 }
953
954 debug(dstr + str);
955}
956
957void Client::sendMessage(const Message &m)
958{
959 JT_Message *j = new JT_Message(rootTask(), m);
960 j->go(true);
961}
962
963void Client::sendSubscription(const Jid &jid, const QString &type)
964{
965 JT_Presence *j = new JT_Presence(rootTask());
966 j->sub(jid, type);
967 j->go(true);
968}
969
970void Client::setPresence(const Status &s)
971{
972 JT_Presence *j = new JT_Presence(rootTask());
973 j->pres(s);
974 j->go(true);
975
976 // update our resourceList
977 ppPresence(jid(), s);
978 //ResourceList::Iterator rit = d->resourceList.find(resource());
979 //Resource &r = *rit;
980 //r.setStatus(s);
981}
982
983QString Client::OSName() const
984{
985 return d->osname;
986}
987
988QString Client::timeZone() const
989{
990 return d->tzname;
991}
992
993int Client::timeZoneOffset() const
994{
995 return d->tzoffset;
996}
997
998QString Client::clientName() const
999{
1000 return d->clientName;
1001}
1002
1003QString Client::clientVersion() const
1004{
1005 return d->clientVersion;
1006}
1007
1008void Client::setOSName(const QString &name)
1009{
1010 d->osname = name;
1011}
1012
1013void Client::setTimeZone(const QString &name, int offset)
1014{
1015 d->tzname = name;
1016 d->tzoffset = offset;
1017}
1018
1019void Client::setClientName(const QString &s)
1020{
1021 d->clientName = s;
1022}
1023
1024void Client::setClientVersion(const QString &s)
1025{
1026 d->clientVersion = s;
1027}
1028
1029void Client::s5b_incomingReady()
1030{
1031 S5BConnection *c = d->s5bman->takeIncoming();
1032 if(!c)
1033 return;
1034 if(!d->ftman) {
1035 c->close();
1036 c->deleteLater();
1037 return;
1038 }
1039 d->ftman->s5b_incomingReady(c);
1040 //d->jlman->insertStream(c);
1041 //incomingJidLink();
1042}
1043
1044void Client::ibb_incomingReady()
1045{
1046 IBBConnection *c = d->ibbman->takeIncoming();
1047 if(!c)
1048 return;
1049 c->deleteLater();
1050 //d->jlman->insertStream(c);
1051 //incomingJidLink();
1052}
1053
1054//----------------------------------------------------------------------------
1055// Task
1056//----------------------------------------------------------------------------
1057class Task::TaskPrivate
1058{
1059public:
1060 TaskPrivate() {}
1061
1062 QString id;
1063 bool success;
1064 int statusCode;
1065 QString statusString;
1066 Client *client;
1067 bool insig, deleteme, autoDelete;
1068 bool done;
1069};
1070
1071Task::Task(Task *parent)
1072:QObject(parent)
1073{
1074 init();
1075
1076 d->client = parent->client();
1077 d->id = client()->genUniqueId();
1078 connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
1079}
1080
1081Task::Task(Client *parent, bool)
1082:QObject(0)
1083{
1084 init();
1085
1086 d->client = parent;
1087 connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
1088}
1089
1090Task::~Task()
1091{
1092 delete d;
1093}
1094
1095void Task::init()
1096{
1097 d = new TaskPrivate;
1098 d->success = false;
1099 d->insig = false;
1100 d->deleteme = false;
1101 d->autoDelete = false;
1102 d->done = false;
1103}
1104
1105Task *Task::parent() const
1106{
1107 return (Task *)QObject::parent();
1108}
1109
1110Client *Task::client() const
1111{
1112 return d->client;
1113}
1114
1115QDomDocument *Task::doc() const
1116{
1117 return client()->doc();
1118}
1119
1120QString Task::id() const
1121{
1122 return d->id;
1123}
1124
1125bool Task::success() const
1126{
1127 return d->success;
1128}
1129
1130int Task::statusCode() const
1131{
1132 return d->statusCode;
1133}
1134
1135const QString & Task::statusString() const
1136{
1137 return d->statusString;
1138}
1139
1140void Task::go(bool autoDelete)
1141{
1142 d->autoDelete = autoDelete;
1143
1144 onGo();
1145}
1146
1147bool Task::take(const QDomElement &x)
1148{
1149 const QObjectList *p = children();
1150 if(!p)
1151 return false;
1152
1153 // pass along the xml
1154 QObjectListIt it(*p);
1155 Task *t;
1156 for(; it.current(); ++it) {
1157 QObject *obj = it.current();
1158 if(!obj->inherits("XMPP::Task"))
1159 continue;
1160
1161 t = static_cast<Task*>(obj);
1162 if(t->take(x))
1163 return true;
1164 }
1165
1166 return false;
1167}
1168
1169void Task::safeDelete()
1170{
1171 if(d->deleteme)
1172 return;
1173
1174 d->deleteme = true;
1175 if(!d->insig)
1176 SafeDelete::deleteSingle(this);
1177}
1178
1179void Task::onGo()
1180{
1181}
1182
1183void Task::onDisconnect()
1184{
1185 if(!d->done) {
1186 d->success = false;
1187 d->statusCode = ErrDisc;
1188 d->statusString = tr("Disconnected");
1189
1190 // delay this so that tasks that react don't block the shutdown
1191 QTimer::singleShot(0, this, SLOT(done()));
1192 }
1193}
1194
1195void Task::send(const QDomElement &x)
1196{
1197 client()->send(x);
1198}
1199
1200void Task::setSuccess(int code, const QString &str)
1201{
1202 if(!d->done) {
1203 d->success = true;
1204 d->statusCode = code;
1205 d->statusString = str;
1206 done();
1207 }
1208}
1209
1210void Task::setError(const QDomElement &e)
1211{
1212 if(!d->done) {
1213 d->success = false;
1214 getErrorFromElement(e, &d->statusCode, &d->statusString);
1215 done();
1216 }
1217}
1218
1219void Task::setError(int code, const QString &str)
1220{
1221 if(!d->done) {
1222 d->success = false;
1223 d->statusCode = code;
1224 d->statusString = str;
1225 done();
1226 }
1227}
1228
1229void Task::done()
1230{
1231 if(d->done || d->insig)
1232 return;
1233 d->done = true;
1234
1235 if(d->deleteme || d->autoDelete)
1236 d->deleteme = true;
1237
1238 d->insig = true;
1239 finished();
1240 d->insig = false;
1241
1242 if(d->deleteme)
1243 SafeDelete::deleteSingle(this);
1244}
1245
1246void Task::clientDisconnected()
1247{
1248 onDisconnect();
1249}
1250
1251void Task::debug(const char *fmt, ...)
1252{
1253 char *buf;
1254 QString str;
1255 int size = 1024;
1256 int r;
1257
1258 do {
1259 buf = new char[size];
1260 va_list ap;
1261 va_start(ap, fmt);
1262 r = vsnprintf(buf, size, fmt, ap);
1263 va_end(ap);
1264
1265 if(r != -1)
1266 str = QString(buf);
1267
1268 delete [] buf;
1269
1270 size *= 2;
1271 } while(r == -1);
1272
1273 debug(str);
1274}
1275
1276void Task::debug(const QString &str)
1277{
1278 client()->debug(QString("%1: ").arg(className()) + str);
1279}
1280
1281bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
1282{
1283 if(x.tagName() != "iq")
1284 return false;
1285
1286 Jid from(x.attribute("from"));
1287 Jid local = client()->jid();
1288 Jid server = client()->host();
1289
1290 // empty 'from' ?
1291 if(from.isEmpty()) {
1292 // allowed if we are querying the server
1293 if(!to.isEmpty() && !to.compare(server))
1294 return false;
1295 }
1296 // from ourself?
1297 else if(from.compare(local, false)) {
1298 // allowed if we are querying ourself or the server
1299 if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
1300 return false;
1301 }
1302 // from anywhere else?
1303 else {
1304 if(!from.compare(to))
1305 return false;
1306 }
1307
1308 if(!id.isEmpty()) {
1309 if(x.attribute("id") != id)
1310 return false;
1311 }
1312
1313 if(!xmlns.isEmpty()) {
1314 if(queryNS(x) != xmlns)
1315 return false;
1316 }
1317
1318 return true;
1319}
1320
1321//---------------------------------------------------------------------------
1322// LiveRosterItem
1323//---------------------------------------------------------------------------
1324LiveRosterItem::LiveRosterItem(const Jid &jid)
1325:RosterItem(jid)
1326{
1327 setFlagForDelete(false);
1328}
1329
1330LiveRosterItem::LiveRosterItem(const RosterItem &i)
1331{
1332 setRosterItem(i);
1333 setFlagForDelete(false);
1334}
1335
1336LiveRosterItem::~LiveRosterItem()
1337{
1338}
1339
1340void LiveRosterItem::setRosterItem(const RosterItem &i)
1341{
1342 setJid(i.jid());
1343 setName(i.name());
1344 setGroups(i.groups());
1345 setSubscription(i.subscription());
1346 setAsk(i.ask());
1347 setIsPush(i.isPush());
1348}
1349
1350ResourceList & LiveRosterItem::resourceList()
1351{
1352 return v_resourceList;
1353}
1354
1355ResourceList::Iterator LiveRosterItem::priority()
1356{
1357 return v_resourceList.priority();
1358}
1359
1360const ResourceList & LiveRosterItem::resourceList() const
1361{
1362 return v_resourceList;
1363}
1364
1365ResourceList::ConstIterator LiveRosterItem::priority() const
1366{
1367 return v_resourceList.priority();
1368}
1369
1370bool LiveRosterItem::isAvailable() const
1371{
1372 if(v_resourceList.count() > 0)
1373 return true;
1374 return false;
1375}
1376
1377const Status & LiveRosterItem::lastUnavailableStatus() const
1378{
1379 return v_lastUnavailableStatus;
1380}
1381
1382bool LiveRosterItem::flagForDelete() const
1383{
1384 return v_flagForDelete;
1385}
1386
1387void LiveRosterItem::setLastUnavailableStatus(const Status &s)
1388{
1389 v_lastUnavailableStatus = s;
1390}
1391
1392void LiveRosterItem::setFlagForDelete(bool b)
1393{
1394 v_flagForDelete = b;
1395}
1396
1397//---------------------------------------------------------------------------
1398// LiveRoster
1399//---------------------------------------------------------------------------
1400LiveRoster::LiveRoster()
1401:QValueList<LiveRosterItem>()
1402{
1403}
1404
1405LiveRoster::~LiveRoster()
1406{
1407}
1408
1409void LiveRoster::flagAllForDelete()
1410{
1411 for(Iterator it = begin(); it != end(); ++it)
1412 (*it).setFlagForDelete(true);
1413}
1414
1415LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes)
1416{
1417 Iterator it;
1418 for(it = begin(); it != end(); ++it) {
1419 if((*it).jid().compare(j, compareRes))
1420 break;
1421 }
1422 return it;
1423}
1424
1425LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const
1426{
1427 ConstIterator it;
1428 for(it = begin(); it != end(); ++it) {
1429 if((*it).jid().compare(j, compareRes))
1430 break;
1431 }
1432 return it;
1433}
1434
1435}
Note: See TracBrowser for help on using the repository browser.