File indexing completed on 2024-04-14 04:01:46
0001 /* 0002 * jabbercontact.cpp - Regular Kopete Jabber protocol contact 0003 * 0004 * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net> 0005 * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org> 0006 * 0007 * Kopete (c) by the Kopete developers <kopete-devel@kde.org> 0008 * 0009 * ************************************************************************* 0010 * * * 0011 * * This program is free software; you can redistribute it and/or modify * 0012 * * it under the terms of the GNU General Public License as published by * 0013 * * the Free Software Foundation; either either version 2 0014 of the License, or (at your option) any later version.of the License, or * 0015 * * (at your option) any later version. * 0016 * * * 0017 * ************************************************************************* 0018 */ 0019 #include <config-kopete.h> 0020 0021 #include "jabbercontact.h" 0022 #include "jabber_protocol_debug.h" 0023 0024 #include "xmpp_tasks.h" 0025 #include "im.h" 0026 #include "tasks/jt_getlastactivity.h" 0027 0028 #include <QTimer> 0029 #include <QDateTime> 0030 #include <QTextDocument> 0031 #include <QImage> 0032 #include <QRegExp> 0033 #include <QBuffer> 0034 0035 #include <KLocalizedString> 0036 #include <KMessageBox> 0037 #include <KFileDialog> 0038 #include <QAction> 0039 #include <KActionMenu> 0040 #include <QIcon> 0041 #include <KStandardDirs> 0042 #include <kio/netaccess.h> 0043 #include <KInputDialog> 0044 #include <kopeteview.h> 0045 0046 #include "kopetecontactlist.h" 0047 #include "kopetegroup.h" 0048 #include "kopeteuiglobal.h" 0049 #include "kopetechatsessionmanager.h" 0050 #include "kopeteaccountmanager.h" 0051 #include "kopetemetacontact.h" 0052 #include "jabberprotocol.h" 0053 #include "jabberaccount.h" 0054 #include "jabberclient.h" 0055 #include "jabberchatsession.h" 0056 #include "jabberresource.h" 0057 #include "jabberresourcepool.h" 0058 #include "jabberfiletransfer.h" 0059 #include "jabbertransport.h" 0060 #include "dlgjabbervcard.h" 0061 0062 #ifdef SUPPORT_JINGLE 0063 // #include "jinglesessionmanager.h" 0064 // #include "jinglevoicesession.h" 0065 #include "jinglevoicesessiondialog.h" 0066 #endif 0067 0068 /** 0069 * JabberContact constructor 0070 */ 0071 JabberContact::JabberContact (const XMPP::RosterItem &rosterItem, Kopete::Account *_account, Kopete::MetaContact * mc, const QString &legacyId) 0072 : JabberBaseContact ( rosterItem, _account, mc, legacyId) , mDiscoDone(false), m_syncTimer(0L) 0073 { 0074 qCDebug(JABBER_PROTOCOL_LOG) << contactId() << " is created - " << this; 0075 // this contact is able to transfer files 0076 setFileCapable ( true ); 0077 0078 /* 0079 * Catch when we're going online for the first time to 0080 * update our properties from a vCard. (properties are 0081 * not available during startup, so we need to read 0082 * them later - this also serves as a random update 0083 * feature) 0084 * Note: The only time account->myself() could be a 0085 * NULL pointer is if this contact here is the myself() 0086 * instance itself. Since in that case we wouldn't 0087 * get updates at all, we need to treat that as a 0088 * special case. 0089 */ 0090 0091 mVCardUpdateInProgress = false; 0092 0093 if ( !account()->myself () ) 0094 { 0095 // this contact is a regular contact 0096 connect ( this, 0097 SIGNAL (onlineStatusChanged(Kopete::Contact*,Kopete::OnlineStatus,Kopete::OnlineStatus)), 0098 this, SLOT (slotCheckVCard()) ); 0099 } 0100 else 0101 { 0102 // this contact is the myself instance 0103 connect ( account()->myself (), 0104 SIGNAL (onlineStatusChanged(Kopete::Contact*,Kopete::OnlineStatus,Kopete::OnlineStatus)), 0105 this, SLOT (slotCheckVCard()) ); 0106 0107 connect ( account()->myself (), 0108 SIGNAL (onlineStatusChanged(Kopete::Contact*,Kopete::OnlineStatus,Kopete::OnlineStatus)), 0109 this, SLOT (slotCheckLastActivity(Kopete::Contact*,Kopete::OnlineStatus,Kopete::OnlineStatus)) ); 0110 0111 /* 0112 * Trigger update once if we're already connected for contacts 0113 * that are being added while we are online. 0114 */ 0115 if ( account()->myself()->onlineStatus().isDefinitelyOnline() ) 0116 { 0117 slotGetTimedVCard (); 0118 } 0119 } 0120 0121 mRequestOfflineEvent = false; 0122 mRequestDisplayedEvent = false; 0123 mRequestDeliveredEvent = false; 0124 mRequestComposingEvent = false; 0125 } 0126 0127 JabberContact::~JabberContact() 0128 { 0129 qCDebug(JABBER_PROTOCOL_LOG) << contactId() << " is destroyed - " << this; 0130 } 0131 0132 QList<QAction *> *JabberContact::customContextMenuActions () 0133 { 0134 0135 QList<QAction *> *actionCollection = new QList<QAction*>(); 0136 0137 KActionMenu *actionAuthorization = new KActionMenu ( QIcon::fromTheme( QLatin1String( "network-connect"), i18n ("Authorization" )), this); 0138 0139 QAction *resendAuthAction, *requestAuthAction, *removeAuthAction; 0140 0141 resendAuthAction = new QAction( this ); 0142 resendAuthAction->setIcon( (QIcon::fromTheme( QLatin1String( "mail-forward" )) ) ); 0143 resendAuthAction->setText( i18n ("(Re)send Authorization To") ); 0144 resendAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::To || mRosterItem.subscription().type() == XMPP::Subscription::None ); 0145 connect(resendAuthAction, SIGNAL(triggered(bool)), SLOT(slotSendAuth())); 0146 actionAuthorization->addAction(resendAuthAction); 0147 0148 requestAuthAction = new QAction( this ); 0149 requestAuthAction->setIcon( (QIcon::fromTheme( QLatin1String( "mail-reply-sender" )) ) ); 0150 requestAuthAction->setText( i18n ("(Re)request Authorization From") ); 0151 requestAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::From || mRosterItem.subscription().type() == XMPP::Subscription::None ); 0152 connect(requestAuthAction, SIGNAL(triggered(bool)), SLOT(slotRequestAuth())); 0153 actionAuthorization->addAction(requestAuthAction); 0154 0155 removeAuthAction = new QAction( this ); 0156 removeAuthAction->setIcon( (QIcon::fromTheme( QLatin1String( "edit-delete" )) ) ); 0157 removeAuthAction->setText( i18n ("Remove Authorization From") ); 0158 removeAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From ); 0159 connect(removeAuthAction, SIGNAL(triggered(bool)), SLOT(slotRemoveAuth())); 0160 actionAuthorization->addAction(removeAuthAction); 0161 0162 KActionMenu *actionSetAvailability = new KActionMenu ( QIcon::fromTheme( QLatin1String( "user-identity", 0, QStringList() << QString() << "user-online"), i18n ("Set Availability" )), this ); 0163 0164 #define KACTION(status, text, name, slot) \ 0165 { QAction *tmp = new QAction(this); \ 0166 tmp->setIcon( QIcon::fromTheme(QIcon((status).iconFor(this)))); \ 0167 tmp->setText( text ); \ 0168 connect(tmp, SIGNAL(triggered(bool)), (slot));\ 0169 actionSetAvailability->addAction(tmp); } 0170 0171 KACTION( protocol()->JabberKOSOnline, i18n ("Online"), "actionOnline", SLOT(slotStatusOnline()) ); 0172 KACTION( protocol()->JabberKOSChatty, i18n ("Free to Chat"), "actionChatty", SLOT(slotStatusChatty()) ); 0173 KACTION( protocol()->JabberKOSAway, i18n ("Away"), "actionAway", SLOT(slotStatusAway()) ); 0174 KACTION( protocol()->JabberKOSXA, i18n ("Extended Away"), "actionXA", SLOT(slotStatusXA()) ); 0175 KACTION( protocol()->JabberKOSDND, i18n ("Do Not Disturb"), "actionDND", SLOT(slotStatusDND()) ); 0176 KACTION( protocol()->JabberKOSInvisible, i18n ("Invisible"), "actionInvisible", SLOT(slotStatusInvisible()) ); 0177 0178 #undef KACTION 0179 0180 KActionMenu *actionSelectResource = new KActionMenu ( i18n ("Select Resource"), this ); 0181 0182 // if the contact is online, display the resources we have for it, 0183 // otherwise disable the menu 0184 if (onlineStatus ().status () == Kopete::OnlineStatus::Offline) 0185 { 0186 actionSelectResource->setEnabled ( false ); 0187 } 0188 else 0189 { 0190 QStringList items; 0191 XMPP::ResourceList availableResources; 0192 0193 int activeItem = 0, i = 1; 0194 const XMPP::Resource lockedResource = account()->resourcePool()->lockedResource ( mRosterItem.jid () ); 0195 0196 // put default resource first 0197 items.append (i18n ("Automatic (best/default resource)")); 0198 0199 account()->resourcePool()->findResources ( mRosterItem.jid (), availableResources ); 0200 0201 foreach(const XMPP::Resource& res, availableResources) 0202 { 0203 items.append ( res.name() ); 0204 0205 if ( res.name() == lockedResource.name() ) 0206 activeItem = i; 0207 0208 ++i; 0209 } 0210 0211 // now go through the string list and add the resources with their icons 0212 i = 0; 0213 foreach(const QString& str, items) 0214 { 0215 if( i == activeItem ) 0216 { 0217 QAction *tmp = new QAction( this ); 0218 tmp->setIcon( QIcon::fromTheme( QLatin1String( "dialog-ok" )) ); 0219 tmp->setText( str); 0220 connect(tmp, SIGNAL(triggered(bool)), SLOT(slotSelectResource())); 0221 actionSelectResource->addAction(tmp); 0222 } 0223 else 0224 { 0225 /* 0226 * Select icon, using bestResource() without lock for the automatic entry 0227 * and the resources' respective status icons for the rest. 0228 */ 0229 QIcon iconSet ( !i ? 0230 protocol()->resourceToKOS ( account()->resourcePool()->bestResource ( mRosterItem.jid(), false ) ).iconFor ( account () ) : protocol()->resourceToKOS ( *availableResources.find(str) ).iconFor ( account () )); 0231 0232 QAction *tmp = new QAction(this); 0233 tmp->setIcon( QIcon::fromTheme(iconSet) ); 0234 tmp->setText( str ); 0235 connect(tmp, SIGNAL(triggered(bool)), SLOT(slotSelectResource())); 0236 actionSelectResource->addAction ( tmp ); 0237 } 0238 0239 i++; 0240 } 0241 0242 } 0243 0244 actionCollection->append( actionAuthorization ); 0245 actionCollection->append( actionSetAvailability ); 0246 actionCollection->append( actionSelectResource ); 0247 0248 0249 #ifdef SUPPORT_JINGLE 0250 QAction *actionVoiceCall = new QAction( (i18n ("Voice call"), "voicecall", 0, this, SLOT (voiceCall()), 0, "jabber_voicecall"); 0251 actionVoiceCall->setEnabled( false ); 0252 0253 actionCollection->append( actionVoiceCall ); 0254 0255 // Check if the current contact support Voice calls, also honor lock by default. 0256 JabberResource *bestResource = account()->resourcePool()->bestJabberResource( mRosterItem.jid() ); 0257 if( bestResource && bestResource->features().canVoice() ) 0258 { 0259 actionVoiceCall->setEnabled( true ); 0260 } 0261 #endif 0262 0263 return actionCollection; 0264 } 0265 0266 void JabberContact::handleIncomingMessage (const XMPP::Message & message) 0267 { 0268 QString viewPlugin; 0269 Kopete::Message *newMessage = 0L; 0270 0271 qCDebug(JABBER_PROTOCOL_LOG) << "Received Message Type:" << message.type (); 0272 0273 // fetch message manager 0274 JabberChatSession *mManager = manager ( message.from().resource (), Kopete::Contact::CanCreate ); 0275 0276 // evaluate notifications 0277 if ( message.type () != "error" ) 0278 { 0279 if (!message.invite().isEmpty()) 0280 { 0281 QString room=message.invite(); 0282 QString originalBody=message.body().isEmpty() ? QString() : 0283 i18n( "The original message is: <i>\" %1 \"</i><br />" , message.body().toHtmlEscaped()); 0284 QString mes=i18n("<qt><i>%1</i> has invited you to join the conference <b>%2</b><br />%3<br />" 0285 "If you want to accept and join, just <b>enter your nickname</b> and press ok<br />" 0286 "If you want to decline, press cancel</qt>", 0287 message.from().full(), room , originalBody); 0288 0289 bool ok=false; 0290 QString futureNewNickName = KInputDialog::getText( i18n( "Invited to a conference - Jabber Plugin" ), 0291 mes, QString() , &ok , (mManager ? dynamic_cast<QWidget*>(mManager->view(false)) : 0) ); 0292 if ( !ok || !account()->isConnected() || futureNewNickName.isEmpty() ) 0293 return; 0294 0295 XMPP::Jid roomjid(room); 0296 account()->client()->joinGroupChat( roomjid.host() , roomjid.user() , futureNewNickName ); 0297 return; 0298 } 0299 else if (message.body().isEmpty()) 0300 // Then here could be event notifications 0301 { 0302 if (message.containsEvent ( XMPP::CancelEvent ) || (message.chatState() != XMPP::StateNone && message.chatState() != XMPP::StateComposing) ) 0303 mManager->receivedTypingMsg ( this, false ); 0304 else if (message.containsEvent ( XMPP::ComposingEvent )|| message.chatState() == XMPP::StateComposing ) 0305 mManager->receivedTypingMsg ( this, true ); 0306 if (message.containsEvent ( XMPP::DisplayedEvent ) ) 0307 mManager->receivedEventNotification ( i18n("Message has been displayed") ); 0308 else if (message.containsEvent ( XMPP::DeliveredEvent ) ) 0309 mManager->receivedEventNotification ( i18n("Message has been delivered") ); 0310 else if (message.containsEvent ( XMPP::OfflineEvent ) ) 0311 { 0312 mManager->receivedEventNotification( i18n("Message stored on the server, contact offline") ); 0313 } 0314 else if (message.chatState() == XMPP::StateGone ) 0315 { 0316 if(mManager->view( Kopete::Contact::CannotCreate )) 0317 { //show an internal message if the user has not already closed his window 0318 Kopete::Message m=Kopete::Message ( this, mManager->members() ); 0319 m.setPlainBody( i18n("%1 has ended his/her participation in the chat session.", metaContact()->displayName()) ); 0320 m.setDirection( Kopete::Message::Internal ); 0321 0322 mManager->appendMessage ( m, message.from().resource () ); 0323 } 0324 } 0325 } 0326 else 0327 // Then here could be event notification requests 0328 { 0329 mRequestComposingEvent = message.containsEvent ( XMPP::ComposingEvent ); 0330 mRequestOfflineEvent = message.containsEvent ( XMPP::OfflineEvent ); 0331 mRequestDeliveredEvent = message.containsEvent ( XMPP::DeliveredEvent ); 0332 mRequestDisplayedEvent = message.containsEvent ( XMPP::DisplayedEvent); 0333 } 0334 } 0335 0336 /** 0337 * Don't display empty messages, these were most likely just carrying 0338 * event notifications or other payload. 0339 */ 0340 if ( message.body().isEmpty () && message.urlList().isEmpty () && !message.containsHTML() && message.xencrypted().isEmpty() ) 0341 return; 0342 0343 // determine message type 0344 if (message.type () == "chat") 0345 viewPlugin = "kopete_chatwindow"; 0346 else 0347 viewPlugin = "kopete_emailwindow"; 0348 0349 Kopete::ContactPtrList contactList; 0350 contactList.append ( account()->myself () ); 0351 0352 // check for errors 0353 if ( message.type () == "error" ) 0354 { 0355 newMessage = new Kopete::Message( this, contactList ); 0356 newMessage->setTimestamp( message.timeStamp() ); 0357 newMessage->setPlainBody( i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"", 0358 message.body (), message.error().text ) ); 0359 newMessage->setSubject( message.subject() ); 0360 newMessage->setDirection( Kopete::Message::Inbound ); 0361 newMessage->setRequestedPlugin( viewPlugin ); 0362 } 0363 else 0364 { 0365 // store message id for outgoing notifications 0366 mLastReceivedMessageId = message.id (); 0367 0368 // convert XMPP::Message into Kopete::Message 0369 // retrieve and reformat body 0370 QString body = message.body (); 0371 if( !message.xencrypted().isEmpty() ) 0372 { 0373 body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n"); 0374 } 0375 else if( message.containsHTML() ) 0376 { 0377 qCDebug(JABBER_PROTOCOL_LOG) << "Received a xHTML message"; 0378 newMessage = new Kopete::Message ( this, contactList ); 0379 newMessage->setTimestamp( message.timeStamp() ); 0380 newMessage->setHtmlBody( message.html().toString() ); 0381 newMessage->setSubject( message.subject() ); 0382 newMessage->setDirection( Kopete::Message::Inbound ); 0383 newMessage->setRequestedPlugin( viewPlugin ); 0384 } 0385 else if ( !body.isEmpty () ) 0386 { 0387 qCDebug(JABBER_PROTOCOL_LOG) << "Received a plain text message"; 0388 newMessage = new Kopete::Message ( this, contactList ); 0389 newMessage->setTimestamp( message.timeStamp() ); 0390 newMessage->setPlainBody( body ); 0391 newMessage->setSubject( message.subject() ); 0392 newMessage->setDirection( Kopete::Message::Inbound ); 0393 newMessage->setRequestedPlugin( viewPlugin ); 0394 } 0395 } 0396 0397 // append message to (eventually new) manager and preselect the originating resource 0398 if ( newMessage ) 0399 { 0400 mManager->appendMessage ( *newMessage, message.from().resource () ); 0401 0402 delete newMessage; 0403 } 0404 0405 // append URLs as separate messages 0406 0407 /* 0408 * We need to copy it here because Iris returns a copy 0409 * and we can't work with a returned copy in a for() loop. 0410 */ 0411 XMPP::UrlList urlList = message.urlList(); 0412 0413 foreach(XMPP::UrlList::const_reference xurl, urlList) 0414 { 0415 QString description = !xurl.desc().isEmpty() ? Qt::escape ( xurl.desc() ) : xurl.url(); 0416 0417 Kopete::Message msg ( this, contactList ); 0418 msg.setTimestamp( message.timeStamp() ); 0419 msg.setHtmlBody( QString ( "<a href=\"%1\">%2</a>" ).arg ( xurl.url(), description ) ); 0420 msg.setSubject( message.subject() ); 0421 msg.setDirection( Kopete::Message::Inbound ); 0422 msg.setRequestedPlugin( viewPlugin ); 0423 0424 mManager->appendMessage ( msg, message.from().resource () ); 0425 } 0426 } 0427 0428 void JabberContact::slotCheckVCard () 0429 { 0430 QDateTime cacheDate; 0431 Kopete::Property cacheDateString = property ( protocol()->propVCardCacheTimeStamp ); 0432 0433 // don't do anything while we are offline 0434 if ( !account()->myself()->onlineStatus().isDefinitelyOnline () ) 0435 { 0436 return; 0437 } 0438 0439 if(!mDiscoDone) 0440 { 0441 if(transport()) //no need to disco if this is a legacy contact 0442 mDiscoDone = true; 0443 else if(!rosterItem().jid().node().isEmpty()) 0444 mDiscoDone = true; //contact with an @ are not transport for sure 0445 else 0446 { 0447 mDiscoDone = true; //or it will happen twice, we don't want that. 0448 //disco to see if it's not a transport 0449 XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask()); 0450 QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished())); 0451 jt->get(rosterItem().jid(), QString()); 0452 jt->go(true); 0453 } 0454 } 0455 0456 0457 // avoid warning if key does not exist in configuration file 0458 if ( cacheDateString.isNull () ) 0459 cacheDate = QDateTime::currentDateTime().addDays ( -2 ); 0460 else 0461 cacheDate = QDateTime::fromString ( cacheDateString.value().toString (), Qt::ISODate ); 0462 0463 qCDebug(JABBER_PROTOCOL_LOG) << "Cached vCard data for " << contactId () << " from " << cacheDate.toString (); 0464 0465 if ( !mVCardUpdateInProgress && ( cacheDate.addDays ( 1 ) < QDateTime::currentDateTime () ) ) 0466 { 0467 qCDebug(JABBER_PROTOCOL_LOG) << "Scheduling update."; 0468 0469 mVCardUpdateInProgress = true; 0470 0471 // current data is older than 24 hours, request a new one 0472 QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT (slotGetTimedVCard()) ); 0473 } 0474 0475 } 0476 0477 void JabberContact::slotGetTimedVCard () 0478 { 0479 mVCardUpdateInProgress = false; 0480 0481 // check if we are still connected - eventually we lost our connection in the meantime 0482 if ( !account()->myself()->onlineStatus().isDefinitelyOnline () ) 0483 { 0484 // we are not connected, discard this update 0485 return; 0486 } 0487 0488 if(!mDiscoDone) 0489 { 0490 if(transport()) //no need to disco if this is a legacy contact 0491 mDiscoDone = true; 0492 else if(!rosterItem().jid().node().isEmpty()) 0493 mDiscoDone = true; //contact with an @ are not transport for sure 0494 else 0495 { 0496 //disco to see if it's not a transport 0497 XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask()); 0498 QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished())); 0499 jt->get(rosterItem().jid(), QString()); 0500 jt->go(true); 0501 } 0502 } 0503 0504 qCDebug(JABBER_PROTOCOL_LOG) << "Requesting vCard for " << contactId () << " from update timer."; 0505 0506 mVCardUpdateInProgress = true; 0507 0508 // request vCard 0509 XMPP::JT_VCard *task = new XMPP::JT_VCard ( account()->client()->rootTask () ); 0510 // signal to ourselves when the vCard data arrived 0511 QObject::connect ( task, SIGNAL (finished()), this, SLOT (slotGotVCard()) ); 0512 task->get ( mRosterItem.jid () ); 0513 task->go ( true ); 0514 0515 } 0516 0517 void JabberContact::slotGotVCard () 0518 { 0519 0520 XMPP::JT_VCard * vCard = (XMPP::JT_VCard *) sender (); 0521 0522 // update timestamp of last vCard retrieval 0523 if ( metaContact() && !metaContact()->isTemporary () ) 0524 { 0525 setProperty ( protocol()->propVCardCacheTimeStamp, QDateTime::currentDateTime().toString ( Qt::ISODate ) ); 0526 } 0527 0528 mVCardUpdateInProgress = false; 0529 0530 if ( !vCard->success() ) 0531 { 0532 /* 0533 * A vCard for the user does not exist or the 0534 * request was unsuccessful or incomplete. 0535 * The timestamp was already updated when 0536 * requesting the vCard, so it's safe to 0537 * just do nothing here. 0538 */ 0539 return; 0540 } 0541 0542 setPropertiesFromVCard ( vCard->vcard () ); 0543 0544 } 0545 0546 void JabberContact::slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus ) 0547 { 0548 0549 /* 0550 * Checking the last activity only makes sense if a contact is offline. 0551 * So, this check should only be done in the following cases: 0552 * - Kopete goes online for the first time and this contact is offline, or 0553 * - Kopete is already online and this contact went offline. 0554 * 0555 * Since Kopete already takes care of maintaining the lastSeen property 0556 * if the contact changes its state while we are online, we don't need 0557 * to query its activity after we are already connected. 0558 */ 0559 0560 if ( onlineStatus().isDefinitelyOnline () ) 0561 { 0562 // Kopete already deals with lastSeen if the contact is online 0563 return; 0564 } 0565 0566 if ( ( oldStatus.status () == Kopete::OnlineStatus::Connecting ) && newStatus.isDefinitelyOnline () ) 0567 { 0568 qCDebug(JABBER_PROTOCOL_LOG) << "Scheduling request for last activity for " << mRosterItem.jid().bare (); 0569 0570 QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT (slotGetTimedLastActivity()) ); 0571 } 0572 0573 } 0574 0575 void JabberContact::slotGetTimedLastActivity () 0576 { 0577 /* 0578 * We have been called from @ref slotCheckLastActivity. 0579 * We could have lost our connection in the meantime, 0580 * so make sure we are online. Additionally, the contact 0581 * itself could have gone online, so make sure it is 0582 * still offline. (otherwise the last seen property is 0583 * maintained by Kopete) 0584 */ 0585 0586 if ( onlineStatus().isDefinitelyOnline () ) 0587 { 0588 // Kopete already deals with setting lastSeen if the contact is online 0589 return; 0590 } 0591 0592 if ( account()->myself()->onlineStatus().isDefinitelyOnline () ) 0593 { 0594 qCDebug(JABBER_PROTOCOL_LOG) << "Requesting last activity from timer for " << mRosterItem.jid().bare (); 0595 0596 JT_GetLastActivity *task = new JT_GetLastActivity ( account()->client()->rootTask () ); 0597 QObject::connect ( task, SIGNAL (finished()), this, SLOT (slotGotLastActivity()) ); 0598 task->get ( mRosterItem.jid () ); 0599 task->go ( true ); 0600 } 0601 0602 } 0603 0604 void JabberContact::slotGotLastActivity () 0605 { 0606 JT_GetLastActivity *task = (JT_GetLastActivity *) sender (); 0607 0608 if ( task->success () ) 0609 { 0610 setProperty ( protocol()->propLastSeen, QDateTime::currentDateTime().addSecs ( -task->seconds () ) ); 0611 if( !task->message().isEmpty() ) 0612 { 0613 setStatusMessage( task->message() ); 0614 } 0615 } 0616 0617 } 0618 0619 void JabberContact::slotSendVCard() 0620 { 0621 XMPP::VCard vCard; 0622 XMPP::VCard::AddressList addressList; 0623 XMPP::VCard::EmailList emailList; 0624 XMPP::VCard::PhoneList phoneList; 0625 0626 if ( !account()->isConnected () ) 0627 { 0628 account()->errorConnectFirst (); 0629 return; 0630 } 0631 0632 // General information 0633 vCard.setNickName (property(protocol()->propNickName).value().toString()); 0634 vCard.setFullName (property(protocol()->propFullName).value().toString()); 0635 vCard.setJid (property(protocol()->propJid).value().toString()); 0636 vCard.setBdayStr (property(protocol()->propBirthday).value().toString()); 0637 vCard.setTimezone (property(protocol()->propTimezone).value().toString()); 0638 vCard.setUrl (property(protocol()->propHomepage).value().toString()); 0639 0640 // home address tab 0641 XMPP::VCard::Address homeAddress; 0642 0643 homeAddress.home = true; 0644 homeAddress.street = property(protocol()->propHomeStreet).value().toString(); 0645 homeAddress.extaddr = property(protocol()->propHomeExtAddr).value().toString(); 0646 homeAddress.pobox = property(protocol()->propHomePOBox).value().toString(); 0647 homeAddress.locality = property(protocol()->propHomeCity).value().toString(); 0648 homeAddress.pcode = property(protocol()->propHomePostalCode).value().toString(); 0649 homeAddress.country = property(protocol()->propHomeCountry).value().toString(); 0650 0651 // work address tab 0652 XMPP::VCard::Address workAddress; 0653 0654 workAddress.work = true; 0655 workAddress.street = property(protocol()->propWorkStreet).value().toString(); 0656 workAddress.extaddr = property(protocol()->propWorkExtAddr).value().toString(); 0657 workAddress.pobox = property(protocol()->propWorkPOBox).value().toString(); 0658 workAddress.locality = property(protocol()->propWorkCity).value().toString(); 0659 workAddress.pcode = property(protocol()->propWorkPostalCode).value().toString(); 0660 workAddress.country = property(protocol()->propWorkCountry).value().toString(); 0661 0662 addressList.append(homeAddress); 0663 addressList.append(workAddress); 0664 0665 vCard.setAddressList(addressList); 0666 0667 // home email 0668 XMPP::VCard::Email homeEmail; 0669 0670 homeEmail.home = true; 0671 homeEmail.userid = property(protocol()->propEmailAddress).value().toString(); 0672 0673 // work email 0674 XMPP::VCard::Email workEmail; 0675 0676 workEmail.work = true; 0677 workEmail.userid = property(protocol()->propWorkEmailAddress).value().toString(); 0678 0679 emailList.append(homeEmail); 0680 emailList.append(workEmail); 0681 0682 vCard.setEmailList(emailList); 0683 0684 // work information tab 0685 XMPP::VCard::Org org; 0686 org.name = property(protocol()->propCompanyName).value().toString(); 0687 org.unit = property(protocol()->propCompanyDepartement).value().toString().split(','); 0688 vCard.setOrg(org); 0689 vCard.setTitle (property(protocol()->propCompanyPosition).value().toString()); 0690 vCard.setRole (property(protocol()->propCompanyRole).value().toString()); 0691 0692 // phone numbers tab 0693 XMPP::VCard::Phone phoneHome; 0694 phoneHome.home = true; 0695 phoneHome.number = property(protocol()->propPrivatePhone).value().toString(); 0696 0697 XMPP::VCard::Phone phoneWork; 0698 phoneWork.work = true; 0699 phoneWork.number = property(protocol()->propWorkPhone).value().toString(); 0700 0701 XMPP::VCard::Phone phoneFax; 0702 phoneFax.fax = true; 0703 phoneFax.number = property(protocol()->propPhoneFax).value().toString(); 0704 0705 XMPP::VCard::Phone phoneCell; 0706 phoneCell.cell = true; 0707 phoneCell.number = property(protocol()->propPrivateMobilePhone).value().toString(); 0708 0709 phoneList.append(phoneHome); 0710 phoneList.append(phoneWork); 0711 phoneList.append(phoneFax); 0712 phoneList.append(phoneCell); 0713 0714 vCard.setPhoneList(phoneList); 0715 0716 // about tab 0717 vCard.setDesc(property(protocol()->propAbout).value().toString()); 0718 0719 // Set contact photo as a binary value (if he has set a photo) 0720 if( hasProperty( protocol()->propPhoto.key() ) ) 0721 { 0722 QString photoPath = property( protocol()->propPhoto ).value().toString(); 0723 QImage image( photoPath ); 0724 QByteArray ba; 0725 QBuffer buffer( &ba ); 0726 buffer.open( QIODevice::WriteOnly ); 0727 image.save( &buffer, "PNG" ); 0728 vCard.setPhoto( ba ); 0729 } 0730 0731 vCard.setVersion("3.0"); 0732 vCard.setProdId("Kopete"); 0733 0734 XMPP::JT_VCard *task = new XMPP::JT_VCard (account()->client()->rootTask ()); 0735 // signal to ourselves when the vCard data arrived 0736 QObject::connect (task, SIGNAL (finished()), this, SLOT (slotSentVCard())); 0737 task->set (rosterItem().jid(), vCard); 0738 task->go (true); 0739 } 0740 0741 void JabberContact::setPhoto( const QString &photoPath ) 0742 { 0743 QImage contactPhoto(photoPath); 0744 QString newPhotoPath = photoPath; 0745 if(contactPhoto.width() > 96 || contactPhoto.height() > 96) 0746 { 0747 // Save image to a new location if the image isn't the correct format. 0748 QString newLocation( KStandardDirs::locateLocal( "appdata", "jabberphotos/"+ KUrl(photoPath).fileName().toLower() ) ); 0749 0750 // Scale and crop the picture. 0751 contactPhoto = contactPhoto.scaled( 96, 96, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0752 // crop image if not square 0753 if(contactPhoto.width() < contactPhoto.height()) 0754 contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 96, 96); 0755 else if (contactPhoto.width() > contactPhoto.height()) 0756 contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 96, 96); 0757 0758 // Use the cropped/scaled image now. 0759 if(!contactPhoto.save(newLocation, "PNG")) 0760 newPhotoPath.clear(); 0761 else 0762 newPhotoPath = newLocation; 0763 } 0764 else if (contactPhoto.width() < 32 || contactPhoto.height() < 32) 0765 { 0766 // Save image to a new location if the image isn't the correct format. 0767 QString newLocation( KStandardDirs::locateLocal( "appdata", "jabberphotos/"+ KUrl(photoPath).fileName().toLower() ) ); 0768 0769 // Scale and crop the picture. 0770 contactPhoto = contactPhoto.scaled( 32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation ); 0771 // crop image if not square 0772 if(contactPhoto.width() < contactPhoto.height()) 0773 contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 32, 32); 0774 else if (contactPhoto.width() > contactPhoto.height()) 0775 contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 32, 32); 0776 0777 // Use the cropped/scaled image now. 0778 if(!contactPhoto.save(newLocation, "PNG")) 0779 newPhotoPath.clear(); 0780 else 0781 newPhotoPath = newLocation; 0782 } 0783 else if (contactPhoto.width() != contactPhoto.height()) 0784 { 0785 // Save image to a new location if the image isn't the correct format. 0786 QString newLocation( KStandardDirs::locateLocal( "appdata", "jabberphotos/"+ KUrl(photoPath).fileName().toLower() ) ); 0787 0788 if(contactPhoto.width() < contactPhoto.height()) 0789 contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, contactPhoto.height(), contactPhoto.height()); 0790 else if (contactPhoto.width() > contactPhoto.height()) 0791 contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, contactPhoto.height(), contactPhoto.height()); 0792 0793 // Use the cropped/scaled image now. 0794 if(!contactPhoto.save(newLocation, "PNG")) 0795 newPhotoPath.clear(); 0796 else 0797 newPhotoPath = newLocation; 0798 } 0799 0800 setProperty( protocol()->propPhoto, newPhotoPath ); 0801 } 0802 0803 void JabberContact::slotSentVCard () 0804 { 0805 0806 } 0807 0808 void JabberContact::slotChatSessionDeleted ( QObject *sender ) 0809 { 0810 qCDebug(JABBER_PROTOCOL_LOG) << "Message manager deleted, collecting the pieces..."; 0811 0812 JabberChatSession *manager = static_cast<JabberChatSession *>(sender); 0813 0814 mManagers.removeAll ( manager ); 0815 0816 } 0817 0818 JabberChatSession *JabberContact::manager ( Kopete::ContactPtrList chatMembers, Kopete::Contact::CanCreateFlags canCreate ) 0819 { 0820 qCDebug(JABBER_PROTOCOL_LOG) << "called, canCreate: " << canCreate; 0821 0822 Kopete::ChatSession *_manager = Kopete::ChatSessionManager::self()->findChatSession ( account()->myself(), chatMembers, protocol() ); 0823 JabberChatSession *manager = dynamic_cast<JabberChatSession*>( _manager ); 0824 0825 /* 0826 * If we didn't find a message manager for this contact, 0827 * instantiate a new one if we are allowed to. (otherwise return 0) 0828 */ 0829 if ( !manager && canCreate ) 0830 { 0831 XMPP::Jid jid = rosterItem().jid(); 0832 0833 /* 0834 * If we have no hardwired JID, set any eventually 0835 * locked resource as preselected resource. 0836 * If there is no locked resource, the resource field 0837 * will stay empty. 0838 */ 0839 if ( jid.resource().isEmpty () ) 0840 jid.setResource ( account()->resourcePool()->lockedResource ( jid ).name () ); 0841 0842 qCDebug(JABBER_PROTOCOL_LOG) << "No manager found, creating a new one with resource '" << jid.resource () << "'"; 0843 0844 manager = new JabberChatSession ( protocol(), static_cast<JabberBaseContact *>(account()->myself()), chatMembers, jid.resource () ); 0845 connect ( manager, SIGNAL (destroyed(QObject*)), this, SLOT (slotChatSessionDeleted(QObject*)) ); 0846 mManagers.append ( manager ); 0847 } 0848 0849 return manager; 0850 0851 } 0852 0853 Kopete::ChatSession *JabberContact::manager ( Kopete::Contact::CanCreateFlags canCreate ) 0854 { 0855 qCDebug(JABBER_PROTOCOL_LOG) << "called, canCreate: " << canCreate; 0856 0857 Kopete::ContactPtrList chatMembers; 0858 chatMembers.append ( this ); 0859 0860 return manager ( chatMembers, canCreate ); 0861 0862 } 0863 0864 JabberChatSession *JabberContact::manager ( const QString &resource, Kopete::Contact::CanCreateFlags canCreate ) 0865 { 0866 qCDebug(JABBER_PROTOCOL_LOG) << "called, canCreate: " << canCreate << ", Resource: '" << resource << "'"; 0867 0868 /* 0869 * First of all, see if we already have a manager matching 0870 * the requested resource or if there are any managers with 0871 * an empty resource. 0872 */ 0873 if ( !resource.isEmpty () ) 0874 { 0875 QList<JabberChatSession*>::iterator it = mManagers.begin(); 0876 QList<JabberChatSession*>::iterator end = mManagers.end(); 0877 for ( ; it != end ; ++it ) 0878 { 0879 JabberChatSession *mManager = *it; 0880 if ( mManager->resource().isEmpty () || ( mManager->resource () == resource ) ) 0881 { 0882 // we found a matching manager, return this one 0883 qCDebug(JABBER_PROTOCOL_LOG) << "Found an existing message manager for this resource."; 0884 return mManager; 0885 } 0886 } 0887 0888 qCDebug(JABBER_PROTOCOL_LOG) << "No manager found for this resource, creating a new one."; 0889 0890 /* 0891 * If we have come this far, we were either supposed to create 0892 * a manager with a preselected resource but have found 0893 * no available manager. (not even one with an empty resource) 0894 * This means, we will have to create a new one with a 0895 * preselected resource. 0896 */ 0897 Kopete::ContactPtrList chatmembers; 0898 chatmembers.append ( this ); 0899 JabberChatSession *manager = new JabberChatSession ( protocol(), 0900 static_cast<JabberBaseContact *>(account()->myself()), 0901 chatmembers, resource ); 0902 connect ( manager, SIGNAL (destroyed(QObject*)), this, SLOT (slotChatSessionDeleted(QObject*)) ); 0903 mManagers.append ( manager ); 0904 0905 return manager; 0906 } 0907 0908 qCDebug(JABBER_PROTOCOL_LOG) << "Resource is empty, grabbing first available manager."; 0909 0910 /* 0911 * The resource is empty, so just return first available manager. 0912 */ 0913 return dynamic_cast<JabberChatSession *>( manager ( canCreate ) ); 0914 0915 } 0916 0917 void JabberContact::deleteContact () 0918 { 0919 qCDebug(JABBER_PROTOCOL_LOG) << "Removing user " << contactId (); 0920 0921 if ( !account()->isConnected () ) 0922 { 0923 account()->errorConnectFirst (); 0924 return; 0925 } 0926 0927 /* 0928 * Follow the recommendation of 0929 * JEP-0162: Best Practices for Roster and Subscription Management 0930 * http://www.jabber.org/jeps/jep-0162.html#removal 0931 */ 0932 0933 bool remove_from_roster=false; 0934 0935 if( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From ) 0936 { 0937 int result = KMessageBox::questionTwoActionsCancel (Kopete::UI::Global::mainWidget(), 0938 i18n ( "Do you also want to remove user %1's authorization to see your status?" , 0939 mRosterItem.jid().bare () ), i18n ("Notification"), 0940 KStandardGuiItem::del (), KGuiItem( i18n("Keep") ),KStandardGuiItem::cancel(), "JabberRemoveAuthorizationOnDelete" ); 0941 if(result == KMessageBox::PrimaryAction ) 0942 remove_from_roster = true; 0943 else if( result == KMessageBox::Cancel) 0944 return; 0945 } 0946 else if( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::To ) 0947 remove_from_roster = true; 0948 0949 if( remove_from_roster ) 0950 { 0951 XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () ); 0952 rosterTask->remove ( mRosterItem.jid () ); 0953 rosterTask->go ( true ); 0954 } 0955 else 0956 { 0957 sendSubscription("unsubscribe"); 0958 0959 XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () ); 0960 rosterTask->set ( mRosterItem.jid (), QString() , QStringList() ); 0961 rosterTask->go (true); 0962 } 0963 0964 } 0965 0966 void JabberContact::sync ( unsigned int ) 0967 { 0968 // if we are offline or this is a temporary contact or we should not synch, don't bother 0969 if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () || metaContact() == Kopete::ContactList::self()->myself() ) 0970 return; 0971 0972 qCDebug(JABBER_PROTOCOL_LOG) << contactId () /*<< " - " <<kdBacktrace()*/; 0973 0974 if(!m_syncTimer) 0975 { 0976 m_syncTimer=new QTimer(this); 0977 connect(m_syncTimer, SIGNAL(timeout()) , this , SLOT(slotDelayedSync())); 0978 } 0979 m_syncTimer->setSingleShot(true); 0980 m_syncTimer->start(2*1000); 0981 0982 /* 0983 the sync operation is delayed, because when we are doing a move to group operation, 0984 kopete first add the contact to the group, then removes it. 0985 Theses two operations should anyway be done in only one pass. 0986 0987 if there is two jabber contact in one metacontact, this may result in an infinite change of 0988 groups between theses two contacts, and the server is being flooded. 0989 */ 0990 } 0991 0992 void JabberContact::slotDelayedSync( ) 0993 { 0994 m_syncTimer->deleteLater(); 0995 m_syncTimer=0L; 0996 // if we are offline or this is a temporary contact or we should not synch, don't bother 0997 if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () ) 0998 return; 0999 1000 bool changed=metaContact()->displayName() != mRosterItem.name(); 1001 1002 1003 QStringList groups; 1004 Kopete::GroupList groupList = metaContact ()->groups (); 1005 1006 qCDebug(JABBER_PROTOCOL_LOG) << "Synchronizing contact " << contactId (); 1007 1008 foreach ( Kopete::Group * g, groupList ) 1009 { 1010 if ( g->type () != Kopete::Group::TopLevel ) 1011 groups += g->displayName (); 1012 } 1013 1014 if(mRosterItem.groups() != groups) 1015 { 1016 changed=true; 1017 mRosterItem.setGroups ( groups ); 1018 } 1019 1020 if(!changed) 1021 { 1022 qCDebug(JABBER_PROTOCOL_LOG) << "contact has not changed, abort sync"; 1023 return; 1024 } 1025 1026 XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () ); 1027 1028 rosterTask->set ( mRosterItem.jid (), metaContact()->displayName (), mRosterItem.groups () ); 1029 rosterTask->go (true); 1030 1031 } 1032 1033 void JabberContact::sendFile ( const KUrl &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ ) 1034 { 1035 QString filePath; 1036 1037 // if the file location is null, then get it from a file open dialog 1038 if ( !sourceURL.isValid () ) 1039 filePath = KFileDialog::getOpenFileName( KUrl(), "*", 0L, i18n ( "Kopete File Transfer" ) ); 1040 else 1041 filePath = sourceURL.path(KUrl::RemoveTrailingSlash); 1042 1043 QFile file ( filePath ); 1044 1045 if ( file.exists () ) 1046 { 1047 // send the file 1048 new JabberFileTransfer ( account (), this, filePath ); 1049 } 1050 1051 } 1052 1053 1054 void JabberContact::slotSendAuth () 1055 { 1056 1057 qCDebug(JABBER_PROTOCOL_LOG) << "(Re)send auth " << contactId (); 1058 1059 sendSubscription ("subscribed"); 1060 1061 } 1062 1063 void JabberContact::slotRequestAuth () 1064 { 1065 1066 qCDebug(JABBER_PROTOCOL_LOG) << "(Re)request auth " << contactId (); 1067 1068 sendSubscription ("subscribe"); 1069 1070 } 1071 1072 void JabberContact::slotRemoveAuth () 1073 { 1074 1075 qCDebug(JABBER_PROTOCOL_LOG) << "Remove auth " << contactId (); 1076 1077 sendSubscription ("unsubscribed"); 1078 1079 } 1080 1081 void JabberContact::sendSubscription ( const QString& subType ) 1082 { 1083 1084 if ( !account()->isConnected () ) 1085 { 1086 account()->errorConnectFirst (); 1087 return; 1088 } 1089 1090 XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () ); 1091 1092 task->sub ( mRosterItem.jid().full (), subType ); 1093 task->go ( true ); 1094 1095 } 1096 1097 void JabberContact::slotSelectResource () 1098 { 1099 int currentItem = QString ( static_cast<const QAction *>( sender() )->objectName () ).toUInt (); 1100 1101 /* 1102 * Warn the user if there is already an active chat window. 1103 * The resource selection will only apply for newly opened 1104 * windows. 1105 */ 1106 if ( manager ( Kopete::Contact::CannotCreate ) != 0 ) 1107 { 1108 KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (), 1109 KMessageBox::Information, 1110 i18n ("You have preselected a resource for contact %1, " 1111 "but you still have open chat windows for this contact. " 1112 "The preselected resource will only apply to newly opened " 1113 "chat windows.", contactId () ), 1114 i18n ("Jabber Resource Selector") ); 1115 } 1116 1117 if (currentItem == 0) 1118 { 1119 qCDebug(JABBER_PROTOCOL_LOG) << "Removing active resource, trusting bestResource()."; 1120 1121 account()->resourcePool()->removeLock ( rosterItem().jid() ); 1122 } 1123 else 1124 { 1125 QString selectedResource = static_cast<const QAction *>(sender())->text(); 1126 1127 qCDebug(JABBER_PROTOCOL_LOG) << "Moving to resource " << selectedResource; 1128 1129 account()->resourcePool()->lockToResource ( rosterItem().jid() , XMPP::Resource ( selectedResource ) ); 1130 } 1131 1132 } 1133 1134 void JabberContact::sendPresence ( const XMPP::Status status ) 1135 { 1136 1137 if ( !account()->isConnected () ) 1138 { 1139 account()->errorConnectFirst (); 1140 return; 1141 } 1142 1143 XMPP::Status newStatus = status; 1144 1145 // honor our priority 1146 if(newStatus.isAvailable()) 1147 newStatus.setPriority ( account()->configGroup()->readEntry ( "Priority", 5 ) ); 1148 1149 XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () ); 1150 1151 task->pres ( bestAddress (), newStatus); 1152 task->go ( true ); 1153 1154 } 1155 1156 1157 void JabberContact::slotStatusOnline () 1158 { 1159 1160 XMPP::Status status; 1161 status.setShow(""); 1162 1163 sendPresence ( status ); 1164 1165 } 1166 1167 void JabberContact::slotStatusChatty () 1168 { 1169 1170 XMPP::Status status; 1171 status.setShow ("chat"); 1172 1173 sendPresence ( status ); 1174 1175 } 1176 1177 void JabberContact::slotStatusAway () 1178 { 1179 1180 XMPP::Status status; 1181 status.setShow ("away"); 1182 1183 sendPresence ( status ); 1184 1185 } 1186 1187 void JabberContact::slotStatusXA () 1188 { 1189 1190 XMPP::Status status; 1191 status.setShow ("xa"); 1192 1193 sendPresence ( status ); 1194 1195 } 1196 1197 void JabberContact::slotStatusDND () 1198 { 1199 1200 XMPP::Status status; 1201 status.setShow ("dnd"); 1202 1203 sendPresence ( status ); 1204 1205 1206 } 1207 1208 void JabberContact::slotStatusInvisible () 1209 { 1210 1211 XMPP::Status status; 1212 status.setIsAvailable( false ); 1213 1214 sendPresence ( status ); 1215 1216 } 1217 1218 bool JabberContact::isContactRequestingEvent( XMPP::MsgEvent event ) 1219 { 1220 if ( event == OfflineEvent ) 1221 return mRequestOfflineEvent; 1222 else if ( event == DeliveredEvent ) 1223 return mRequestDeliveredEvent; 1224 else if ( event == DisplayedEvent ) 1225 return mRequestDisplayedEvent; 1226 else if ( event == ComposingEvent ) 1227 return mRequestComposingEvent; 1228 else if ( event == CancelEvent ) 1229 return mRequestComposingEvent; 1230 else 1231 return false; 1232 } 1233 1234 QString JabberContact::lastReceivedMessageId () const 1235 { 1236 return mLastReceivedMessageId; 1237 } 1238 1239 void JabberContact::voiceCall( ) 1240 { 1241 #ifdef SUPPORT_JINGLE 1242 Jid jid = mRosterItem.jid(); 1243 1244 // It's honor lock by default. 1245 JabberResource *bestResource = account()->resourcePool()->bestJabberResource( jid ); 1246 if( bestResource ) 1247 { 1248 if( jid.resource().isEmpty() ) 1249 { 1250 // If the jid resource is empty, get the JID from best resource for this contact. 1251 jid = bestResource->jid(); 1252 } 1253 1254 // Check if the voice caller exist and the current resource support voice. 1255 if( account()->voiceCaller() && bestResource->features().canVoice() ) 1256 { 1257 JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( jid, account()->voiceCaller() ); 1258 voiceDialog->show(); 1259 voiceDialog->start(); 1260 } 1261 #if 0 1262 if( account()->sessionManager() && bestResource->features().canVoice() ) 1263 { 1264 JingleVoiceSession *session = static_cast<JingleVoiceSession*>(account()->sessionManager()->createSession("http://www.google.com/session/phone", jid)); 1265 1266 JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog(session); 1267 voiceDialog->show(); 1268 voiceDialog->start(); 1269 } 1270 #endif 1271 } 1272 else 1273 { 1274 // Shouldn't never go there. 1275 } 1276 #endif 1277 } 1278 1279 void JabberContact::slotDiscoFinished( ) 1280 { 1281 mDiscoDone = true; 1282 JT_DiscoInfo *jt = (JT_DiscoInfo *)sender(); 1283 1284 bool is_transport=false; 1285 QString tr_type; 1286 1287 if ( jt->success() ) 1288 { 1289 QList<XMPP::DiscoItem::Identity> identities = jt->item().identities(); 1290 QList<XMPP::DiscoItem::Identity>::Iterator it; 1291 for ( it = identities.begin(); it != identities.end(); ++it ) 1292 { 1293 XMPP::DiscoItem::Identity ident=*it; 1294 if(ident.category == "gateway") 1295 { 1296 is_transport=true; 1297 tr_type=ident.type; 1298 //name=ident.name; 1299 1300 break; //(we currently only support gateway) 1301 } 1302 else if (ident.category == "service") 1303 { 1304 //The ApaSMSAgent is reporting itself as service (instead of gateway) which is broken. 1305 //we anyway support it. See bug 127811 1306 if(ident.type == "sms") 1307 { 1308 is_transport=true; 1309 tr_type=ident.type; 1310 } 1311 } 1312 } 1313 } 1314 1315 if(is_transport && !transport()) 1316 { //ok, we are not a contact, we are a transport.... 1317 1318 XMPP::RosterItem ri = rosterItem(); 1319 Kopete::MetaContact *mc=metaContact(); 1320 JabberAccount *parentAccount=account(); 1321 Kopete::OnlineStatus status=onlineStatus(); 1322 1323 qCDebug(JABBER_PROTOCOL_LOG) << ri.jid().full() << " is not a contact but a gateway - " << this; 1324 1325 if( Kopete::AccountManager::self()->findAccount( protocol()->pluginId() , account()->accountId() + '/' + ri.jid().bare() ) ) 1326 { 1327 qCDebug(JABBER_PROTOCOL_LOG) << "oops, transport already exists, abort operation "; 1328 return; 1329 } 1330 1331 delete this; //we are not a contact i said ! 1332 1333 if(mc->contacts().count() == 0) 1334 Kopete::ContactList::self()->removeMetaContact( mc ); 1335 1336 //we need to create the transport when 'this' is already deleted, so transport->myself() will not conflict with it 1337 JabberTransport *transport = new JabberTransport( parentAccount , ri , tr_type ); 1338 if(!Kopete::AccountManager::self()->registerAccount( transport )) 1339 return; 1340 1341 transport->setIdentity( parentAccount->identity() ); 1342 transport->myself()->setOnlineStatus( status ); //push back the online status 1343 return; 1344 } 1345 } 1346 1347 #include "moc_jabbercontact.cpp"