summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/jabber/jabbercontact.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/jabber/jabbercontact.cpp')
-rw-r--r--kopete/protocols/jabber/jabbercontact.cpp1328
1 files changed, 1328 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jabbercontact.cpp b/kopete/protocols/jabber/jabbercontact.cpp
new file mode 100644
index 00000000..c8589e1e
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercontact.cpp
@@ -0,0 +1,1328 @@
+ /*
+ * jabbercontact.cpp - Regular Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <[email protected]>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+ *
+ * Kopete (c) by the Kopete developers <[email protected]>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabbercontact.h"
+
+#include "xmpp_tasks.h"
+#include "im.h"
+
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qstylesheet.h>
+#include <qimage.h>
+#include <qregexp.h>
+#include <qbuffer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kinputdialog.h>
+#include <kopeteview.h>
+
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteaccountmanager.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabberchatsession.h"
+#include "jabberresource.h"
+#include "jabberresourcepool.h"
+#include "jabberfiletransfer.h"
+#include "jabbertransport.h"
+#include "dlgjabbervcard.h"
+
+#ifdef SUPPORT_JINGLE
+// #include "jinglesessionmanager.h"
+// #include "jinglevoicesession.h"
+#include "jinglevoicesessiondialog.h"
+#endif
+
+/**
+ * JabberContact constructor
+ */
+JabberContact::JabberContact (const XMPP::RosterItem &rosterItem, Kopete::Account *_account, Kopete::MetaContact * mc, const QString &legacyId)
+ : JabberBaseContact ( rosterItem, _account, mc, legacyId) , mDiscoDone(false), m_syncTimer(0L)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << contactId() << " is created - " << this << endl;
+ // this contact is able to transfer files
+ setFileCapable ( true );
+
+ /*
+ * Catch when we're going online for the first time to
+ * update our properties from a vCard. (properties are
+ * not available during startup, so we need to read
+ * them later - this also serves as a random update
+ * feature)
+ * Note: The only time account->myself() could be a
+ * NULL pointer is if this contact here is the myself()
+ * instance itself. Since in that case we wouldn't
+ * get updates at all, we need to treat that as a
+ * special case.
+ */
+
+ mVCardUpdateInProgress = false;
+
+ if ( !account()->myself () )
+ {
+ // this contact is a regular contact
+ connect ( this,
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckVCard () ) );
+ }
+ else
+ {
+ // this contact is the myself instance
+ connect ( account()->myself (),
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckVCard () ) );
+
+ connect ( account()->myself (),
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+
+ /*
+ * Trigger update once if we're already connected for contacts
+ * that are being added while we are online.
+ */
+ if ( account()->myself()->onlineStatus().isDefinitelyOnline() )
+ {
+ slotGetTimedVCard ();
+ }
+ }
+
+ mRequestOfflineEvent = false;
+ mRequestDisplayedEvent = false;
+ mRequestDeliveredEvent = false;
+ mRequestComposingEvent = false;
+ mRequestGoneEvent = false;
+}
+
+
+
+JabberContact::~JabberContact()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << contactId() << " is destroyed - " << this << endl;
+}
+
+QPtrList<KAction> *JabberContact::customContextMenuActions ()
+{
+
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+
+ KActionMenu *actionAuthorization = new KActionMenu ( i18n ("Authorization"), "connect_established", this, "jabber_authorization");
+
+ KAction *resendAuthAction, *requestAuthAction, *removeAuthAction;
+
+ resendAuthAction = new KAction (i18n ("(Re)send Authorization To"), "mail_forward", 0,
+ this, SLOT (slotSendAuth ()), actionAuthorization, "actionSendAuth");
+ resendAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::To || mRosterItem.subscription().type() == XMPP::Subscription::None );
+ actionAuthorization->insert(resendAuthAction);
+
+ requestAuthAction = new KAction (i18n ("(Re)request Authorization From"), "mail_reply", 0,
+ this, SLOT (slotRequestAuth ()), actionAuthorization, "actionRequestAuth");
+ requestAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::From || mRosterItem.subscription().type() == XMPP::Subscription::None );
+ actionAuthorization->insert(requestAuthAction);
+
+ removeAuthAction = new KAction (i18n ("Remove Authorization From"), "mail_delete", 0,
+ this, SLOT (slotRemoveAuth ()), actionAuthorization, "actionRemoveAuth");
+ removeAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From );
+ actionAuthorization->insert(removeAuthAction);
+
+ KActionMenu *actionSetAvailability = new KActionMenu (i18n ("Set Availability"), "kopeteavailable", this, "jabber_online");
+
+ actionSetAvailability->insert(new KAction (i18n ("Online"), protocol()->JabberKOSOnline.iconFor(this),
+ 0, this, SLOT (slotStatusOnline ()), actionSetAvailability, "actionOnline"));
+ actionSetAvailability->insert(new KAction (i18n ("Free to Chat"), protocol()->JabberKOSChatty.iconFor(this),
+ 0, this, SLOT (slotStatusChatty ()), actionSetAvailability, "actionChatty"));
+ actionSetAvailability->insert(new KAction (i18n ("Away"), protocol()->JabberKOSAway.iconFor(this),
+ 0, this, SLOT (slotStatusAway ()), actionSetAvailability, "actionAway"));
+ actionSetAvailability->insert(new KAction (i18n ("Extended Away"), protocol()->JabberKOSXA.iconFor(this),
+ 0, this, SLOT (slotStatusXA ()), actionSetAvailability, "actionXA"));
+ actionSetAvailability->insert(new KAction (i18n ("Do Not Disturb"), protocol()->JabberKOSDND.iconFor(this),
+ 0, this, SLOT (slotStatusDND ()), actionSetAvailability, "actionDND"));
+ actionSetAvailability->insert(new KAction (i18n ("Invisible"), protocol()->JabberKOSInvisible.iconFor(this),
+ 0, this, SLOT (slotStatusInvisible ()), actionSetAvailability, "actionInvisible"));
+
+ KActionMenu *actionSelectResource = new KActionMenu (i18n ("Select Resource"), "connect_no", this, "actionSelectResource");
+
+ // if the contact is online, display the resources we have for it,
+ // otherwise disable the menu
+ if (onlineStatus ().status () == Kopete::OnlineStatus::Offline)
+ {
+ actionSelectResource->setEnabled ( false );
+ }
+ else
+ {
+ QStringList items;
+ XMPP::ResourceList availableResources;
+
+ int activeItem = 0, i = 1;
+ const XMPP::Resource lockedResource = account()->resourcePool()->lockedResource ( mRosterItem.jid () );
+
+ // put default resource first
+ items.append (i18n ("Automatic (best/default resource)"));
+
+ account()->resourcePool()->findResources ( mRosterItem.jid (), availableResources );
+
+ XMPP::ResourceList::const_iterator resourcesEnd = availableResources.end ();
+ for ( XMPP::ResourceList::const_iterator it = availableResources.begin(); it != resourcesEnd; ++it, i++)
+ {
+ items.append ( (*it).name() );
+
+ if ( (*it).name() == lockedResource.name() )
+ activeItem = i;
+ }
+
+ // now go through the string list and add the resources with their icons
+ i = 0;
+ QStringList::const_iterator itemsEnd = items.end ();
+ for(QStringList::const_iterator it = items.begin(); it != itemsEnd; ++it)
+ {
+ if( i == activeItem )
+ {
+ actionSelectResource->insert ( new KAction( ( *it ), "button_ok", 0, this, SLOT( slotSelectResource() ),
+ actionSelectResource, QString::number( i ).latin1() ) );
+ }
+ else
+ {
+ /*
+ * Select icon, using bestResource() without lock for the automatic entry
+ * and the resources' respective status icons for the rest.
+ */
+ QIconSet iconSet ( !i ?
+ protocol()->resourceToKOS ( account()->resourcePool()->bestResource ( mRosterItem.jid(), false ) ).iconFor ( account () ) : protocol()->resourceToKOS ( *availableResources.find(*it) ).iconFor ( account () ));
+
+ actionSelectResource->insert ( new KAction( ( *it ), iconSet, 0, this, SLOT( slotSelectResource() ),
+ actionSelectResource, QString::number( i ).latin1() ) );
+ }
+
+ i++;
+ }
+
+ }
+
+ actionCollection->append( actionAuthorization );
+ actionCollection->append( actionSetAvailability );
+ actionCollection->append( actionSelectResource );
+
+
+#ifdef SUPPORT_JINGLE
+ KAction *actionVoiceCall = new KAction (i18n ("Voice call"), "voicecall", 0, this, SLOT (voiceCall ()), this, "jabber_voicecall");
+ actionVoiceCall->setEnabled( false );
+
+ actionCollection->append( actionVoiceCall );
+
+ // Check if the current contact support Voice calls, also honour lock by default.
+ JabberResource *bestResource = account()->resourcePool()->bestJabberResource( mRosterItem.jid() );
+ if( bestResource && bestResource->features().canVoice() )
+ {
+ actionVoiceCall->setEnabled( true );
+ }
+#endif
+
+ return actionCollection;
+}
+
+void JabberContact::handleIncomingMessage (const XMPP::Message & message)
+{
+ QString viewPlugin;
+ Kopete::Message *newMessage = 0L;
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Received Message Type:" << message.type () << endl;
+
+ // fetch message manager
+ JabberChatSession *mManager = manager ( message.from().resource (), Kopete::Contact::CanCreate );
+
+ // evaluate notifications
+ if ( message.type () != "error" )
+ {
+ if (!message.invite().isEmpty())
+ {
+ QString room=message.invite();
+ QString originalBody=message.body().isEmpty() ? QString() :
+ i18n( "The original message is : <i>\" %1 \"</i><br>" ).arg(QStyleSheet::escape(message.body()));
+ QString mes=i18n("<qt><i>%1</i> invited you to join the conference <b>%2</b><br>%3<br>"
+ "If you want to accept and join, just <b>enter your nickname</b> and press ok<br>"
+ "If you want to decline, press cancel</qt>")
+ .arg(message.from().full(), room , originalBody);
+
+ bool ok=false;
+ QString futureNewNickName = KInputDialog::getText( i18n( "Invited to a conference - Jabber Plugin" ),
+ mes, QString() , &ok , (mManager ? dynamic_cast<QWidget*>(mManager->view(false)) : 0) );
+ if ( !ok || !account()->isConnected() || futureNewNickName.isEmpty() )
+ return;
+
+ XMPP::Jid roomjid(room);
+ account()->client()->joinGroupChat( roomjid.host() , roomjid.user() , futureNewNickName );
+ return;
+ }
+ else if (message.body().isEmpty())
+ // Then here could be event notifications
+ {
+ if (message.containsEvent ( XMPP::CancelEvent ) )
+ mManager->receivedTypingMsg ( this, false );
+ else if (message.containsEvent ( XMPP::ComposingEvent ) )
+ mManager->receivedTypingMsg ( this, true );
+ else if (message.containsEvent ( XMPP::DisplayedEvent ) )
+ mManager->receivedEventNotification ( i18n("Message has been displayed") );
+ else if (message.containsEvent ( XMPP::DeliveredEvent ) )
+ mManager->receivedEventNotification ( i18n("Message has been delivered") );
+ else if (message.containsEvent ( XMPP::OfflineEvent ) )
+ {
+ mManager->receivedEventNotification( i18n("Message stored on the server, contact offline") );
+ }
+ else if (message.containsEvent ( XMPP::GoneEvent ) )
+ {
+ if(mManager->view( Kopete::Contact::CannotCreate ))
+ { //show an internal message if the user has not already closed his window
+ Kopete::Message m=Kopete::Message ( this, mManager->members(),
+ i18n("%1 has ended their participation in the chat session.").arg(metaContact()->displayName()),
+ Kopete::Message::Internal );
+ m.setImportance(Kopete::Message::Low);
+ mManager->view()->appendMessage ( m ); //use KopeteView::AppendMessage to bypass notifications
+ }
+ }
+ }
+ else
+ // Then here could be event notification requests
+ {
+ mRequestComposingEvent = message.containsEvent ( XMPP::ComposingEvent );
+ mRequestOfflineEvent = message.containsEvent ( XMPP::OfflineEvent );
+ mRequestDeliveredEvent = message.containsEvent ( XMPP::DeliveredEvent );
+ mRequestDisplayedEvent = message.containsEvent ( XMPP::DisplayedEvent);
+ mRequestGoneEvent= message.containsEvent ( XMPP::GoneEvent);
+ }
+ }
+
+ /**
+ * Don't display empty messages, these were most likely just carrying
+ * event notifications or other payload.
+ */
+ if ( message.body().isEmpty () && message.urlList().isEmpty () && message.xHTMLBody().isEmpty() && !message.xencrypted() )
+ return;
+
+ // determine message type
+ if (message.type () == "chat")
+ viewPlugin = "kopete_chatwindow";
+ else
+ viewPlugin = "kopete_emailwindow";
+
+ Kopete::ContactPtrList contactList;
+ contactList.append ( account()->myself () );
+
+ // check for errors
+ if ( message.type () == "error" )
+ {
+ newMessage = new Kopete::Message( message.timeStamp (), this, contactList,
+ i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"").
+ arg ( message.body () ).arg ( message.error().text ),
+ message.subject(), Kopete::Message::Inbound, Kopete::Message::PlainText, viewPlugin );
+ }
+ else
+ {
+ // store message id for outgoing notifications
+ mLastReceivedMessageId = message.id ();
+
+ // retrieve and reformat body
+ QString body = message.body ();
+ QString xHTMLBody;
+ if( !message.xencrypted().isEmpty () )
+ {
+ body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n");
+ }
+ else
+ {
+ xHTMLBody = message.xHTMLBody ();
+ }
+
+ // convert XMPP::Message into Kopete::Message
+ if (!xHTMLBody.isEmpty()) {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Received a xHTML message" << endl;
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList, xHTMLBody,
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::RichText, viewPlugin );
+ }
+ else if ( !body.isEmpty () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Received a plain text message" << endl;
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList, body,
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::PlainText, viewPlugin );
+ }
+ }
+
+ // append message to (eventually new) manager and preselect the originating resource
+ if ( newMessage )
+ {
+ mManager->appendMessage ( *newMessage, message.from().resource () );
+
+ delete newMessage;
+ }
+
+ // append URLs as separate messages
+ if ( !message.urlList().isEmpty () )
+ {
+ /*
+ * We need to copy it here because Iris returns a copy
+ * and we can't work with a returned copy in a for() loop.
+ */
+ XMPP::UrlList urlList = message.urlList();
+
+ for ( XMPP::UrlList::const_iterator it = urlList.begin (); it != urlList.end (); ++it )
+ {
+ QString description = (*it).desc().isEmpty() ? (*it).url() : QStyleSheet::escape ( (*it).desc() );
+ QString url = (*it).url ();
+
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList,
+ QString ( "<a href=\"%1\">%2</a>" ).arg ( url, description ),
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::RichText, viewPlugin );
+
+ mManager->appendMessage ( *newMessage, message.from().resource () );
+
+ delete newMessage;
+ }
+ }
+
+}
+
+void JabberContact::slotCheckVCard ()
+{
+ QDateTime cacheDate;
+ Kopete::ContactProperty cacheDateString = property ( protocol()->propVCardCacheTimeStamp );
+
+ // don't do anything while we are offline
+ if ( !account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ return;
+ }
+
+ if(!mDiscoDone)
+ {
+ if(transport()) //no need to disco if this is a legacy contact
+ mDiscoDone = true;
+ else if(!rosterItem().jid().node().isEmpty())
+ mDiscoDone = true; //contact with an @ are not transport for sure
+ else
+ {
+ mDiscoDone = true; //or it will happen twice, we don't want that.
+ //disco to see if it's not a transport
+ XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask());
+ QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished()));
+ jt->get(rosterItem().jid(), QString());
+ jt->go(true);
+ }
+ }
+
+
+ // avoid warning if key does not exist in configuration file
+ if ( cacheDateString.isNull () )
+ cacheDate = QDateTime::currentDateTime().addDays ( -2 );
+ else
+ cacheDate = QDateTime::fromString ( cacheDateString.value().toString (), Qt::ISODate );
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Cached vCard data for " << contactId () << " from " << cacheDate.toString () << endl;
+
+ if ( !mVCardUpdateInProgress && ( cacheDate.addDays ( 1 ) < QDateTime::currentDateTime () ) )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Scheduling update." << endl;
+
+ mVCardUpdateInProgress = true;
+
+ // current data is older than 24 hours, request a new one
+ QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetTimedVCard () ) );
+ }
+
+}
+
+void JabberContact::slotGetTimedVCard ()
+{
+ mVCardUpdateInProgress = false;
+
+ // check if we are still connected - eventually we lost our connection in the meantime
+ if ( !account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ // we are not connected, discard this update
+ return;
+ }
+
+ if(!mDiscoDone)
+ {
+ if(transport()) //no need to disco if this is a legacy contact
+ mDiscoDone = true;
+ else if(!rosterItem().jid().node().isEmpty())
+ mDiscoDone = true; //contact with an @ are not transport for sure
+ else
+ {
+ //disco to see if it's not a transport
+ XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask());
+ QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished()));
+ jt->get(rosterItem().jid(), QString());
+ jt->go(true);
+ }
+ }
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting vCard for " << contactId () << " from update timer." << endl;
+
+ mVCardUpdateInProgress = true;
+
+ // request vCard
+ XMPP::JT_VCard *task = new XMPP::JT_VCard ( account()->client()->rootTask () );
+ // signal to ourselves when the vCard data arrived
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotGotVCard () ) );
+ task->get ( mRosterItem.jid () );
+ task->go ( true );
+
+}
+
+void JabberContact::slotGotVCard ()
+{
+
+ XMPP::JT_VCard * vCard = (XMPP::JT_VCard *) sender ();
+
+ // update timestamp of last vCard retrieval
+ if ( metaContact() && !metaContact()->isTemporary () )
+ {
+ setProperty ( protocol()->propVCardCacheTimeStamp, QDateTime::currentDateTime().toString ( Qt::ISODate ) );
+ }
+
+ mVCardUpdateInProgress = false;
+
+ if ( !vCard->success() )
+ {
+ /*
+ * A vCard for the user does not exist or the
+ * request was unsuccessful or incomplete.
+ * The timestamp was already updated when
+ * requesting the vCard, so it's safe to
+ * just do nothing here.
+ */
+ return;
+ }
+
+ setPropertiesFromVCard ( vCard->vcard () );
+
+}
+
+void JabberContact::slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
+{
+
+ /*
+ * Checking the last activity only makes sense if a contact is offline.
+ * So, this check should only be done in the following cases:
+ * - Kopete goes online for the first time and this contact is offline, or
+ * - Kopete is already online and this contact went offline.
+ *
+ * Since Kopete already takes care of maintaining the lastSeen property
+ * if the contact changes its state while we are online, we don't need
+ * to query its activity after we are already connected.
+ */
+
+ if ( onlineStatus().isDefinitelyOnline () )
+ {
+ // Kopete already deals with lastSeen if the contact is online
+ return;
+ }
+
+ if ( ( oldStatus.status () == Kopete::OnlineStatus::Connecting ) && newStatus.isDefinitelyOnline () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Scheduling request for last activity for " << mRosterItem.jid().bare () << endl;
+
+ QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetTimedLastActivity () ) );
+ }
+
+}
+
+void JabberContact::slotGetTimedLastActivity ()
+{
+ /*
+ * We have been called from @ref slotCheckLastActivity.
+ * We could have lost our connection in the meantime,
+ * so make sure we are online. Additionally, the contact
+ * itself could have gone online, so make sure it is
+ * still offline. (otherwise the last seen property is
+ * maintained by Kopete)
+ */
+
+ if ( onlineStatus().isDefinitelyOnline () )
+ {
+ // Kopete already deals with setting lastSeen if the contact is online
+ return;
+ }
+
+ if ( account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting last activity from timer for " << mRosterItem.jid().bare () << endl;
+
+ XMPP::JT_GetLastActivity *task = new XMPP::JT_GetLastActivity ( account()->client()->rootTask () );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotGotLastActivity () ) );
+ task->get ( mRosterItem.jid () );
+ task->go ( true );
+ }
+
+}
+
+void JabberContact::slotGotLastActivity ()
+{
+ XMPP::JT_GetLastActivity *task = (XMPP::JT_GetLastActivity *) sender ();
+
+ if ( task->success () )
+ {
+ setProperty ( protocol()->propLastSeen, QDateTime::currentDateTime().addSecs ( -task->seconds () ) );
+ if( !task->message().isEmpty() )
+ {
+ setProperty( protocol()->propAwayMessage, task->message() );
+ }
+ }
+
+}
+
+void JabberContact::slotSendVCard()
+{
+ XMPP::VCard vCard;
+ XMPP::VCard::AddressList addressList;
+ XMPP::VCard::EmailList emailList;
+ XMPP::VCard::PhoneList phoneList;
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ // General information
+ vCard.setNickName (property(protocol()->propNickName).value().toString());
+ vCard.setFullName (property(protocol()->propFullName).value().toString());
+ vCard.setJid (property(protocol()->propJid).value().toString());
+ vCard.setBdayStr (property(protocol()->propBirthday).value().toString());
+ vCard.setTimezone (property(protocol()->propTimezone).value().toString());
+ vCard.setUrl (property(protocol()->propHomepage).value().toString());
+
+ // home address tab
+ XMPP::VCard::Address homeAddress;
+
+ homeAddress.home = true;
+ homeAddress.street = property(protocol()->propHomeStreet).value().toString();
+ homeAddress.extaddr = property(protocol()->propHomeExtAddr).value().toString();
+ homeAddress.pobox = property(protocol()->propHomePOBox).value().toString();
+ homeAddress.locality = property(protocol()->propHomeCity).value().toString();
+ homeAddress.pcode = property(protocol()->propHomePostalCode).value().toString();
+ homeAddress.country = property(protocol()->propHomeCountry).value().toString();
+
+ // work address tab
+ XMPP::VCard::Address workAddress;
+
+ workAddress.work = true;
+ workAddress.street = property(protocol()->propWorkStreet).value().toString();
+ workAddress.extaddr = property(protocol()->propWorkExtAddr).value().toString();
+ workAddress.pobox = property(protocol()->propWorkPOBox).value().toString();
+ workAddress.locality = property(protocol()->propWorkCity).value().toString();
+ workAddress.pcode = property(protocol()->propWorkPostalCode).value().toString();
+ workAddress.country = property(protocol()->propWorkCountry).value().toString();
+
+ addressList.append(homeAddress);
+ addressList.append(workAddress);
+
+ vCard.setAddressList(addressList);
+
+ // home email
+ XMPP::VCard::Email homeEmail;
+
+ homeEmail.home = true;
+ homeEmail.userid = property(protocol()->propEmailAddress).value().toString();
+
+ // work email
+ XMPP::VCard::Email workEmail;
+
+ workEmail.work = true;
+ workEmail.userid = property(protocol()->propWorkEmailAddress).value().toString();
+
+ emailList.append(homeEmail);
+ emailList.append(workEmail);
+
+ vCard.setEmailList(emailList);
+
+ // work information tab
+ XMPP::VCard::Org org;
+ org.name = property(protocol()->propCompanyName).value().toString();
+ org.unit = QStringList::split(",", property(protocol()->propCompanyDepartement).value().toString());
+ vCard.setOrg(org);
+ vCard.setTitle (property(protocol()->propCompanyPosition).value().toString());
+ vCard.setRole (property(protocol()->propCompanyRole).value().toString());
+
+ // phone numbers tab
+ XMPP::VCard::Phone phoneHome;
+ phoneHome.home = true;
+ phoneHome.number = property(protocol()->propPrivatePhone).value().toString();
+
+ XMPP::VCard::Phone phoneWork;
+ phoneWork.work = true;
+ phoneWork.number = property(protocol()->propWorkPhone).value().toString();
+
+ XMPP::VCard::Phone phoneFax;
+ phoneFax.fax = true;
+ phoneFax.number = property(protocol()->propPhoneFax).value().toString();
+
+ XMPP::VCard::Phone phoneCell;
+ phoneCell.cell = true;
+ phoneCell.number = property(protocol()->propPrivateMobilePhone).value().toString();
+
+ phoneList.append(phoneHome);
+ phoneList.append(phoneWork);
+ phoneList.append(phoneFax);
+ phoneList.append(phoneCell);
+
+ vCard.setPhoneList(phoneList);
+
+ // about tab
+ vCard.setDesc(property(protocol()->propAbout).value().toString());
+
+ // Set contact photo as a binary value (if he has set a photo)
+ if( hasProperty( protocol()->propPhoto.key() ) )
+ {
+ QString photoPath = property( protocol()->propPhoto ).value().toString();
+ QImage image( photoPath );
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ image.save( &buffer, "PNG" );
+ vCard.setPhoto( ba );
+ }
+
+ vCard.setVersion("3.0");
+ vCard.setProdId("Kopete");
+
+ XMPP::JT_VCard *task = new XMPP::JT_VCard (account()->client()->rootTask ());
+ // signal to ourselves when the vCard data arrived
+ QObject::connect (task, SIGNAL (finished ()), this, SLOT (slotSentVCard ()));
+ task->set (vCard);
+ task->go (true);
+}
+
+void JabberContact::setPhoto( const QString &photoPath )
+{
+ QImage contactPhoto(photoPath);
+ QString newPhotoPath = photoPath;
+ if(contactPhoto.width() > 96 || contactPhoto.height() > 96)
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ // Scale and crop the picture.
+ contactPhoto = contactPhoto.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 96, 96);
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 96, 96);
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+ else if (contactPhoto.width() < 32 || contactPhoto.height() < 32)
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ // Scale and crop the picture.
+ contactPhoto = contactPhoto.smoothScale( 32, 32, QImage::ScaleMin );
+ // crop image if not square
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 32, 32);
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 32, 32);
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+ else if (contactPhoto.width() != contactPhoto.height())
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, contactPhoto.height(), contactPhoto.height());
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, contactPhoto.height(), contactPhoto.height());
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+
+ setProperty( protocol()->propPhoto, newPhotoPath );
+}
+
+void JabberContact::slotSentVCard ()
+{
+
+}
+
+void JabberContact::slotChatSessionDeleted ( QObject *sender )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Message manager deleted, collecting the pieces..." << endl;
+
+ JabberChatSession *manager = static_cast<JabberChatSession *>(sender);
+
+ mManagers.remove ( mManagers.find ( manager ) );
+
+}
+
+JabberChatSession *JabberContact::manager ( Kopete::ContactPtrList chatMembers, Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << endl;
+
+ Kopete::ChatSession *_manager = Kopete::ChatSessionManager::self()->findChatSession ( account()->myself(), chatMembers, protocol() );
+ JabberChatSession *manager = dynamic_cast<JabberChatSession*>( _manager );
+
+ /*
+ * If we didn't find a message manager for this contact,
+ * instantiate a new one if we are allowed to. (otherwise return 0)
+ */
+ if ( !manager && canCreate )
+ {
+ XMPP::Jid jid = rosterItem().jid();
+
+ /*
+ * If we have no hardwired JID, set any eventually
+ * locked resource as preselected resource.
+ * If there is no locked resource, the resource field
+ * will stay empty.
+ */
+ if ( jid.resource().isEmpty () )
+ jid.setResource ( account()->resourcePool()->lockedResource ( jid ).name () );
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No manager found, creating a new one with resource '" << jid.resource () << "'" << endl;
+
+ manager = new JabberChatSession ( protocol(), static_cast<JabberBaseContact *>(account()->myself()), chatMembers, jid.resource () );
+ connect ( manager, SIGNAL ( destroyed ( QObject * ) ), this, SLOT ( slotChatSessionDeleted ( QObject * ) ) );
+ mManagers.append ( manager );
+ }
+
+ return manager;
+
+}
+
+Kopete::ChatSession *JabberContact::manager ( Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << endl;
+
+ Kopete::ContactPtrList chatMembers;
+ chatMembers.append ( this );
+
+ return manager ( chatMembers, canCreate );
+
+}
+
+JabberChatSession *JabberContact::manager ( const QString &resource, Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << ", Resource: '" << resource << "'" << endl;
+
+ /*
+ * First of all, see if we already have a manager matching
+ * the requested resource or if there are any managers with
+ * an empty resource.
+ */
+ if ( !resource.isEmpty () )
+ {
+ for ( JabberChatSession *mManager = mManagers.first (); mManager; mManager = mManagers.next () )
+ {
+ if ( mManager->resource().isEmpty () || ( mManager->resource () == resource ) )
+ {
+ // we found a matching manager, return this one
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Found an existing message manager for this resource." << endl;
+ return mManager;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No manager found for this resource, creating a new one." << endl;
+
+ /*
+ * If we have come this far, we were either supposed to create
+ * a manager with a preselected resource but have found
+ * no available manager. (not even one with an empty resource)
+ * This means, we will have to create a new one with a
+ * preselected resource.
+ */
+ Kopete::ContactPtrList chatmembers;
+ chatmembers.append ( this );
+ JabberChatSession *manager = new JabberChatSession ( protocol(),
+ static_cast<JabberBaseContact *>(account()->myself()),
+ chatmembers, resource );
+ connect ( manager, SIGNAL ( destroyed ( QObject * ) ), this, SLOT ( slotChatSessionDeleted ( QObject * ) ) );
+ mManagers.append ( manager );
+
+ return manager;
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Resource is empty, grabbing first available manager." << endl;
+
+ /*
+ * The resource is empty, so just return first available manager.
+ */
+ return dynamic_cast<JabberChatSession *>( manager ( canCreate ) );
+
+}
+
+void JabberContact::deleteContact ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing user " << contactId () << endl;
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ /*
+ * Follow the recommendation of
+ * JEP-0162: Best Practices for Roster and Subscription Management
+ * http://www.jabber.org/jeps/jep-0162.html#removal
+ */
+
+ bool remove_from_roster=false;
+
+ if( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From )
+ {
+ int result = KMessageBox::questionYesNoCancel (Kopete::UI::Global::mainWidget(),
+ i18n ( "Do you also want to remove the authorization from user %1 to see your status?" ).
+ arg ( mRosterItem.jid().bare () ), i18n ("Notification"),
+ KStdGuiItem::del (), i18n("Keep"), "JabberRemoveAuthorizationOnDelete" );
+ if(result == KMessageBox::Yes )
+ remove_from_roster = true;
+ else if( result == KMessageBox::Cancel)
+ return;
+ }
+ else if( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::To )
+ remove_from_roster = true;
+
+ if( remove_from_roster )
+ {
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+ rosterTask->remove ( mRosterItem.jid () );
+ rosterTask->go ( true );
+ }
+ else
+ {
+ sendSubscription("unsubscribe");
+
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+ rosterTask->set ( mRosterItem.jid (), QString() , QStringList() );
+ rosterTask->go (true);
+ }
+
+}
+
+void JabberContact::sync ( unsigned int )
+{
+ // if we are offline or this is a temporary contact or we should not synch, don't bother
+ if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () || metaContact() == Kopete::ContactList::self()->myself() )
+ return;
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << contactId () /*<< " - " <<kdBacktrace()*/ << endl;
+
+ if(!m_syncTimer)
+ {
+ m_syncTimer=new QTimer(this);
+ connect(m_syncTimer, SIGNAL(timeout()) , this , SLOT(slotDelayedSync()));
+ }
+ m_syncTimer->start(2*1000,true);
+ /*
+ the sync operation is delayed, because when we are doing a move to group operation,
+ kopete first add the contact to the group, then removes it.
+ Theses two operations should anyway be done in only one pass.
+
+ if there is two jabber contact in one metacontact, this may result in an infinite change of
+ groups between theses two contacts, and the server is being flooded.
+ */
+}
+
+void JabberContact::slotDelayedSync( )
+{
+ m_syncTimer->deleteLater();
+ m_syncTimer=0L;
+ // if we are offline or this is a temporary contact or we should not synch, don't bother
+ if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () )
+ return;
+
+ bool changed=metaContact()->displayName() != mRosterItem.name();
+
+
+ QStringList groups;
+ Kopete::GroupList groupList = metaContact ()->groups ();
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Synchronizing contact " << contactId () << endl;
+
+ for ( Kopete::Group * g = groupList.first (); g; g = groupList.next () )
+ {
+ if ( g->type () != Kopete::Group::TopLevel )
+ groups += g->displayName ();
+ }
+
+ if(mRosterItem.groups() != groups)
+ {
+ changed=true;
+ mRosterItem.setGroups ( groups );
+ }
+
+ if(!changed)
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "contact has not changed, abort sync" << endl;
+ return;
+ }
+
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+
+ rosterTask->set ( mRosterItem.jid (), metaContact()->displayName (), mRosterItem.groups () );
+ rosterTask->go (true);
+
+}
+
+void JabberContact::sendFile ( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ // if the file location is null, then get it from a file open dialog
+ if ( !sourceURL.isValid () )
+ filePath = KFileDialog::getOpenFileName( QString::null , "*", 0L, i18n ( "Kopete File Transfer" ) );
+ else
+ filePath = sourceURL.path(-1);
+
+ QFile file ( filePath );
+
+ if ( file.exists () )
+ {
+ // send the file
+ new JabberFileTransfer ( account (), this, filePath );
+ }
+
+}
+
+
+void JabberContact::slotSendAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "(Re)send auth " << contactId () << endl;
+
+ sendSubscription ("subscribed");
+
+}
+
+void JabberContact::slotRequestAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "(Re)request auth " << contactId () << endl;
+
+ sendSubscription ("subscribe");
+
+}
+
+void JabberContact::slotRemoveAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Remove auth " << contactId () << endl;
+
+ sendSubscription ("unsubscribed");
+
+}
+
+void JabberContact::sendSubscription ( const QString& subType )
+{
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () );
+
+ task->sub ( mRosterItem.jid().full (), subType );
+ task->go ( true );
+
+}
+
+void JabberContact::slotSelectResource ()
+{
+ int currentItem = QString ( static_cast<const KAction *>( sender() )->name () ).toUInt ();
+
+ /*
+ * Warn the user if there is already an active chat window.
+ * The resource selection will only apply for newly opened
+ * windows.
+ */
+ if ( manager ( Kopete::Contact::CannotCreate ) != 0 )
+ {
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Information,
+ i18n ("You have preselected a resource for contact %1, "
+ "but you still have open chat windows for this contact. "
+ "The preselected resource will only apply to newly opened "
+ "chat windows.").arg ( contactId () ),
+ i18n ("Jabber Resource Selector") );
+ }
+
+ if (currentItem == 0)
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing active resource, trusting bestResource()." << endl;
+
+ account()->resourcePool()->removeLock ( rosterItem().jid() );
+ }
+ else
+ {
+ QString selectedResource = static_cast<const KAction *>(sender())->text();
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Moving to resource " << selectedResource << endl;
+
+ account()->resourcePool()->lockToResource ( rosterItem().jid() , XMPP::Resource ( selectedResource ) );
+ }
+
+}
+
+void JabberContact::sendPresence ( const XMPP::Status status )
+{
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ XMPP::Status newStatus = status;
+
+ // honour our priority
+ if(newStatus.isAvailable())
+ newStatus.setPriority ( account()->configGroup()->readNumEntry ( "Priority", 5 ) );
+
+ XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () );
+
+ task->pres ( bestAddress (), newStatus);
+ task->go ( true );
+
+}
+
+
+void JabberContact::slotStatusOnline ()
+{
+
+ XMPP::Status status;
+ status.setShow("");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusChatty ()
+{
+
+ XMPP::Status status;
+ status.setShow ("chat");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusAway ()
+{
+
+ XMPP::Status status;
+ status.setShow ("away");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusXA ()
+{
+
+ XMPP::Status status;
+ status.setShow ("xa");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusDND ()
+{
+
+ XMPP::Status status;
+ status.setShow ("dnd");
+
+ sendPresence ( status );
+
+
+}
+
+void JabberContact::slotStatusInvisible ()
+{
+
+ XMPP::Status status;
+ status.setIsAvailable( false );
+
+ sendPresence ( status );
+
+}
+
+bool JabberContact::isContactRequestingEvent( XMPP::MsgEvent event )
+{
+ if ( event == OfflineEvent )
+ return mRequestOfflineEvent;
+ else if ( event == DeliveredEvent )
+ return mRequestDeliveredEvent;
+ else if ( event == DisplayedEvent )
+ return mRequestDisplayedEvent;
+ else if ( event == ComposingEvent )
+ return mRequestComposingEvent;
+ else if ( event == CancelEvent )
+ return mRequestComposingEvent;
+ else if ( event == GoneEvent )
+ return mRequestGoneEvent;
+ else
+ return false;
+}
+
+QString JabberContact::lastReceivedMessageId () const
+{
+ return mLastReceivedMessageId;
+}
+
+void JabberContact::voiceCall( )
+{
+#ifdef SUPPORT_JINGLE
+ Jid jid = mRosterItem.jid();
+
+ // It's honour lock by default.
+ JabberResource *bestResource = account()->resourcePool()->bestJabberResource( jid );
+ if( bestResource )
+ {
+ if( jid.resource().isEmpty() )
+ {
+ // If the jid resource is empty, get the JID from best resource for this contact.
+ jid = bestResource->jid();
+ }
+
+ // Check if the voice caller exist and the current resource support voice.
+ if( account()->voiceCaller() && bestResource->features().canVoice() )
+ {
+ JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( jid, account()->voiceCaller() );
+ voiceDialog->show();
+ voiceDialog->start();
+ }
+#if 0
+ if( account()->sessionManager() && bestResource->features().canVoice() )
+ {
+ JingleVoiceSession *session = static_cast<JingleVoiceSession*>(account()->sessionManager()->createSession("http://www.google.com/session/phone", jid));
+
+ JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog(session);
+ voiceDialog->show();
+ voiceDialog->start();
+ }
+#endif
+ }
+ else
+ {
+ // Shouldn't never go there.
+ }
+#endif
+}
+
+void JabberContact::slotDiscoFinished( )
+{
+ mDiscoDone = true;
+ JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
+
+ bool is_transport=false;
+ QString tr_type;
+
+ if ( jt->success() )
+ {
+ QValueList<XMPP::DiscoItem::Identity> identities = jt->item().identities();
+ QValueList<XMPP::DiscoItem::Identity>::Iterator it;
+ for ( it = identities.begin(); it != identities.end(); ++it )
+ {
+ XMPP::DiscoItem::Identity ident=*it;
+ if(ident.category == "gateway")
+ {
+ is_transport=true;
+ tr_type=ident.type;
+ //name=ident.name;
+
+ break; //(we currently only support gateway)
+ }
+ else if (ident.category == "service")
+ {
+ //The ApaSMSAgent is reporting itself as service (instead of gateway) which is broken.
+ //we anyway support it. See bug 127811
+ if(ident.type == "sms")
+ {
+ is_transport=true;
+ tr_type=ident.type;
+ }
+ }
+ }
+ }
+
+ if(is_transport && !transport())
+ { //ok, we are not a contact, we are a transport....
+
+ XMPP::RosterItem ri = rosterItem();
+ Kopete::MetaContact *mc=metaContact();
+ JabberAccount *parentAccount=account();
+ Kopete::OnlineStatus status=onlineStatus();
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << ri.jid().full() << " is not a contact but a gateway - " << this << endl;
+
+ if( Kopete::AccountManager::self()->findAccount( protocol()->pluginId() , account()->accountId() + "/" + ri.jid().bare() ) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "oops, transport already exists, abort operation " << endl;
+ return;
+ }
+
+ delete this; //we are not a contact i said !
+
+ if(mc->contacts().count() == 0)
+ Kopete::ContactList::self()->removeMetaContact( mc );
+
+ //we need to create the transport when 'this' is already deleted, so transport->myself() will not conflict with it
+ JabberTransport *transport = new JabberTransport( parentAccount , ri , tr_type );
+ if(!Kopete::AccountManager::self()->registerAccount( transport ))
+ return;
+ transport->myself()->setOnlineStatus( status ); //push back the online status
+ return;
+ }
+}
+
+
+
+#include "jabbercontact.moc"