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

Last change on this file since 5 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 28.6 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 // HACK HACK HACK
475 QGuardedPtr<ClientStream> pstream = d->stream;
476
477 while(pstream && d->stream->stanzaAvailable()) {
478 Stanza s = d->stream->read();
479
480 QString out = s.toString();
481 debug(QString("Client: incoming: [\n%1]\n").arg(out));
482 xmlIncoming(out);
483
484 QDomElement x = oldStyleNS(s.element());
485 distribute(x);
486 }
487}
488
489void Client::streamIncomingXml(const QString &s)
490{
491 QString str = s;
492 if(str.at(str.length()-1) != '\n')
493 str += '\n';
494 xmlIncoming(str);
495}
496
497void Client::streamOutgoingXml(const QString &s)
498{
499 QString str = s;
500 if(str.at(str.length()-1) != '\n')
501 str += '\n';
502 xmlOutgoing(str);
503}
504
505void Client::debug(const QString &str)
506{
507 debugText(str);
508}
509
510QString Client::genUniqueId()
511{
512 QString s;
513 s.sprintf("a%x", d->id_seed);
514 d->id_seed += 0x10;
515 return s;
516}
517
518Task *Client::rootTask()
519{
520 return d->root;
521}
522
523QDomDocument *Client::doc() const
524{
525 return &d->doc;
526}
527
528void Client::distribute(const QDomElement &x)
529{
530 if(x.hasAttribute("from")) {
531 Jid j(x.attribute("from"));
532 if(!j.isValid()) {
533 debug("Client: bad 'from' JID\n");
534 return;
535 }
536 }
537
538 if(!rootTask()->take(x)) {
539 debug("Client: packet was ignored.\n");
540 }
541}
542
543static QDomElement addCorrectNS(const QDomElement &e)
544{
545 uint x;
546
547 // grab child nodes
548 /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
549 QDomNodeList nl = e.childNodes();
550 for(x = 0; x < nl.count(); ++x)
551 frag.appendChild(nl.item(x).cloneNode());*/
552
553 // find closest xmlns
554 QDomNode n = e;
555 while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
556 n = n.parentNode();
557 QString ns;
558 if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
559 ns = "jabber:client";
560 else
561 ns = n.toElement().attribute("xmlns");
562
563 // make a new node
564 QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
565
566 // copy attributes
567 QDomNamedNodeMap al = e.attributes();
568 for(x = 0; x < al.count(); ++x) {
569 QDomAttr a = al.item(x).toAttr();
570 if(a.name() != "xmlns")
571 i.setAttributeNodeNS(a.cloneNode().toAttr());
572 }
573
574 // copy children
575 QDomNodeList nl = e.childNodes();
576 for(x = 0; x < nl.count(); ++x) {
577 QDomNode n = nl.item(x);
578 if(n.isElement())
579 i.appendChild(addCorrectNS(n.toElement()));
580 else
581 i.appendChild(n.cloneNode());
582 }
583
584 //i.appendChild(frag);
585 return i;
586}
587
588void Client::send(const QDomElement &x)
589{
590 if(!d->stream)
591 return;
592
593 //QString out;
594 //QTextStream ts(&out, IO_WriteOnly);
595 //x.save(ts, 0);
596
597 //QString out = Stream::xmlToString(x);
598 //debug(QString("Client: outgoing: [\n%1]\n").arg(out));
599 //xmlOutgoing(out);
600
601 QDomElement e = addCorrectNS(x);
602 Stanza s = d->stream->createStanza(e);
603 if(s.isNull()) {
604 //printf("bad stanza??\n");
605 return;
606 }
607
608 QString out = s.toString();
609 debug(QString("Client: outgoing: [\n%1]\n").arg(out));
610 xmlOutgoing(out);
611
612 //printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).latin1(), Stream::xmlToString(e).latin1(), s.toString().latin1());
613 d->stream->write(s);
614}
615
616void Client::send(const QString &str)
617{
618 if(!d->stream)
619 return;
620
621 debug(QString("Client: outgoing: [\n%1]\n").arg(str));
622 xmlOutgoing(str);
623 static_cast<ClientStream*>(d->stream)->writeDirect(str);
624}
625
626Stream & Client::stream()
627{
628 return *d->stream;
629}
630
631const LiveRoster & Client::roster() const
632{
633 return d->roster;
634}
635
636const ResourceList & Client::resourceList() const
637{
638 return d->resourceList;
639}
640
641QString Client::host() const
642{
643 return d->host;
644}
645
646QString Client::user() const
647{
648 return d->user;
649}
650
651QString Client::pass() const
652{
653 return d->pass;
654}
655
656QString Client::resource() const
657{
658 return d->resource;
659}
660
661Jid Client::jid() const
662{
663 QString s;
664 if(!d->user.isEmpty())
665 s += d->user + '@';
666 s += d->host;
667 if(!d->resource.isEmpty()) {
668 s += '/';
669 s += d->resource;
670 }
671
672 return Jid(s);
673}
674
675void Client::ppSubscription(const Jid &j, const QString &s)
676{
677 subscription(j, s);
678}
679
680void Client::ppPresence(const Jid &j, const Status &s)
681{
682 if(s.isAvailable())
683 debug(QString("Client: %1 is available.\n").arg(j.full()));
684 else
685 debug(QString("Client: %1 is unavailable.\n").arg(j.full()));
686
687 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
688 GroupChat &i = *it;
689
690 if(i.j.compare(j, false)) {
691 bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false;
692
693 debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us));
694 switch(i.status) {
695 case GroupChat::Connecting:
696 if(us && s.hasError()) {
697 Jid j = i.j;
698 d->groupChatList.remove(it);
699 groupChatError(j, s.errorCode(), s.errorString());
700 }
701 else {
702 // don't signal success unless it is a non-error presence
703 if(!s.hasError()) {
704 i.status = GroupChat::Connected;
705 groupChatJoined(i.j);
706 }
707 groupChatPresence(j, s);
708 }
709 break;
710 case GroupChat::Connected:
711 groupChatPresence(j, s);
712 break;
713 case GroupChat::Closing:
714 if(us && !s.isAvailable()) {
715 Jid j = i.j;
716 d->groupChatList.remove(it);
717 groupChatLeft(j);
718 }
719 break;
720 default:
721 break;
722 }
723
724 return;
725 }
726 }
727
728 if(s.hasError()) {
729 presenceError(j, s.errorCode(), s.errorString());
730 return;
731 }
732
733 // is it me?
734 if(j.compare(jid(), false)) {
735 updateSelfPresence(j, s);
736 }
737 else {
738 // update all relavent roster entries
739 for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) {
740 LiveRosterItem &i = *it;
741
742 if(!i.jid().compare(j, false))
743 continue;
744
745 // roster item has its own resource?
746 if(!i.jid().resource().isEmpty()) {
747 if(i.jid().resource() != j.resource())
748 continue;
749 }
750
751 updatePresence(&i, j, s);
752 }
753 }
754}
755
756void Client::updateSelfPresence(const Jid &j, const Status &s)
757{
758 ResourceList::Iterator rit = d->resourceList.find(j.resource());
759 bool found = (rit == d->resourceList.end()) ? false: true;
760
761 // unavailable? remove the resource
762 if(!s.isAvailable()) {
763 if(found) {
764 debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource()));
765 (*rit).setStatus(s);
766 resourceUnavailable(j, *rit);
767 d->resourceList.remove(rit);
768 }
769 }
770 // available? add/update the resource
771 else {
772 Resource r;
773 if(!found) {
774 r = Resource(j.resource(), s);
775 d->resourceList += r;
776 debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource()));
777 }
778 else {
779 (*rit).setStatus(s);
780 r = *rit;
781 debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource()));
782 }
783
784 resourceAvailable(j, r);
785 }
786}
787
788void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s)
789{
790 ResourceList::Iterator rit = i->resourceList().find(j.resource());
791 bool found = (rit == i->resourceList().end()) ? false: true;
792
793 // unavailable? remove the resource
794 if(!s.isAvailable()) {
795 if(found) {
796 (*rit).setStatus(s);
797 debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
798 resourceUnavailable(j, *rit);
799 i->resourceList().remove(rit);
800 i->setLastUnavailableStatus(s);
801 }
802 }
803 // available? add/update the resource
804 else {
805 Resource r;
806 if(!found) {
807 r = Resource(j.resource(), s);
808 i->resourceList() += r;
809 debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
810 }
811 else {
812 (*rit).setStatus(s);
813 r = *rit;
814 debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
815 }
816
817 resourceAvailable(j, r);
818 }
819}
820
821void Client::pmMessage(const Message &m)
822{
823 debug(QString("Client: Message from %1\n").arg(m.from().full()));
824
825 if(m.type() == "groupchat") {
826 for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
827 const GroupChat &i = *it;
828
829 if(!i.j.compare(m.from(), false))
830 continue;
831
832 if(i.status == GroupChat::Connected)
833 messageReceived(m);
834 }
835 }
836 else
837 messageReceived(m);
838}
839
840void Client::prRoster(const Roster &r)
841{
842 importRoster(r);
843}
844
845void Client::rosterRequest()
846{
847 if(!d->active)
848 return;
849
850 JT_Roster *r = new JT_Roster(rootTask());
851 connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished()));
852 r->get();
853 d->roster.flagAllForDelete(); // mod_groups patch
854 r->go(true);
855}
856
857void Client::slotRosterRequestFinished()
858{
859 JT_Roster *r = (JT_Roster *)sender();
860 // on success, let's take it
861 if(r->success()) {
862 //d->roster.flagAllForDelete(); // mod_groups patch
863
864 importRoster(r->roster());
865
866 for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) {
867 LiveRosterItem &i = *it;
868 if(i.flagForDelete()) {
869 rosterItemRemoved(i);
870 it = d->roster.remove(it);
871 }
872 else
873 ++it;
874 }
875 }
876 else {
877 // don't report a disconnect. Client::error() will do that.
878 if(r->statusCode() == Task::ErrDisc)
879 return;
880 }
881
882 // report success / fail
883 rosterRequestFinished(r->success(), r->statusCode(), r->statusString());
884}
885
886void Client::importRoster(const Roster &r)
887{
888 for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) {
889 importRosterItem(*it);
890 }
891}
892
893void Client::importRosterItem(const RosterItem &item)
894{
895 QString substr;
896 switch(item.subscription().type()) {
897 case Subscription::Both:
898 substr = "<-->"; break;
899 case Subscription::From:
900 substr = " ->"; break;
901 case Subscription::To:
902 substr = "<- "; break;
903 case Subscription::Remove:
904 substr = "xxxx"; break;
905 case Subscription::None:
906 default:
907 substr = "----"; break;
908 }
909
910 QString dstr, str;
911 str.sprintf(" %s %-32s", substr.latin1(), item.jid().full().latin1());
912 if(!item.name().isEmpty())
913 str += QString(" [") + item.name() + "]";
914 str += '\n';
915
916 // Remove
917 if(item.subscription().type() == Subscription::Remove) {
918 LiveRoster::Iterator it = d->roster.find(item.jid());
919 if(it != d->roster.end()) {
920 rosterItemRemoved(*it);
921 d->roster.remove(it);
922 }
923 dstr = "Client: (Removed) ";
924 }
925 // Add/Update
926 else {
927 LiveRoster::Iterator it = d->roster.find(item.jid());
928 if(it != d->roster.end()) {
929 LiveRosterItem &i = *it;
930 i.setFlagForDelete(false);
931 i.setRosterItem(item);
932 rosterItemUpdated(i);
933 dstr = "Client: (Updated) ";
934 }
935 else {
936 LiveRosterItem i(item);
937 d->roster += i;
938
939 // signal it
940 rosterItemAdded(i);
941 dstr = "Client: (Added) ";
942 }
943 }
944
945 debug(dstr + str);
946}
947
948void Client::sendMessage(const Message &m)
949{
950 JT_Message *j = new JT_Message(rootTask(), m);
951 j->go(true);
952}
953
954void Client::sendSubscription(const Jid &jid, const QString &type)
955{
956 JT_Presence *j = new JT_Presence(rootTask());
957 j->sub(jid, type);
958 j->go(true);
959}
960
961void Client::setPresence(const Status &s)
962{
963 JT_Presence *j = new JT_Presence(rootTask());
964 j->pres(s);
965 j->go(true);
966
967 // update our resourceList
968 ppPresence(jid(), s);
969 //ResourceList::Iterator rit = d->resourceList.find(resource());
970 //Resource &r = *rit;
971 //r.setStatus(s);
972}
973
974QString Client::OSName() const
975{
976 return d->osname;
977}
978
979QString Client::timeZone() const
980{
981 return d->tzname;
982}
983
984int Client::timeZoneOffset() const
985{
986 return d->tzoffset;
987}
988
989QString Client::clientName() const
990{
991 return d->clientName;
992}
993
994QString Client::clientVersion() const
995{
996 return d->clientVersion;
997}
998
999void Client::setOSName(const QString &name)
1000{
1001 d->osname = name;
1002}
1003
1004void Client::setTimeZone(const QString &name, int offset)
1005{
1006 d->tzname = name;
1007 d->tzoffset = offset;
1008}
1009
1010void Client::setClientName(const QString &s)
1011{
1012 d->clientName = s;
1013}
1014
1015void Client::setClientVersion(const QString &s)
1016{
1017 d->clientVersion = s;
1018}
1019
1020void Client::s5b_incomingReady()
1021{
1022 S5BConnection *c = d->s5bman->takeIncoming();
1023 if(!c)
1024 return;
1025 if(!d->ftman) {
1026 c->close();
1027 c->deleteLater();
1028 return;
1029 }
1030 d->ftman->s5b_incomingReady(c);
1031 //d->jlman->insertStream(c);
1032 //incomingJidLink();
1033}
1034
1035void Client::ibb_incomingReady()
1036{
1037 IBBConnection *c = d->ibbman->takeIncoming();
1038 if(!c)
1039 return;
1040 c->deleteLater();
1041 //d->jlman->insertStream(c);
1042 //incomingJidLink();
1043}
1044
1045//----------------------------------------------------------------------------
1046// Task
1047//----------------------------------------------------------------------------
1048class Task::TaskPrivate
1049{
1050public:
1051 TaskPrivate() {}
1052
1053 QString id;
1054 bool success;
1055 int statusCode;
1056 QString statusString;
1057 Client *client;
1058 bool insig, deleteme, autoDelete;
1059 bool done;
1060};
1061
1062Task::Task(Task *parent)
1063:QObject(parent)
1064{
1065 init();
1066
1067 d->client = parent->client();
1068 d->id = client()->genUniqueId();
1069 connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
1070}
1071
1072Task::Task(Client *parent, bool)
1073:QObject(0)
1074{
1075 init();
1076
1077 d->client = parent;
1078 connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
1079}
1080
1081Task::~Task()
1082{
1083 delete d;
1084}
1085
1086void Task::init()
1087{
1088 d = new TaskPrivate;
1089 d->success = false;
1090 d->insig = false;
1091 d->deleteme = false;
1092 d->autoDelete = false;
1093 d->done = false;
1094}
1095
1096Task *Task::parent() const
1097{
1098 return (Task *)QObject::parent();
1099}
1100
1101Client *Task::client() const
1102{
1103 return d->client;
1104}
1105
1106QDomDocument *Task::doc() const
1107{
1108 return client()->doc();
1109}
1110
1111QString Task::id() const
1112{
1113 return d->id;
1114}
1115
1116bool Task::success() const
1117{
1118 return d->success;
1119}
1120
1121int Task::statusCode() const
1122{
1123 return d->statusCode;
1124}
1125
1126const QString & Task::statusString() const
1127{
1128 return d->statusString;
1129}
1130
1131void Task::go(bool autoDelete)
1132{
1133 d->autoDelete = autoDelete;
1134
1135 onGo();
1136}
1137
1138bool Task::take(const QDomElement &x)
1139{
1140 const QObjectList *p = children();
1141 if(!p)
1142 return false;
1143
1144 // pass along the xml
1145 QObjectListIt it(*p);
1146 Task *t;
1147 for(; it.current(); ++it) {
1148 QObject *obj = it.current();
1149 if(!obj->inherits("XMPP::Task"))
1150 continue;
1151
1152 t = static_cast<Task*>(obj);
1153 if(t->take(x))
1154 return true;
1155 }
1156
1157 return false;
1158}
1159
1160void Task::safeDelete()
1161{
1162 if(d->deleteme)
1163 return;
1164
1165 d->deleteme = true;
1166 if(!d->insig)
1167 SafeDelete::deleteSingle(this);
1168}
1169
1170void Task::onGo()
1171{
1172}
1173
1174void Task::onDisconnect()
1175{
1176 if(!d->done) {
1177 d->success = false;
1178 d->statusCode = ErrDisc;
1179 d->statusString = tr("Disconnected");
1180
1181 // delay this so that tasks that react don't block the shutdown
1182 QTimer::singleShot(0, this, SLOT(done()));
1183 }
1184}
1185
1186void Task::send(const QDomElement &x)
1187{
1188 client()->send(x);
1189}
1190
1191void Task::setSuccess(int code, const QString &str)
1192{
1193 if(!d->done) {
1194 d->success = true;
1195 d->statusCode = code;
1196 d->statusString = str;
1197 done();
1198 }
1199}
1200
1201void Task::setError(const QDomElement &e)
1202{
1203 if(!d->done) {
1204 d->success = false;
1205 getErrorFromElement(e, &d->statusCode, &d->statusString);
1206 done();
1207 }
1208}
1209
1210void Task::setError(int code, const QString &str)
1211{
1212 if(!d->done) {
1213 d->success = false;
1214 d->statusCode = code;
1215 d->statusString = str;
1216 done();
1217 }
1218}
1219
1220void Task::done()
1221{
1222 if(d->done || d->insig)
1223 return;
1224 d->done = true;
1225
1226 if(d->deleteme || d->autoDelete)
1227 d->deleteme = true;
1228
1229 d->insig = true;
1230 finished();
1231 d->insig = false;
1232
1233 if(d->deleteme)
1234 SafeDelete::deleteSingle(this);
1235}
1236
1237void Task::clientDisconnected()
1238{
1239 onDisconnect();
1240}
1241
1242void Task::debug(const char *fmt, ...)
1243{
1244 char *buf;
1245 QString str;
1246 int size = 1024;
1247 int r;
1248
1249 do {
1250 buf = new char[size];
1251 va_list ap;
1252 va_start(ap, fmt);
1253 r = vsnprintf(buf, size, fmt, ap);
1254 va_end(ap);
1255
1256 if(r != -1)
1257 str = QString(buf);
1258
1259 delete [] buf;
1260
1261 size *= 2;
1262 } while(r == -1);
1263
1264 debug(str);
1265}
1266
1267void Task::debug(const QString &str)
1268{
1269 client()->debug(QString("%1: ").arg(className()) + str);
1270}
1271
1272bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
1273{
1274 if(x.tagName() != "iq")
1275 return false;
1276
1277 Jid from(x.attribute("from"));
1278 Jid local = client()->jid();
1279 Jid server = client()->host();
1280
1281 // empty 'from' ?
1282 if(from.isEmpty()) {
1283 // allowed if we are querying the server
1284 if(!to.isEmpty() && !to.compare(server))
1285 return false;
1286 }
1287 // from ourself?
1288 else if(from.compare(local, false)) {
1289 // allowed if we are querying ourself or the server
1290 if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
1291 return false;
1292 }
1293 // from anywhere else?
1294 else {
1295 if(!from.compare(to))
1296 return false;
1297 }
1298
1299 if(!id.isEmpty()) {
1300 if(x.attribute("id") != id)
1301 return false;
1302 }
1303
1304 if(!xmlns.isEmpty()) {
1305 if(queryNS(x) != xmlns)
1306 return false;
1307 }
1308
1309 return true;
1310}
1311
1312//---------------------------------------------------------------------------
1313// LiveRosterItem
1314//---------------------------------------------------------------------------
1315LiveRosterItem::LiveRosterItem(const Jid &jid)
1316:RosterItem(jid)
1317{
1318 setFlagForDelete(false);
1319}
1320
1321LiveRosterItem::LiveRosterItem(const RosterItem &i)
1322{
1323 setRosterItem(i);
1324 setFlagForDelete(false);
1325}
1326
1327LiveRosterItem::~LiveRosterItem()
1328{
1329}
1330
1331void LiveRosterItem::setRosterItem(const RosterItem &i)
1332{
1333 setJid(i.jid());
1334 setName(i.name());
1335 setGroups(i.groups());
1336 setSubscription(i.subscription());
1337 setAsk(i.ask());
1338 setIsPush(i.isPush());
1339}
1340
1341ResourceList & LiveRosterItem::resourceList()
1342{
1343 return v_resourceList;
1344}
1345
1346ResourceList::Iterator LiveRosterItem::priority()
1347{
1348 return v_resourceList.priority();
1349}
1350
1351const ResourceList & LiveRosterItem::resourceList() const
1352{
1353 return v_resourceList;
1354}
1355
1356ResourceList::ConstIterator LiveRosterItem::priority() const
1357{
1358 return v_resourceList.priority();
1359}
1360
1361bool LiveRosterItem::isAvailable() const
1362{
1363 if(v_resourceList.count() > 0)
1364 return true;
1365 return false;
1366}
1367
1368const Status & LiveRosterItem::lastUnavailableStatus() const
1369{
1370 return v_lastUnavailableStatus;
1371}
1372
1373bool LiveRosterItem::flagForDelete() const
1374{
1375 return v_flagForDelete;
1376}
1377
1378void LiveRosterItem::setLastUnavailableStatus(const Status &s)
1379{
1380 v_lastUnavailableStatus = s;
1381}
1382
1383void LiveRosterItem::setFlagForDelete(bool b)
1384{
1385 v_flagForDelete = b;
1386}
1387
1388//---------------------------------------------------------------------------
1389// LiveRoster
1390//---------------------------------------------------------------------------
1391LiveRoster::LiveRoster()
1392:QValueList<LiveRosterItem>()
1393{
1394}
1395
1396LiveRoster::~LiveRoster()
1397{
1398}
1399
1400void LiveRoster::flagAllForDelete()
1401{
1402 for(Iterator it = begin(); it != end(); ++it)
1403 (*it).setFlagForDelete(true);
1404}
1405
1406LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes)
1407{
1408 Iterator it;
1409 for(it = begin(); it != end(); ++it) {
1410 if((*it).jid().compare(j, compareRes))
1411 break;
1412 }
1413 return it;
1414}
1415
1416LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const
1417{
1418 ConstIterator it;
1419 for(it = begin(); it != end(); ++it) {
1420 if((*it).jid().compare(j, compareRes))
1421 break;
1422 }
1423 return it;
1424}
1425
1426}
Note: See TracBrowser for help on using the repository browser.