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"