1
2 /***************************************************************************
3 jabberaccount.cpp - core Jabber account class
4 -------------------
5 begin : Sat M??? 8 2003
6 copyright : (C) 2003 by Till Gerken <till@tantalo.net>
7 Based on JabberProtocol by Daniel Stone <dstone@kde.org>
8 and Till Gerken <till@tantalo.net>.
9 copyright : (C) 2006 by Olivier Goffart <ogoffart at kde.org>
10 Copyright 2006 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
11
12 Kopete (C) 2001-2006 Kopete developers
13 <kopete-devel@kde.org>.
14 ***************************************************************************/
15
16 /***************************************************************************
17 * *
18 * This program is free software; you can redistribute it and/or modify *
19 * it under the terms of the GNU General Public License as published by *
20 * the Free Software Foundation; either either version 2
21 of the License, or (at your option) any later version.of the License, or *
22 * (at your option) any later version. *
23 * *
24 ***************************************************************************/
25
26 #include "im.h"
27 #include "filetransfer.h"
28 #include "xmpp.h"
29 #include "xmpp_tasks.h"
30 #include "qca.h"
31 #include "bsocket.h"
32
33 #include "jabberaccount.h"
34 #include "jabberbookmarks.h"
35
36 #include <time.h>
37
38 #include <QString>
39 #include <QRegExp>
40 #include <QTimer>
41
42 #include <KComponentData>
43 #include <KConfig>
44 #include <KMessageBox>
45 #include <KLocalizedString>
46 #include <KAboutData>
47 #include <k3socketbase.h>
48 #include <KPasswordDialog>
49 #include <KInputDialog>
50 #include <KActionMenu>
51 #include <KComponentData>
52
53 #include "jabberconnector.h"
54 #include "jabberresourcepool.h"
55 #include "jabbercontactpool.h"
56 // #include "jabberfiletransfer.h"
57 #include "jabbercontact.h"
58 #include "jabbergroupcontact.h"
59 #include "jabbercapabilitiesmanager.h"
60 // #include "jabbertransport.h"
61 /*#include "dlgxmppconsole.h"
62 #include "dlgjabberservices.h"
63 #include "dlgjabberchatjoin.h"*/
64 #include "jt_pubsub.h"
65
66 #include <sys/utsname.h>
67
68 #ifdef SUPPORT_JINGLE
69 #include "voicecaller.h"
70 #include "jinglevoicecaller.h"
71
72 // NOTE: Disabled for 0.12, will develop them futher in KDE4
73 // #include "jinglesessionmanager.h"
74 // #include "jinglesession.h"
75 // #include "jinglevoicesession.h"
76 #include "jinglevoicesessiondialog.h"
77 #endif
78
79 #define KOPETE_CAPS_NODE "http://kopete.kde.org/jabber/caps"
80
81
82
JabberAccount(JabberProtocol * parent,const QString & accountId)83 JabberAccount::JabberAccount (JabberProtocol * parent, const QString & accountId)
84 :JabberProtocol( parent/*, accountId, false */)
85 {
86 qCDebug(JABBER_PROTOCOL_LOG) << "Instantiating new account " << accountId;
87
88 m_protocol = parent;
89
90 m_jabberClient = 0L;
91
92 m_resourcePool = 0L;
93 m_contactPool = 0L;
94 #ifdef SUPPORT_JINGLE
95 m_voiceCaller = 0L;
96 //m_jingleSessionManager = 0L; // NOTE: Disabled for 0.12
97 #endif
98 // m_bookmarks = new JabberBookmarks(this);
99 m_removing=false;
100 m_notifiedUserCannotBindTransferPort = false;
101 // add our own contact to the pool
102 // JabberContact *myContact = contactPool()->addContact ( XMPP::RosterItem ( accountId ), Kopete::ContactList::self()->myself(), false );
103 // setMyself( myContact );
104
105 m_initialPresence = XMPP::Status ( "", "", 5, true );
106
107 }
108
~JabberAccount()109 JabberAccount::~JabberAccount ()
110 {
111 disconnect ( 0/*Kopete::Account::Manual*/ );
112
113 // Remove this account from Capabilities manager.
114 protocol()->capabilitiesManager()->removeAccount( this );
115
116 cleanup ();
117
118 // QMap<QString,JabberTransport*> tranposrts_copy=m_transports;
119 // QMap<QString,JabberTransport*>::Iterator it;
120 // for ( it = tranposrts_copy.begin(); it != tranposrts_copy.end(); ++it )
121 // delete it.value();
122 }
123
cleanup()124 void JabberAccount::cleanup ()
125 {
126
127 delete m_jabberClient;
128
129 m_jabberClient = 0L;
130
131 delete m_resourcePool;
132 m_resourcePool = 0L;
133
134 delete m_contactPool;
135 m_contactPool = 0L;
136
137 #ifdef SUPPORT_JINGLE
138 delete m_voiceCaller;
139 m_voiceCaller = 0L;
140
141 // delete m_jingleSessionManager;
142 // m_jingleSessionManager = 0L;
143 #endif
144 }
145
146 // void JabberAccount::setS5BServerPort ( int port )
147 // {
148 //
149 // if ( !m_jabberClient )
150 // {
151 // return;
152 // }
153 //
154 // if ( !m_jabberClient->setS5BServerPort ( port ) && !m_notifiedUserCannotBindTransferPort)
155 // {
156 // KMessageBox::queuedMessageBox ( 0, KMessageBox::Sorry,
157 // i18n ( "Could not bind the Jabber file transfer manager to a local port. Please check if the file transfer port is already in use, or choose another port in the account settings." ),
158 // i18n ( "Failed to start Jabber File Transfer Manager" ) );
159 // m_notifiedUserCannotBindTransferPort = true;
160 // }
161 //
162 // }
163
resourcePool()164 JabberResourcePool *JabberAccount::resourcePool ()
165 {
166
167 if ( !m_resourcePool )
168 m_resourcePool = new JabberResourcePool ( this );
169
170 return m_resourcePool;
171
172 }
173
contactPool()174 JabberContactPool *JabberAccount::contactPool ()
175 {
176
177 if ( !m_contactPool )
178 m_contactPool = new JabberContactPool ( this );
179
180 return m_contactPool;
181
182 }
183
184 // bool JabberAccount::createContact (const QString & contactId, Kopete::MetaContact * metaContact)
185 // {
186 //
187 // // collect all group names
188 // QStringList groupNames;
189 // Kopete::GroupList groupList = metaContact->groups();
190 // foreach( Kopete::Group *group, groupList )
191 // groupNames += group->displayName();
192 //
193 // XMPP::Jid jid ( contactId );
194 // XMPP::RosterItem item ( jid );
195 // item.setName ( metaContact->displayName () );
196 // item.setGroups ( groupNames );
197 //
198 // // this contact will be created with the "dirty" flag set
199 // // (it will get reset if the contact appears in the roster during connect)
200 // JabberContact *contact = contactPool()->addContact ( item, metaContact, true );
201 //
202 // return ( contact != 0 );
203 //
204 // }
205
errorConnectFirst()206 void JabberAccount::errorConnectFirst ()
207 {
208
209 KMessageBox::queuedMessageBox ( 0,
210 KMessageBox::Error,
211 i18n ("Please connect first."), i18n ("Jabber Error") );
212
213 }
214
errorConnectionLost()215 void JabberAccount::errorConnectionLost ()
216 {
217 // disconnected(0 /*Kopete::Account::ConnectionReset */);
218 }
219
isConnecting()220 bool JabberAccount::isConnecting ()
221 {
222 return false;
223 // XMPP::Jid jid ( contactId () );
224 //
225 // // see if we are currently trying to connect
226 // return resourcePool()->bestResource ( jid ).status().show () == QString("connecting");
227
228 }
229
connectWithPassword(const QString & password)230 void JabberAccount::connectWithPassword ( const QString &password )
231 {
232 qCDebug(JABBER_PROTOCOL_LOG) << "called";
233
234 /* Cancel connection process if no password has been supplied. */
235 if ( password.isEmpty () )
236 {
237 disconnect ( 0/*Kopete::Account::Manual*/ );
238 return;
239 }
240
241 /* Don't do anything if we are already connected. */
242 if ( isConnected () )
243 return;
244
245 // instantiate new client backend or clean up old one
246 if ( !m_jabberClient )
247 {
248 m_jabberClient = new JabberClient;
249
250 QObject::connect ( m_jabberClient, SIGNAL (csDisconnected()), this, SLOT (slotCSDisconnected()) );
251 QObject::connect ( m_jabberClient, SIGNAL (csError(int)), this, SLOT (slotCSError(int)) );
252 QObject::connect ( m_jabberClient, SIGNAL (tlsWarning(QCA::TLS::IdentityResult,QCA::Validity)), this, SLOT (slotHandleTLSWarning(QCA::TLS::IdentityResult,QCA::Validity)) );
253 QObject::connect ( m_jabberClient, SIGNAL (connected()), this, SLOT (slotConnected()) );
254 QObject::connect ( m_jabberClient, SIGNAL (error(JabberClient::ErrorCode)), this, SLOT (slotClientError(JabberClient::ErrorCode)) );
255
256 QObject::connect ( m_jabberClient, SIGNAL (subscription(XMPP::Jid,QString)),
257 this, SLOT (slotSubscription(XMPP::Jid,QString)) );
258 QObject::connect ( m_jabberClient, SIGNAL (rosterRequestFinished(bool)),
259 this, SLOT (slotRosterRequestFinished(bool)) );
260 QObject::connect ( m_jabberClient, SIGNAL (newContact(XMPP::RosterItem)),
261 this, SLOT (slotContactUpdated(XMPP::RosterItem)) );
262 QObject::connect ( m_jabberClient, SIGNAL (contactUpdated(XMPP::RosterItem)),
263 this, SLOT (slotContactUpdated(XMPP::RosterItem)) );
264 QObject::connect ( m_jabberClient, SIGNAL (contactDeleted(XMPP::RosterItem)),
265 this, SLOT (slotContactDeleted(XMPP::RosterItem)) );
266 QObject::connect ( m_jabberClient, SIGNAL (resourceAvailable(XMPP::Jid,XMPP::Resource)),
267 this, SLOT (slotResourceAvailable(XMPP::Jid,XMPP::Resource)) );
268 QObject::connect ( m_jabberClient, SIGNAL (resourceUnavailable(XMPP::Jid,XMPP::Resource)),
269 this, SLOT (slotResourceUnavailable(XMPP::Jid,XMPP::Resource)) );
270 QObject::connect ( m_jabberClient, SIGNAL (messageReceived(XMPP::Message)),
271 this, SLOT (slotReceivedMessage(XMPP::Message)) );
272 QObject::connect ( m_jabberClient, SIGNAL (incomingFileTransfer()),
273 this, SLOT (slotIncomingFileTransfer()) );
274 QObject::connect ( m_jabberClient, SIGNAL (groupChatJoined(XMPP::Jid)),
275 this, SLOT (slotGroupChatJoined(XMPP::Jid)) );
276 QObject::connect ( m_jabberClient, SIGNAL (groupChatLeft(XMPP::Jid)),
277 this, SLOT (slotGroupChatLeft(XMPP::Jid)) );
278 QObject::connect ( m_jabberClient, SIGNAL (groupChatPresence(XMPP::Jid,XMPP::Status)),
279 this, SLOT (slotGroupChatPresence(XMPP::Jid,XMPP::Status)) );
280 QObject::connect ( m_jabberClient, SIGNAL (groupChatError(XMPP::Jid,int,QString)),
281 this, SLOT (slotGroupChatError(XMPP::Jid,int,QString)) );
282 QObject::connect ( m_jabberClient, SIGNAL (debugMessage(QString)),
283 this, SLOT (slotClientDebugMessage(QString)) );
284 }
285 else
286 {
287 m_jabberClient->disconnect ();
288 }
289
290 // we need to use the old protocol for now
291 m_jabberClient->setUseXMPP09 ( true );
292
293 // set SSL flag (this should be converted to forceTLS when using the new protocol)
294 m_jabberClient->setUseSSL ( false );
295
296 // override server and port (this should be dropped when using the new protocol and no direct SSL)
297 m_jabberClient->setOverrideHost ( true, server (), port () );
298
299 // allow plaintext password authentication or not?
300 m_jabberClient->setAllowPlainTextPassword ( false );
301
302 // enable file transfer (if empty, IP will be set after connection has been established)
303 // KConfigGroup config = KSharedConfig::openConfig()->group ( "Jabber" );
304 // m_jabberClient->setFileTransfersEnabled ( true, config.readEntry ( "LocalIP" ) );
305 // setS5BServerPort ( config.readEntry ( "LocalPort", 8010 ) );
306
307 //
308 // Determine system name
309 //
310 struct utsname utsBuf;
311
312 uname (&utsBuf);
313
314 m_jabberClient->setClientName ("Kopete");
315 m_jabberClient->setClientVersion (KGlobal::mainComponent().aboutData()->version ());
316 m_jabberClient->setOSName (QString ("%1 %2").arg (utsBuf.sysname, 1).arg (utsBuf.release, 2));
317
318
319 // Set caps node information
320 m_jabberClient->setCapsNode(KOPETE_CAPS_NODE);
321 m_jabberClient->setCapsVersion(KGlobal::mainComponent().aboutData()->version());
322
323 // Set Disco Identity information
324 DiscoItem::Identity identity;
325 identity.category = "client";
326 identity.type = "pc";
327 identity.name = "Kopete";
328 m_jabberClient->setDiscoIdentity(identity);
329
330 //BEGIN TIMEZONE INFORMATION
331 //
332 // Set timezone information (code from Psi)
333 // Copyright (C) 2001-2003 Justin Karneges
334 //
335 time_t x;
336 time(&x);
337 char str[256];
338 char fmt[32];
339 int timezoneOffset(0);
340 QString timezoneString;
341
342 strcpy ( fmt, "%z" );
343 strftime ( str, 256, fmt, localtime ( &x ) );
344
345 if ( strcmp ( fmt, str ) )
346 {
347 QString s = str;
348 if ( s.at ( 0 ) == '+' )
349 s.remove ( 0, 1 );
350 s.truncate ( s.length () - 2 );
351 timezoneOffset = s.toInt();
352 }
353
354 strcpy ( fmt, "%Z" );
355 strftime ( str, 256, fmt, localtime ( &x ) );
356
357 if ( strcmp ( fmt, str ) )
358 timezoneString = str;
359 //END of timezone code
360
361 qCDebug(JABBER_PROTOCOL_LOG) << "Determined timezone " << timezoneString << " with UTC offset " << timezoneOffset << " hours.";
362
363 m_jabberClient->setTimeZone ( timezoneString, timezoneOffset );
364
365 qCDebug(JABBER_PROTOCOL_LOG) << "Connecting to Jabber server " << server() << ":" << port();
366
367 setPresence( XMPP::Status ("connecting", "", 0, true) );
368
369 switch ( m_jabberClient->connect ( XMPP::Jid ( accountId () + QString("/") + resource () ), password ) )
370 {
371 case JabberClient::NoTLS:
372 // no SSL support, at the connecting stage this means the problem is client-side
373 KMessageBox::queuedMessageBox(0, KMessageBox::Error,
374 i18n ("SSL support could not be initialized for account %1. This is most likely because the QCA TLS plugin is not installed on your system.",
375 accountId()),
376 i18n ("Jabber SSL Error"));
377 break;
378
379 case JabberClient::Ok:
380 default:
381 // everything alright!
382
383 break;
384 }
385 }
386
slotClientDebugMessage(const QString & msg)387 void JabberAccount::slotClientDebugMessage ( const QString &msg )
388 {
389
390 qCDebug(JABBER_PROTOCOL_LOG) << msg;
391
392 }
393
handleTLSWarning(JabberClient * jabberClient,QCA::TLS::IdentityResult identityResult,QCA::Validity validityResult)394 bool JabberAccount::handleTLSWarning (
395 JabberClient *jabberClient,
396 QCA::TLS::IdentityResult identityResult,
397 QCA::Validity validityResult )
398 {
399 QString validityString, code, idString, idCode;
400
401 QString server = jabberClient->jid().domain ();
402 QString accountId = jabberClient->jid().bare ();
403
404 switch ( identityResult )
405 {
406 case QCA::TLS::Valid:
407 break;
408 case QCA::TLS::HostMismatch:
409 idString = i18n("The host name does not match the one in the certificate.");
410 idCode = "HostMismatch";
411 break;
412 case QCA::TLS::InvalidCertificate:
413 idString = i18n("The certificate is invalid.");
414 idCode = "InvalidCert";
415 break;
416 case QCA::TLS::NoCertificate:
417 idString = i18n("No certificate was presented.");
418 idCode = "NoCert";
419 break;
420 }
421
422 switch ( validityResult )
423 {
424 case QCA::ValidityGood:
425 break;
426 case QCA::ErrorRejected:
427 validityString = i18n("The Certificate Authority rejected the certificate.");
428 code = "Rejected";
429 break;
430 case QCA::ErrorUntrusted:
431 validityString = i18n("The certificate is not trusted.");
432 code = "Untrusted";
433 break;
434 case QCA::ErrorSignatureFailed:
435 validityString = i18n("The signature is invalid.");
436 code = "SignatureFailed";
437 break;
438 case QCA::ErrorInvalidCA:
439 validityString = i18n("The Certificate Authority is invalid.");
440 code = "InvalidCA";
441 break;
442 case QCA::ErrorInvalidPurpose:
443 validityString = i18n("Invalid certificate purpose.");
444 code = "InvalidPurpose";
445 break;
446 case QCA::ErrorSelfSigned:
447 validityString = i18n("The certificate is self-signed.");
448 code = "SelfSigned";
449 break;
450 case QCA::ErrorRevoked:
451 validityString = i18n("The certificate has been revoked.");
452 code = "Revoked";
453 break;
454 case QCA::ErrorPathLengthExceeded:
455 validityString = i18n("Maximum certificate chain length was exceeded.");
456 code = "PathLengthExceeded";
457 break;
458 case QCA::ErrorExpired:
459 validityString = i18n("The certificate has expired.");
460 code = "Expired";
461 break;
462 case QCA::ErrorExpiredCA:
463 validityString = i18n("The Certificate Authority has expired.");
464 code = "ExpiredCA";
465 break;
466 case QCA::ErrorValidityUnknown:
467 validityString = i18n("Validity is unknown.");
468 code = "ValidityUnknown";
469 break;
470 }
471
472 QString message;
473
474 if (!idString.isEmpty())
475 {
476 if (!validityString.isEmpty())
477 {
478 message = i18n("<qt><p>The identity and the certificate of server %1 could not be "
479 "validated for account %2:</p><p>%3</p><p>%4</p><p>Do you want to continue?</p></qt>",
480 server, accountId, idString, validityString);
481 }
482 else
483 {
484 message = i18n("<qt><p>The certificate of server %1 could not be validated for "
485 "account %2: %3</p><p>Do you want to continue?</p></qt>",
486 server, accountId, idString);
487 }
488 } else {
489 message = i18n("<qt><p>The certificate of server %1 could not be validated for "
490 "account %2: %3</p><p>Do you want to continue?</p></qt>",
491 server, accountId, validityString);
492 }
493
494 return ( KMessageBox::warningContinueCancel ( 0,
495 message,
496 i18n("Jabber Connection Certificate Problem"),
497 KStandardGuiItem::cont(),KStandardGuiItem::cancel(),
498 QString("KopeteTLSWarning") + server + idCode + code) == KMessageBox::Continue );
499
500 }
501
slotHandleTLSWarning(QCA::TLS::IdentityResult identityResult,QCA::Validity validityResult)502 void JabberAccount::slotHandleTLSWarning (
503 QCA::TLS::IdentityResult identityResult,
504 QCA::Validity validityResult )
505 {
506 qCDebug(JABBER_PROTOCOL_LOG) << "Handling TLS warning...";
507
508 if ( handleTLSWarning ( m_jabberClient, identityResult, validityResult ) )
509 {
510 // resume stream
511 m_jabberClient->continueAfterTLSWarning ();
512 }
513 else
514 {
515 // disconnect stream
516 disconnect ( 0/*Kopete::Account::Manual*/ );
517 }
518
519 }
520
slotClientError(JabberClient::ErrorCode errorCode)521 void JabberAccount::slotClientError ( JabberClient::ErrorCode errorCode )
522 {
523 qCDebug(JABBER_PROTOCOL_LOG) << "Handling client error...";
524
525 switch ( errorCode )
526 {
527 case JabberClient::NoTLS:
528 default:
529 KMessageBox::queuedMessageBox ( 0, KMessageBox::Error,
530 i18n ("An encrypted connection with the Jabber server could not be established."),
531 i18n ("Jabber Connection Error"));
532 disconnect ( 0/*Kopete::Account::Manual*/ );
533 break;
534 }
535
536 }
537
slotConnected()538 void JabberAccount::slotConnected ()
539 {
540 qCDebug(JABBER_PROTOCOL_LOG) << "Connected to Jabber server.";
541
542 #ifdef SUPPORT_JINGLE
543 if(!m_voiceCaller)
544 {
545 qCDebug(JABBER_PROTOCOL_LOG) << "Creating Jingle Voice caller...";
546 m_voiceCaller = new JingleVoiceCaller( this );
547 QObject::connect(m_voiceCaller,SIGNAL(incoming(Jid)),this,SLOT(slotIncomingVoiceCall(Jid)));
548 m_voiceCaller->initialize();
549 }
550
551 #if 0
552 if(!m_jingleSessionManager)
553 {
554 qCDebug(JABBER_PROTOCOL_LOG) << "Creating Jingle Session Manager...";
555 m_jingleSessionManager = new JingleSessionManager( this );
556 QObject::connect(m_jingleSessionManager, SIGNAL(incomingSession(QString,JingleSession*)), this, SLOT(slotIncomingJingleSession(QString,JingleSession*)));
557 }
558 #endif
559
560 // Set caps extensions
561 m_jabberClient->client()->addExtension("voice-v1", Features(QString("http://www.google.com/xmpp/protocol/voice/v1")));
562 #endif
563
564 qCDebug(JABBER_PROTOCOL_LOG) << "Requesting roster...";
565 m_jabberClient->requestRoster ();
566 }
567
slotRosterRequestFinished(bool success)568 void JabberAccount::slotRosterRequestFinished ( bool success )
569 {
570
571 if ( success )
572 {
573 // the roster was imported successfully, clear
574 // all "dirty" items from the contact list
575 contactPool()->cleanUp ();
576 }
577
578 /* Since we are online now, set initial presence. Don't do this
579 * before the roster request or we will receive presence
580 * information before we have updated our roster with actual
581 * contacts from the server! (Iris won't forward presence
582 * information in that case either). */
583 qCDebug(JABBER_PROTOCOL_LOG) << "Setting initial presence...";
584 setPresence ( m_initialPresence );
585
586 }
587
slotIncomingFileTransfer()588 void JabberAccount::slotIncomingFileTransfer ()
589 {
590
591 // delegate the work to a file transfer object
592 // new JabberFileTransfer ( this, client()->fileTransferManager()->takeIncoming () );
593
594 }
595
setOnlineStatus(const JabberProtocol::OnlineStatus & status,const QString & reason)596 void JabberAccount::setOnlineStatus( const JabberProtocol::OnlineStatus& status, const QString& reason )
597 {
598 XMPP::Status xmppStatus = m_protocol->osToStatus( status, reason );
599
600 if( status == JabberProtocol::JabberOffline )
601 {
602 xmppStatus.setIsAvailable( false );
603 qCDebug(JABBER_PROTOCOL_LOG) << "CROSS YOUR FINGERS! THIS IS GONNA BE WILD";
604 disconnect (0/*Manual*/, xmppStatus);
605 return;
606 }
607
608 if( isConnecting () )
609 {
610 return;
611 }
612
613
614 if ( !isConnected () )
615 {
616 // we are not connected yet, so connect now
617 m_initialPresence = xmppStatus;
618 // connect ( status );
619 }
620 else
621 {
622 setPresence ( xmppStatus );
623 }
624 }
625
setStatusMessage(const QString & statusMessage)626 void JabberAccount::setStatusMessage( const QString& statusMessage )
627 {
628 Q_UNUSED(statusMessage);
629 }
630
disconnect(int reason)631 void JabberAccount::disconnect ( int reason )
632 {
633 qCDebug(JABBER_PROTOCOL_LOG) << "disconnect() called";
634
635 if (isConnected ())
636 {
637 qCDebug(JABBER_PROTOCOL_LOG) << "Still connected, closing connection...";
638 /* Tell backend class to disconnect. */
639 m_jabberClient->disconnect ();
640 }
641
642 // make sure that the connection animation gets stopped if we're still
643 // in the process of connecting
644 setPresence ( XMPP::Status ("", "", 0, false) );
645 m_initialPresence = XMPP::Status ("", "", 5, true);
646
647 /* FIXME:
648 * We should delete the JabberClient instance here,
649 * but active timers in Iris prevent us from doing so.
650 * (in a failed connection attempt, these timers will
651 * try to access an already deleted object).
652 * Instead, the instance will lurk until the next
653 * connection attempt.
654 */
655 qCDebug(JABBER_PROTOCOL_LOG) << "Disconnected.";
656
657 // disconnected ( reason );
658 }
659
disconnect(int reason,XMPP::Status & status)660 void JabberAccount::disconnect( int reason, XMPP::Status &status )
661 {
662 qCDebug(JABBER_PROTOCOL_LOG) << "disconnect( reason, status ) called";
663
664 if (isConnected ())
665 {
666 qCDebug(JABBER_PROTOCOL_LOG) << "Still connected, closing connection...";
667 /* Tell backend class to disconnect. */
668 m_jabberClient->disconnect (status);
669 }
670
671 // make sure that the connection animation gets stopped if we're still
672 // in the process of connecting
673 setPresence ( status );
674
675 /* FIXME:
676 * We should delete the JabberClient instance here,
677 * but active timers in Iris prevent us from doing so.
678 * (in a failed connection attempt, these timers will
679 * try to access an already deleted object).
680 * Instead, the instance will lurk until the next
681 * connection attempt.
682 */
683 qCDebug(JABBER_PROTOCOL_LOG) << "Disconnected.";
684
685 // Kopete::Account::disconnected ( reason );
686 }
687
disconnect()688 void JabberAccount::disconnect ()
689 {
690 disconnect ( /*Manual*/0 );
691 }
692
slotConnect()693 void JabberAccount::slotConnect ()
694 {
695 // connect ();
696 }
697
slotDisconnect()698 void JabberAccount::slotDisconnect ()
699 {
700 disconnect ( /*Kopete::Account::Manual*/ 0);
701 }
702
slotCSDisconnected()703 void JabberAccount::slotCSDisconnected ()
704 {
705 qCDebug(JABBER_PROTOCOL_LOG) << "Disconnected from Jabber server.";
706
707 /*
708 * We should delete the JabberClient instance here,
709 * but timers etc prevent us from doing so. Iris does
710 * not like to be deleted from a slot.
711 */
712
713 /* It seems that we don't get offline notifications when going offline
714 * with the protocol, so clear all resources manually. */
715 resourcePool()->clear();
716
717 }
718
handleStreamError(int streamError,int streamCondition,int connectorCode,const QString & server,int & errorClass,QString additionalErrMsg)719 void JabberAccount::handleStreamError (int streamError, int streamCondition, int connectorCode, const QString &server, int &errorClass, QString additionalErrMsg)
720 {
721 QString errorText;
722 QString errorCondition;
723
724 errorClass = 0/*Kopete::Account::InvalidHost*/;
725
726 /*
727 * Display error to user.
728 * FIXME: for unknown errors, maybe add error codes?
729 */
730 switch(streamError)
731 {
732 case XMPP::Stream::ErrParse:
733 errorClass = /*Kopete::Account::Unknown*/0;
734 errorText = i18n("Malformed packet received.");
735 break;
736
737 case XMPP::Stream::ErrProtocol:
738 errorClass = /*Kopete::Account::Unknown*/0;
739 errorText = i18n("There was an unrecoverable error in the protocol.");
740 break;
741
742 case XMPP::Stream::ErrStream:
743 switch(streamCondition)
744 {
745 case XMPP::Stream::GenericStreamError:
746 errorCondition = i18n("Generic stream error.");
747 break;
748 case XMPP::Stream::Conflict:
749 // FIXME: need a better error message here
750 errorCondition = i18n("There was a conflict in the information received.");
751 break;
752 case XMPP::Stream::ConnectionTimeout:
753 errorCondition = i18n("The stream timed out.");
754 break;
755 case XMPP::Stream::InternalServerError:
756 errorCondition = i18n("Internal server error.");
757 break;
758 case XMPP::Stream::InvalidFrom:
759 errorCondition = i18n("Stream packet received from an invalid address.");
760 break;
761 case XMPP::Stream::InvalidXml:
762 errorCondition = i18n("Malformed stream packet received.");
763 break;
764 case XMPP::Stream::PolicyViolation:
765 // FIXME: need a better error message here
766 errorCondition = i18n("Policy violation in the protocol stream.");
767 break;
768 case XMPP::Stream::ResourceConstraint:
769 // FIXME: need a better error message here
770 errorCondition = i18n("Resource constraint.");
771 break;
772 case XMPP::Stream::SystemShutdown:
773 // FIXME: need a better error message here
774 errorCondition = i18n("System shutdown.");
775 break;
776 default:
777 errorCondition = i18n("Unknown reason.");
778 break;
779 }
780
781 errorText = i18n("There was an error in the protocol stream: %1", errorCondition);
782 break;
783
784 case XMPP::ClientStream::ErrConnection:
785 switch(connectorCode)
786 {
787 case KNetwork::KSocketBase::LookupFailure:
788 errorClass = /*Kopete::Account::InvalidHost*/0;
789 errorCondition = i18n("Host not found.");
790 break;
791 case KNetwork::KSocketBase::AddressInUse:
792 errorCondition = i18n("Address is already in use.");
793 break;
794 case KNetwork::KSocketBase::AlreadyCreated:
795 errorCondition = i18n("Cannot recreate the socket.");
796 break;
797 case KNetwork::KSocketBase::AlreadyBound:
798 errorCondition = i18n("Cannot bind the socket again.");
799 break;
800 case KNetwork::KSocketBase::AlreadyConnected:
801 errorCondition = i18n("Socket is already connected.");
802 break;
803 case KNetwork::KSocketBase::NotConnected:
804 errorCondition = i18n("Socket is not connected.");
805 break;
806 case KNetwork::KSocketBase::NotBound:
807 errorCondition = i18n("Socket is not bound.");
808 break;
809 case KNetwork::KSocketBase::NotCreated:
810 errorCondition = i18n("Socket has not been created.");
811 break;
812 case KNetwork::KSocketBase::WouldBlock:
813 errorCondition = i18n("The socket operation would block. You should not see this error: please use \"Report Bug\" from the Help menu.");
814 break;
815 case KNetwork::KSocketBase::ConnectionRefused:
816 errorCondition = i18n("Connection refused.");
817 break;
818 case KNetwork::KSocketBase::ConnectionTimedOut:
819 errorCondition = i18n("Connection timed out.");
820 break;
821 case KNetwork::KSocketBase::InProgress:
822 errorCondition = i18n("Connection attempt already in progress.");
823 break;
824 case KNetwork::KSocketBase::NetFailure:
825 errorCondition = i18n("Network failure.");
826 break;
827 case KNetwork::KSocketBase::NotSupported:
828 errorCondition = i18n("Operation is not supported.");
829 break;
830 case KNetwork::KSocketBase::Timeout:
831 errorCondition = i18n("Socket timed out.");
832 break;
833 default:
834 errorClass = /*Kopete::Account::ConnectionReset*/0;
835 //errorCondition = i18n("Sorry, something unexpected happened that I do not know more about.");
836 break;
837 }
838 if(!errorCondition.isEmpty())
839 errorText = i18n("There was a connection error: %1", errorCondition);
840 break;
841
842 case XMPP::ClientStream::ErrNeg:
843 switch(streamCondition)
844 {
845 case XMPP::ClientStream::HostUnknown:
846 // FIXME: need a better error message here
847 errorCondition = i18n("Unknown host.");
848 break;
849 case XMPP::ClientStream::RemoteConnectionFailed:
850 // FIXME: need a better error message here
851 errorCondition = i18n("Could not connect to a required remote resource.");
852 break;
853 case XMPP::ClientStream::SeeOtherHost:
854 errorCondition = i18n("It appears we have been redirected to another server; I do not know how to handle this.");
855 break;
856 case XMPP::ClientStream::UnsupportedVersion:
857 errorCondition = i18n("Unsupported protocol version.");
858 break;
859 default:
860 errorCondition = i18n("Unknown error.");
861 break;
862 }
863
864 errorText = i18n("There was a negotiation error: %1", errorCondition);
865 break;
866
867 case XMPP::ClientStream::ErrTLS:
868 switch(streamCondition)
869 {
870 case XMPP::ClientStream::TLSStart:
871 errorCondition = i18n("Server rejected our request to start the TLS handshake.");
872 break;
873 case XMPP::ClientStream::TLSFail:
874 errorCondition = i18n("Failed to establish a secure connection.");
875 break;
876 default:
877 errorCondition = i18n("Unknown error.");
878 break;
879 }
880
881 errorText = i18n("There was a Transport Layer Security (TLS) error: %1", errorCondition);
882 break;
883
884 case XMPP::ClientStream::ErrAuth:
885 switch(streamCondition)
886 {
887 case XMPP::ClientStream::GenericAuthError:
888 errorCondition = i18n("Login failed with unknown reason.");
889 break;
890 case XMPP::ClientStream::NoMech:
891 errorCondition = i18n("No appropriate authentication mechanism available.");
892 break;
893 case XMPP::ClientStream::BadProto:
894 errorCondition = i18n("Bad SASL authentication protocol.");
895 break;
896 case XMPP::ClientStream::BadServ:
897 errorCondition = i18n("Server failed mutual authentication.");
898 break;
899 case XMPP::ClientStream::EncryptionRequired:
900 errorCondition = i18n("Encryption is required but not present.");
901 break;
902 case XMPP::ClientStream::InvalidAuthzid:
903 errorCondition = i18n("Invalid user ID.");
904 break;
905 case XMPP::ClientStream::InvalidMech:
906 errorCondition = i18n("Invalid mechanism.");
907 break;
908 case XMPP::ClientStream::InvalidRealm:
909 errorCondition = i18n("Invalid realm.");
910 break;
911 case XMPP::ClientStream::MechTooWeak:
912 errorCondition = i18n("Mechanism too weak.");
913 break;
914 case XMPP::ClientStream::NotAuthorized:
915 errorCondition = i18n("Wrong credentials supplied. (check your user ID and password)");
916 break;
917 case XMPP::ClientStream::TemporaryAuthFailure:
918 errorCondition = i18n("Temporary failure, please try again later.");
919 break;
920 default:
921 errorCondition = i18n("Unknown error.");
922 break;
923 }
924
925 errorText = i18n("There was an error authenticating with the server: %1", errorCondition);
926 break;
927
928 case XMPP::ClientStream::ErrSecurityLayer:
929 switch(streamCondition)
930 {
931 case XMPP::ClientStream::LayerTLS:
932 errorCondition = i18n("Transport Layer Security (TLS) problem.");
933 break;
934 case XMPP::ClientStream::LayerSASL:
935 errorCondition = i18n("Simple Authentication and Security Layer (SASL) problem.");
936 break;
937 default:
938 errorCondition = i18n("Unknown error.");
939 break;
940 }
941
942 errorText = i18n("There was an error in the security layer: %1", errorCondition);
943 break;
944
945 case XMPP::ClientStream::ErrBind:
946 switch(streamCondition)
947 {
948 case XMPP::ClientStream::BindNotAllowed:
949 errorCondition = i18n("No permission to bind the resource.");
950 break;
951 case XMPP::ClientStream::BindConflict:
952 errorCondition = i18n("The resource is already in use.");
953 break;
954 default:
955 errorCondition = i18n("Unknown error.");
956 break;
957 }
958
959 errorText = i18n("Could not bind a resource: %1", errorCondition);
960 break;
961
962 default:
963 errorText = i18n("Unknown error.");
964 break;
965 }
966
967 /*
968 * This mustn't be queued as otherwise the reconnection
969 * API will attempt to reconnect, queueing another
970 * error until memory is exhausted.
971 */
972 if(!errorText.isEmpty()) {
973 if (!additionalErrMsg.isEmpty()) {
974 KMessageBox::detailedError (0,
975 errorText,
976 additionalErrMsg,
977 i18n("Connection problem with Jabber server %1", server));
978 } else {
979 KMessageBox::error (0,
980 errorText,
981 i18n("Connection problem with Jabber server %1", server));
982 }
983 }
984
985 }
986
slotCSError(int error)987 void JabberAccount::slotCSError ( int error )
988 {
989 qCDebug(JABBER_PROTOCOL_LOG) << "Error in stream signalled.";
990
991 if ( ( error == XMPP::ClientStream::ErrAuth )
992 && ( client()->clientStream()->errorCondition () == XMPP::ClientStream::NotAuthorized ) )
993 {
994 qCDebug(JABBER_PROTOCOL_LOG) << "Incorrect password, retrying.";
995 disconnect(/*Kopete::Account::BadPassword*/0);
996 }
997 else
998 {
999 int errorClass = 0;
1000
1001 qCDebug(JABBER_PROTOCOL_LOG) << "Disconnecting.";
1002
1003 // display message to user
1004 if(!m_removing) //when removing the account, connection errors are normal.
1005 handleStreamError (error, client()->clientStream()->errorCondition (), client()->clientConnector()->errorCode (), server (), errorClass, client()->clientStream()->errorText());
1006
1007 disconnect ( errorClass );
1008
1009 /* slotCSDisconnected will not be called*/
1010 resourcePool()->clear();
1011 }
1012
1013 }
1014
1015 /* Set presence (usually called by dialog widget). */
setPresence(const XMPP::Status & status)1016 void JabberAccount::setPresence ( const XMPP::Status &status )
1017 {
1018 qCDebug(JABBER_PROTOCOL_LOG) << "Status: " << status.show () << ", Reason: " << status.status ();
1019
1020 // fetch input status
1021 XMPP::Status newStatus = status;
1022
1023 // TODO: Check if Caps is enabled
1024 // Send entity capabilities
1025 if( client() )
1026 {
1027 newStatus.setCapsNode( client()->capsNode() );
1028 newStatus.setCapsVersion( client()->capsVersion() );
1029 newStatus.setCapsExt( client()->capsExt() );
1030 }
1031
1032 // make sure the status gets the correct priority
1033 newStatus.setPriority ( 5 );
1034
1035 XMPP::Jid jid ( this->contactId() );
1036 XMPP::Resource newResource ( resource (), newStatus );
1037
1038 // update our resource in the resource pool
1039 resourcePool()->addResource ( jid, newResource );
1040
1041 // make sure that we only consider our own resource locally
1042 resourcePool()->lockToResource ( jid, newResource );
1043
1044 /*
1045 * Unless we are in the connecting status, send a presence packet to the server
1046 */
1047 if(status.show () != QString("connecting") )
1048 {
1049 /*
1050 * Make sure we are actually connected before sending out a packet.
1051 */
1052 if (isConnected())
1053 {
1054 qCDebug(JABBER_PROTOCOL_LOG) << "Sending new presence to the server.";
1055
1056 XMPP::JT_Presence * task = new XMPP::JT_Presence ( client()->rootTask ());
1057
1058 task->pres ( newStatus );
1059 task->go ( true );
1060 }
1061 else
1062 {
1063 qCDebug(JABBER_PROTOCOL_LOG) << "We were not connected, presence update aborted.";
1064 }
1065 }
1066
1067 }
1068
slotXMPPConsole()1069 void JabberAccount::slotXMPPConsole ()
1070 {
1071 /* Check if we're connected. */
1072 if ( !isConnected () )
1073 {
1074 errorConnectFirst ();
1075 return;
1076 }
1077
1078 /* dlgXMPPConsole *w = new dlgXMPPConsole( client (), 0);
1079 QObject::connect( m_jabberClient, SIGNAL (incomingXML(QString)),
1080 w, SLOT (slotIncomingXML(QString)) );
1081 QObject::connect( m_jabberClient, SIGNAL (outgoingXML(QString)),
1082 w, SLOT (slotOutgoingXML(QString)) );
1083 w->show();*/
1084 }
1085
slotSubscription(const XMPP::Jid & jid,const QString & type)1086 void JabberAccount::slotSubscription (const XMPP::Jid & jid, const QString & type)
1087 {
1088 qCDebug(JABBER_PROTOCOL_LOG) << jid.full () << ", " << type;
1089
1090 if (type == "subscribe")
1091 {
1092 /*
1093 * A user wants to subscribe to our presence.
1094 */
1095 qCDebug(JABBER_PROTOCOL_LOG) << jid.full () << " is asking for authorization to subscribe.";
1096
1097 // Is the user already in our contact list?
1098 // JabberBaseContact *contact = contactPool()->findExactMatch( jid );
1099 // Kopete::MetaContact *metaContact=0L;
1100 // if(contact)
1101 // metaContact=contact->metaContact();
1102
1103 // Kopete::AddedInfoEvent::ShowActionOptions actions = Kopete::AddedInfoEvent::AuthorizeAction;
1104 // actions |= Kopete::AddedInfoEvent::BlockAction;
1105 //
1106 // if( !metaContact || metaContact->isTemporary() )
1107 // actions |= Kopete::AddedInfoEvent::AddAction;
1108
1109 /* Kopete::AddedInfoEvent* event = new Kopete::AddedInfoEvent( jid.full(), this );
1110 QObject::connect( event, SIGNAL(actionActivated(uint)),
1111 this, SLOT(slotAddedInfoEventActionActivated(uint)) );
1112
1113 event->showActions( actions );
1114 event->sendEvent();*/
1115 }
1116 else if (type == "unsubscribed")
1117 {
1118 /*
1119 * Someone else removed our authorization to see them.
1120 */
1121 qCDebug(JABBER_PROTOCOL_LOG) << jid.full() << " revoked our presence authorization";
1122
1123 XMPP::JT_Roster *task;
1124
1125 switch (KMessageBox::warningYesNo (0,
1126 i18n
1127 ("The Jabber user %1 removed %2's subscription to him/her. "
1128 "This account will no longer be able to view his/her online/offline status. "
1129 "Do you want to delete the contact?",
1130 jid.full(), accountId()), i18n ("Notification"), KStandardGuiItem::del(), KGuiItem( i18n("Keep") )))
1131 {
1132
1133 case KMessageBox::Yes:
1134 /*
1135 * Delete this contact from our roster.
1136 */
1137 task = new XMPP::JT_Roster ( client()->rootTask ());
1138
1139 task->remove (jid);
1140 task->go (true);
1141
1142 break;
1143
1144 default:
1145 /*
1146 * We want to leave the contact in our contact list.
1147 * In this case, we need to delete all the resources
1148 * we have for it, as the Jabber server won't signal us
1149 * that the contact is offline now.
1150 */
1151 resourcePool()->removeAllResources ( jid );
1152 break;
1153
1154 }
1155 }
1156 }
1157
slotAddedInfoEventActionActivated(uint actionId)1158 void JabberAccount::slotAddedInfoEventActionActivated ( uint actionId )
1159 {
1160 /* const Kopete::AddedInfoEvent *event =
1161 dynamic_cast<const Kopete::AddedInfoEvent *>(sender());
1162
1163 if ( !event || !isConnected() )
1164 return;
1165
1166 XMPP::Jid jid(event->contactId());
1167 if ( actionId == Kopete::AddedInfoEvent::AuthorizeAction )
1168 {*/
1169 /*
1170 * Authorize user.
1171 */
1172 /* XMPP::JT_Presence *task = new XMPP::JT_Presence ( client()->rootTask () );
1173 task->sub ( jid, "subscribed" );
1174 task->go ( true );
1175 }
1176 else if ( actionId == Kopete::AddedInfoEvent::BlockAction )
1177 {*/
1178 /*
1179 * Reject subscription.
1180 */
1181 /* XMPP::JT_Presence *task = new XMPP::JT_Presence ( client()->rootTask () );
1182 task->sub ( jid, "unsubscribed" );
1183 task->go ( true );
1184 }
1185 else if( actionId == Kopete::AddedInfoEvent::AddContactAction )
1186 {
1187 Kopete::MetaContact *parentContact=event->addContact();
1188 if(parentContact)
1189 {
1190 QStringList groupNames;
1191 Kopete::GroupList groupList = parentContact->groups();
1192 foreach(Kopete::Group *group,groupList)
1193 groupNames += group->displayName();
1194
1195 XMPP::RosterItem item;
1196
1197 item.setJid ( jid );
1198 item.setName ( parentContact->displayName() );
1199 item.setGroups ( groupNames );
1200
1201 // add the new contact to our roster.
1202 XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( client()->rootTask () );
1203
1204 rosterTask->set ( item.jid(), item.name(), item.groups() );
1205 rosterTask->go ( true );
1206
1207 // send a subscription request.
1208 XMPP::JT_Presence *presenceTask = new XMPP::JT_Presence ( client()->rootTask () );
1209
1210 presenceTask->sub ( jid, "subscribe" );
1211 presenceTask->go ( true );
1212 }
1213 }*/
1214 }
1215
1216
1217
slotContactUpdated(const XMPP::RosterItem & item)1218 void JabberAccount::slotContactUpdated (const XMPP::RosterItem & item)
1219 {
1220
1221 /**
1222 * Subscription types are: Both, From, To, Remove, None.
1223 * Both: Both sides have authed each other, each side
1224 * can see each other's presence
1225 * From: The other side can see us.
1226 * To: We can see the other side. (implies we are
1227 * authed)
1228 * Remove: Other side revoked our subscription request.
1229 * Not to be handled here.
1230 * None: No subscription.
1231 *
1232 * Regardless of the subscription type, we have to add
1233 * a roster item here.
1234 */
1235
1236 qCDebug(JABBER_PROTOCOL_LOG) << "New roster item " << item.jid().full () << " (Subscription: " << item.subscription().toString () << ")";
1237
1238 /*
1239 * See if the contact need to be added, according to the criterias of
1240 * JEP-0162: Best Practices for Roster and Subscription Management
1241 * http://www.jabber.org/jeps/jep-0162.html#contacts
1242 */
1243 bool need_to_add=false;
1244 if(item.subscription().type() == XMPP::Subscription::Both || item.subscription().type() == XMPP::Subscription::To)
1245 need_to_add = true;
1246 else if( !item.ask().isEmpty() )
1247 need_to_add = true;
1248 else if( !item.name().isEmpty() || !item.groups().isEmpty() )
1249 need_to_add = true;
1250
1251 /*
1252 * See if the contact is already on our contact list
1253 */
1254 JabberBaseContact *c= contactPool()->findExactMatch( item.jid() );
1255
1256 // if( c && c == c->Kopete::Contact::account()->myself() ) //don't use JabberBaseContact::account() which return alwaus the JabberAccount, and not the transport
1257 // {
1258 // // don't let remove the gateway contact, eh!
1259 // need_to_add = true;
1260 // }
1261
1262 /* if(need_to_add)
1263 {
1264 Kopete::MetaContact *metaContact=0L;
1265 if (!c)
1266 {*/
1267 /*
1268 * No metacontact has been found which contains a contact with this ID,
1269 * so add a new metacontact to the list.
1270 */
1271 // metaContact = new Kopete::MetaContact ();
1272 // QStringList groups = item.groups ();
1273 //
1274 // // add this metacontact to all groups the contact is a member of
1275 // for (QStringList::Iterator it = groups.begin (); it != groups.end (); ++it)
1276 // metaContact->addToGroup (Kopete::ContactList::self ()->findGroup (*it));
1277 //
1278 // // put it onto contact list
1279 // Kopete::ContactList::self ()->addMetaContact ( metaContact );
1280 // }
1281 // else
1282 // {
1283 // metaContact=c->metaContact();
1284 // //TODO: synchronize groups
1285 // }
1286
1287 /*
1288 * Add / update the contact in our pool. In case the contact is already there,
1289 * it will be updated. In case the contact is not in the meta contact yet, it
1290 * will be added to it.
1291 * The "dirty" flag is false here, because we just received the contact from
1292 * the server's roster. As such, it is now a synchronized entry.
1293 */
1294 JabberBaseContact *contact = contactPool()->addContact ( item, false );
1295
1296 /*
1297 * Set authorization property
1298 */
1299 if ( !item.ask().isEmpty () )
1300 {
1301 // contact->setProperty ( protocol()->propAuthorizationStatus, i18n ( "Waiting for authorization" ) );
1302 }
1303 else
1304 {
1305 // contact->removeProperty ( protocol()->propAuthorizationStatus );
1306 }
1307 // }
1308 /*else*/ if(c) //we don't need to add it, and it is in the contact list
1309 {
1310 /* Kopete::MetaContact *metaContact=c->metaContact();
1311 if(metaContact->isTemporary())
1312 return;
1313 qCDebug(JABBER_PROTOCOL_LOG) << c->contactId() <<
1314 " is on the contact list while it shouldn't. we are removing it. - " << c ;
1315 delete c;
1316 if(metaContact->contacts().isEmpty())
1317 Kopete::ContactList::self()->removeMetaContact( metaContact );*/
1318 }
1319
1320 }
1321
slotContactDeleted(const XMPP::RosterItem & item)1322 void JabberAccount::slotContactDeleted (const XMPP::RosterItem & item)
1323 {
1324 qCDebug(JABBER_PROTOCOL_LOG) << "Deleting contact " << item.jid().full ();
1325
1326 // since the contact instance will get deleted here, the GUI should be updated
1327 contactPool()->removeContact ( item.jid () );
1328
1329 }
1330
slotReceivedMessage(const XMPP::Message & message)1331 void JabberAccount::slotReceivedMessage (const XMPP::Message & message)
1332 {
1333 qCDebug(JABBER_PROTOCOL_LOG) << "New message from " << message.from().full ();
1334
1335 JabberBaseContact *contactFrom;
1336
1337 if ( message.type() == "groupchat" )
1338 {
1339 // this is a groupchat message, forward it to the group contact
1340 // (the one without resource name)
1341 XMPP::Jid jid ( message.from().userHost () );
1342
1343 // try to locate an exact match in our pool first
1344 contactFrom = contactPool()->findExactMatch ( jid );
1345
1346 /**
1347 * If there was no exact match, something is really messed up.
1348 * We can't receive groupchat messages from rooms that we are
1349 * not a member of and if the room contact vanished somehow,
1350 * we're in deep trouble.
1351 */
1352 if ( !contactFrom )
1353 {
1354 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: Received a groupchat message but couldn't find room contact. Ignoring message.";
1355 return;
1356 }
1357 }
1358 else
1359 {
1360 // try to locate an exact match in our pool first
1361 contactFrom = contactPool()->findExactMatch ( message.from () );
1362
1363 if ( !contactFrom )
1364 {
1365 // we have no exact match, try a broader search
1366 contactFrom = contactPool()->findRelevantRecipient ( message.from () );
1367 }
1368
1369 // see if we found the contact in our pool
1370 if ( !contactFrom )
1371 {
1372 // eliminate the resource from this contact,
1373 // otherwise we will add the contact with the
1374 // resource to our list
1375 // NOTE: This is a stupid way to do it, but
1376 // message.from().setResource("") had no
1377 // effect. Iris bug?
1378 XMPP::Jid jid ( message.from().userHost () );
1379
1380 // the contact is not in our pool, add it as a temporary contact
1381 qCDebug(JABBER_PROTOCOL_LOG) << jid.full () << " is unknown to us, creating temporary contact.";
1382
1383 // Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
1384 //
1385 // metaContact->setTemporary (true);
1386
1387 contactFrom = contactPool()->addContact ( XMPP::RosterItem ( jid ), false );
1388
1389 // Kopete::ContactList::self ()->addMetaContact (metaContact);
1390 }
1391 }
1392
1393 // pass the message on to the contact
1394 contactFrom->handleIncomingMessage (message);
1395
1396 }
1397
slotJoinNewChat()1398 void JabberAccount::slotJoinNewChat ()
1399 {
1400
1401 if (!isConnected ())
1402 {
1403 errorConnectFirst ();
1404 return;
1405 }
1406
1407 // dlgJabberChatJoin *joinDialog = new dlgJabberChatJoin ( this, 0 );
1408 // joinDialog->show ();
1409
1410 }
1411
slotGroupChatJoined(const XMPP::Jid & jid)1412 void JabberAccount::slotGroupChatJoined (const XMPP::Jid & jid)
1413 {
1414 qCDebug(JABBER_PROTOCOL_LOG) << "Joined groupchat " << jid.full ();
1415
1416 // Create new meta contact that holds the groupchat contact.
1417 // Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
1418
1419 // metaContact->setTemporary ( true );
1420
1421 // Create a groupchat contact for this room
1422 JabberGroupContact *groupContact = dynamic_cast<JabberGroupContact *>( contactPool()->addGroupContact ( XMPP::RosterItem ( jid ), true, false ) );
1423
1424 if(groupContact)
1425 {
1426 // Add the groupchat contact to the meta contact.
1427 //metaContact->addContact ( groupContact );
1428
1429 // Kopete::ContactList::self ()->addMetaContact ( metaContact );
1430 }
1431 // else
1432 // delete metaContact;
1433
1434 /**
1435 * Add an initial resource for this contact to the pool. We need
1436 * to do this to be able to lock the group status to our own presence.
1437 * Our own presence will be updated right after this method returned
1438 * by slotGroupChatPresence(), since the server will signal our own
1439 * presence back to us.
1440 */
1441 resourcePool()->addResource ( XMPP::Jid ( jid.userHost () ), XMPP::Resource ( jid.resource () ) );
1442
1443 // lock the room to our own status
1444 resourcePool()->lockToResource ( XMPP::Jid ( jid.userHost () ), jid.resource () );
1445
1446 // m_bookmarks->insertGroupChat(jid);
1447 }
1448
slotGroupChatLeft(const XMPP::Jid & jid)1449 void JabberAccount::slotGroupChatLeft (const XMPP::Jid & jid)
1450 {
1451 qCDebug(JABBER_PROTOCOL_LOG) << "Left groupchat " << jid.full ();
1452
1453 // remove group contact from list
1454 // Kopete::Contact *contact =
1455 // Kopete::ContactList::self()->findContact( protocol()->pluginId() , accountId() , jid.userHost() );
1456 //
1457 // if ( contact )
1458 // {
1459 // Kopete::MetaContact *metaContact= contact->metaContact();
1460 // if( metaContact && metaContact->isTemporary() )
1461 // Kopete::ContactList::self()->removeMetaContact ( metaContact );
1462 // else
1463 // contact->deleteLater();
1464 // }
1465
1466 // now remove it from our pool, which should clean up all subcontacts as well
1467 contactPool()->removeContact ( XMPP::Jid ( jid.userHost () ) );
1468
1469 }
1470
slotGroupChatPresence(const XMPP::Jid & jid,const XMPP::Status & status)1471 void JabberAccount::slotGroupChatPresence (const XMPP::Jid & jid, const XMPP::Status & status)
1472 {
1473 qCDebug(JABBER_PROTOCOL_LOG) << "Received groupchat presence for room " << jid.full ();
1474
1475 // fetch room contact (the one without resource)
1476 JabberGroupContact *groupContact = dynamic_cast<JabberGroupContact *>( contactPool()->findExactMatch ( XMPP::Jid ( jid.userHost () ) ) );
1477
1478 if ( !groupContact )
1479 {
1480 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: Groupchat presence signalled, but we don't have a room contact?";
1481 return;
1482 }
1483
1484 if ( !status.isAvailable () )
1485 {
1486 qCDebug(JABBER_PROTOCOL_LOG) << jid.full () << " has become unavailable, removing from room";
1487
1488 // remove the resource from the pool
1489 resourcePool()->removeResource ( jid, XMPP::Resource ( jid.resource (), status ) );
1490
1491 // the person has become unavailable, remove it
1492 groupContact->removeSubContact ( XMPP::RosterItem ( jid ) );
1493 }
1494 else
1495 {
1496 // add a resource for this contact to the pool (existing resources will be updated)
1497 resourcePool()->addResource ( jid, XMPP::Resource ( jid.resource (), status ) );
1498
1499 // make sure the contact exists in the room (if it exists already, it won't be added twice)
1500 groupContact->addSubContact ( XMPP::RosterItem ( jid ) );
1501 }
1502
1503 }
1504
slotGroupChatError(const XMPP::Jid & jid,int error,const QString & reason)1505 void JabberAccount::slotGroupChatError (const XMPP::Jid &jid, int error, const QString &reason)
1506 {
1507 qCDebug(JABBER_PROTOCOL_LOG) << "Group chat error - room " << jid.full () << " had error " << error << " (" << reason << ")";
1508
1509 switch (error)
1510 {
1511 case JabberClient::InvalidPasswordForMUC:
1512 {
1513 KPasswordDialog dlg(0);
1514 dlg.setPrompt(i18n("A password is required to join the room %1.", jid.node()));
1515 if (dlg.exec() == KPasswordDialog::Accepted)
1516 m_jabberClient->joinGroupChat(jid.domain(), jid.node(), jid.resource(), dlg.password());
1517 }
1518 break;
1519
1520 case JabberClient::NicknameConflict:
1521 {
1522 bool ok;
1523 QString nickname = KInputDialog::getText(i18n("Error trying to join %1: nickname %2 is already in use", jid.node(), jid.resource()),
1524 i18n("Provide your nickname"),
1525 QString(),
1526 &ok);
1527 if (ok)
1528 {
1529 m_jabberClient->joinGroupChat(jid.domain(), jid.node(), nickname);
1530 }
1531 }
1532 break;
1533
1534 case JabberClient::BannedFromThisMUC:
1535 KMessageBox::queuedMessageBox ( 0,
1536 KMessageBox::Error,
1537 i18n ("You cannot join the room %1 because you have been banned", jid.node()),
1538 i18n ("Jabber Group Chat") );
1539 break;
1540
1541 case JabberClient::MaxUsersReachedForThisMuc:
1542 KMessageBox::queuedMessageBox ( 0,
1543 KMessageBox::Error,
1544 i18n ("You cannot join the room %1 because the maximum number of users has been reached", jid.node()),
1545 i18n ("Jabber Group Chat") );
1546 break;
1547
1548 default:
1549 {
1550 QString detailedReason = reason.isEmpty () ? i18n ( "No reason given by the server" ) : reason;
1551
1552 KMessageBox::queuedMessageBox ( 0,
1553 KMessageBox::Error,
1554 i18n ("There was an error processing your request for groupchat %1. (Reason: %2, Code %3)", jid.full (), detailedReason, error ),
1555 i18n ("Jabber Group Chat") );
1556 }
1557 }
1558 }
1559
slotResourceAvailable(const XMPP::Jid & jid,const XMPP::Resource & resource)1560 void JabberAccount::slotResourceAvailable (const XMPP::Jid & jid, const XMPP::Resource & resource)
1561 {
1562
1563 qCDebug(JABBER_PROTOCOL_LOG) << "New resource available for " << jid.full();
1564
1565 resourcePool()->addResource ( jid, resource );
1566
1567 }
1568
slotResourceUnavailable(const XMPP::Jid & jid,const XMPP::Resource & resource)1569 void JabberAccount::slotResourceUnavailable (const XMPP::Jid & jid, const XMPP::Resource & resource)
1570 {
1571
1572 qCDebug(JABBER_PROTOCOL_LOG) << "Resource now unavailable for " << jid.full ();
1573
1574 resourcePool()->removeResource ( jid, resource );
1575
1576 }
1577
slotEditVCard()1578 void JabberAccount::slotEditVCard ()
1579 {
1580 // static_cast<JabberContact *>( myself() )->slotUserInfo ();
1581 }
1582
resource() const1583 const QString JabberAccount::resource () const
1584 {
1585
1586 return "KsirK";
1587
1588 }
1589
server() const1590 const QString JabberAccount::server () const
1591 {
1592
1593 return "localhost";
1594
1595 }
1596
port() const1597 const int JabberAccount::port () const
1598 {
1599
1600 return 5222;
1601
1602 }
1603
slotGetServices()1604 void JabberAccount::slotGetServices ()
1605 {
1606 /* dlgJabberServices *dialog = new dlgJabberServices (this);
1607
1608 dialog->show ();
1609 dialog->raise ();*/
1610 }
1611
1612 // void JabberAccount::slotIncomingVoiceCall( const Jid &jid )
1613 // {
1614 // qCDebug(JABBER_PROTOCOL_LOG) ;
1615 // #ifdef SUPPORT_JINGLE
1616 // if(voiceCaller())
1617 // {
1618 // qCDebug(JABBER_PROTOCOL_LOG) << "Showing voice dialog.";
1619 // JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( jid, voiceCaller() );
1620 // voiceDialog->show();
1621 // }
1622 // #else
1623 // Q_UNUSED(jid);
1624 // #endif
1625 // }
1626
1627 // void JabberAccount::slotIncomingJingleSession( const QString &sessionType, JingleSession *session )
1628 // {
1629 // #ifdef SUPPORT_JINGLE
1630 // if(sessionType == "http://www.google.com/session/phone")
1631 // {
1632 // QString from = ((XMPP::Jid)session->peers().first()).full();
1633 // //KMessageBox::queuedMessageBox( 0, KMessageBox::Information, QString("Received a voice session invitation from %1.").arg(from) );
1634 // JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( static_cast<JingleVoiceSession*>(session) );
1635 // voiceDialog->show();
1636 // }
1637 // #else
1638 // Q_UNUSED( sessionType );
1639 // Q_UNUSED( session );
1640 // #endif
1641 // }
1642
1643
1644 // void JabberAccount::addTransport( JabberTransport * tr, const QString &jid )
1645 // {
1646 // m_transports.insert(jid,tr);
1647 // }
1648 //
1649 // void JabberAccount::removeTransport( const QString &jid )
1650 // {
1651 // m_transports.remove(jid);
1652 // }
1653
1654 // bool JabberAccount::removeAccount( )
1655 // {
1656 // if(!m_removing)
1657 // {
1658 // int result=KMessageBox::warningYesNoCancel( 0 ,
1659 // i18n( "Do you want to also unregister \"%1\" from the Jabber server ?\n"
1660 // "If you unregister, your whole contact list may be removed from the server,"
1661 // " and you will never be able to connect to this account with any client", accountLabel() ),
1662 // i18n("Unregister"),
1663 // KGuiItem(i18n( "Remove and Unregister" ), "edit-delete"),
1664 // KGuiItem(i18n( "Remove only from Kopete"), "user-trash"),KStandardGuiItem::cancel(),
1665 // QString(), KMessageBox::Notify | KMessageBox::Dangerous );
1666 // if(result == KMessageBox::Cancel)
1667 // {
1668 // return false;
1669 // }
1670 // else if(result == KMessageBox::Yes)
1671 // {
1672 // if (!isConnected())
1673 // {
1674 // errorConnectFirst ();
1675 // return false;
1676 // }
1677 //
1678 // XMPP::JT_Register *task = new XMPP::JT_Register ( client()->rootTask () );
1679 // QObject::connect ( task, SIGNAL (finished()), this, SLOT (slotUnregisterFinished) );
1680 // task->unreg ();
1681 // task->go ( true );
1682 // m_removing=true;
1683 // // from my experiment, not all server reply us with a response. it simply dosconnect
1684 // // so after one seconde, we will force to remove the account
1685 // QTimer::singleShot(1111, this, SLOT(slotUnregisterFinished()));
1686 //
1687 // return false; //the account will be removed when the task will be finished
1688 // }
1689 // }
1690 //
1691 // //remove transports from config file.
1692 // /* QMap<QString,JabberTransport*> tranposrts_copy=m_transports;
1693 // QMap<QString,JabberTransport*>::Iterator it;
1694 // for ( it = tranposrts_copy.begin(); it != tranposrts_copy.end(); ++it )
1695 // {
1696 // (*it)->jabberAccountRemoved();
1697 // }*/
1698 // return true;
1699 // }
1700
slotUnregisterFinished()1701 void JabberAccount::slotUnregisterFinished( )
1702 {
1703 const XMPP::JT_Register * task = dynamic_cast<const XMPP::JT_Register *>(sender ());
1704
1705 if ( task && ! task->success ())
1706 {
1707 KMessageBox::queuedMessageBox ( 0L, KMessageBox::Error,
1708 i18n ("An error occurred while trying to remove the account:\n%1", task->statusString()),
1709 i18n ("Jabber Account Unregistration"));
1710 m_removing=false;
1711 return;
1712 }
1713 // if(m_removing) //it may be because this is now the timer.
1714 // Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
1715 }
1716
1717
1718
1719 // vim: set noet ts=4 sts=4 sw=4:
1720