File indexing completed on 2024-04-21 04:04:43

0001  /*
0002   *   jabberprotocol.cpp  -  Base class for the Kopete Jabber protocol
0003   *
0004   * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
0005   * Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
0006   * Copyright (c) 2006      by Olivier Goffart  <ogoffart at kde.org>
0007   *
0008   *  Kopete   (c) by the Kopete developers  <kopete-devel@kde.org>
0009   *
0010   * *************************************************************************
0011   * *                                                                       *
0012   * * This program is free software; you can redistribute it and/or modify  *
0013   * * it under the terms of the GNU General Public License as published by  *
0014   * * the Free Software Foundation; either either version 2
0015    of the License, or (at your option) any later version.of the License, or     *
0016   * * (at your option) any later version.                                   *
0017   * *                                                                       *
0018   * *************************************************************************
0019   */
0020 
0021 #include "jabber_protocol_debug.h"
0022 
0023 #include <KGenericFactory>
0024 #include <KConfig>
0025 
0026 #include <QTimer>
0027 #include <QStringList>
0028 #include <QList>
0029 
0030 #include "im.h"
0031 #include "xmpp.h"
0032 
0033 #include <sys/utsname.h>
0034 
0035 // #include "kopetecontact.h"
0036 // #include "kopetecontactlist.h"
0037 // #include "kopetemetacontact.h"
0038 // #include "kopetechatsession.h"
0039 // #include "kopeteonlinestatusmanager.h"
0040 // #include "kopeteglobal.h"
0041 // #include "kopeteprotocol.h"
0042 // #include "kopeteplugin.h"
0043 // #include "kopeteaccountmanager.h"
0044 // #include "addcontactpage.h"
0045 // #include "kopetecommandhandler.h"
0046 
0047 #include "jabbercontact.h"
0048 #include "jabberaddcontactpage.h"
0049 #include "jabberprotocol.h"
0050 #include "jabberaccount.h"
0051 #include "jabbereditaccountwidget.h"
0052 #include "jabbercapabilitiesmanager.h"
0053 #include "jabbertransport.h"
0054 #include "dlgjabberservices.h"
0055 #include "dlgjabberchatjoin.h"
0056 #include "dlgregister.h"
0057 
0058 JabberProtocol *JabberProtocol::protocolInstance = 0;
0059 
0060 K_PLUGIN_FACTORY( JabberProtocolFactory, registerPlugin<JabberProtocol>(); )
0061 K_EXPORT_PLUGIN( JabberProtocolFactory( "kopete_jabber" ) )
0062 
0063 JabberProtocol::JabberProtocol (QObject * parent)
0064 : QObject(parent)
0065 {
0066 
0067     qCDebug(JABBER_PROTOCOL_LOG) << "[JabberProtocol] Loading ...";
0068 
0069     /* This is meant to be a singleton, so we will check if we have
0070      * been loaded before. */
0071     if (protocolInstance)
0072     {
0073         qCDebug(JABBER_PROTOCOL_LOG) << "[JabberProtocol] Warning: Protocol already " << "loaded, not initializing again.";
0074         return;
0075     }
0076 
0077     protocolInstance = this;
0078 
0079     // Init the Entity Capabilities manager.
0080     capsManager = new JabberCapabilitiesManager;
0081     capsManager->loadCachedInformation();
0082     
0083     registerAsProtocolHandler(QString::fromLatin1("xmpp"));
0084 }
0085 
0086 JabberProtocol::~JabberProtocol ()
0087 {
0088     //disconnectAll();
0089 
0090     delete capsManager;
0091     capsManager = 0L;
0092 
0093     /* make sure that the next attempt to load Jabber
0094      * re-initializes the protocol class. */
0095     protocolInstance = 0L;
0096 }
0097 
0098 
0099 
0100 AddContactPage *JabberProtocol::createAddContactWidget (QWidget * parent, Kopete::Account * i)
0101 {
0102     qCDebug(JABBER_PROTOCOL_LOG) << "Create Add Contact  Widget";
0103     return new JabberAddContactPage (i, parent);
0104 }
0105 
0106 KopeteEditAccountWidget *JabberProtocol::createEditAccountWidget (Kopete::Account * account, QWidget * parent)
0107 {
0108     qCDebug(JABBER_PROTOCOL_LOG) << "Edit Account Widget";
0109     JabberAccount *ja=dynamic_cast < JabberAccount * >(account);
0110     if(ja || !account)
0111         return new JabberEditAccountWidget (this,ja , parent);
0112     else
0113     {
0114         JabberTransport *transport = dynamic_cast < JabberTransport * >(account);
0115         if(!transport || !transport->account()->client() )
0116             return 0L;
0117         dlgRegister *registerDialog = new dlgRegister (transport->account(), transport->myself()->contactId());
0118         registerDialog->show (); 
0119         registerDialog->raise ();
0120         return 0l; //we make ourself our own dialog, not an editAccountWidget.
0121     }
0122 }
0123 
0124 Kopete::Account *JabberProtocol::createNewAccount (const QString & accountId)
0125 {
0126     qCDebug(JABBER_PROTOCOL_LOG) << "Create New Account. ID: " << accountId;
0127     if( Kopete::AccountManager::self()->findAccount( pluginId() , accountId ) )
0128         return 0L;  //the account may already exist if greated just above
0129 
0130     int slash=accountId.indexOf('/');
0131     if(slash>=0)
0132     {
0133         QString realAccountId=accountId.left(slash);
0134         JabberAccount *realAccount=dynamic_cast<JabberAccount*>(Kopete::AccountManager::self()->findAccount( pluginId() , realAccountId ));
0135         if(!realAccount) //if it doesn't exist yet, create it
0136         {
0137             realAccount = new JabberAccount( this, realAccountId );
0138             if(!Kopete::AccountManager::self()->registerAccount(  realAccount ) )
0139                 return 0L;
0140         }
0141         if(!realAccount)
0142             return 0L;
0143         return new JabberTransport( realAccount , accountId );
0144     }
0145     else
0146     {
0147         return new JabberAccount (this, accountId);
0148     }
0149 }
0150 
0151 Kopete::OnlineStatus JabberProtocol::resourceToKOS ( const XMPP::Resource &resource )
0152 {
0153 
0154     // update to offline by default
0155     Kopete::OnlineStatus status = JabberKOSOffline;
0156 
0157     if ( !resource.status().isAvailable () )
0158     {
0159         // resource is offline
0160         status = JabberKOSOffline;
0161     }
0162     else
0163     {
0164         if (resource.status ().show ().isEmpty ())
0165         {
0166             if (resource.status ().isInvisible ())
0167             {
0168                 status = JabberKOSInvisible;
0169             }
0170             else
0171             {
0172                 status = JabberKOSOnline;
0173             }
0174         }
0175         else
0176         if (resource.status ().show () == "chat")
0177         {
0178             status = JabberKOSChatty;
0179         }
0180         else if (resource.status ().show () == "away")
0181         {
0182             status = JabberKOSAway;
0183         }
0184         else if (resource.status ().show () == "xa")
0185         {
0186             status = JabberKOSXA;
0187         }
0188         else if (resource.status ().show () == "dnd")
0189         {
0190             status = JabberKOSDND;
0191         }
0192         else if (resource.status ().show () == "online")
0193         { // the ApaSMSAgent sms gateway report status as "online" even if it's not in the RFC 3921 � 2.2.2.1 
0194             // See Bug 129059
0195             status = JabberKOSOnline;
0196         }
0197         else if (resource.status ().show () == "connecting")
0198         { // this is for kopete internals
0199             status = JabberKOSConnecting;
0200         }
0201         else
0202         {
0203             qCDebug(JABBER_PROTOCOL_LOG) << "Unknown status <show>" << resource.status ().show () << "</show> for contact. One of your contact is probably using a broken client, ask him to report a bug";
0204         }
0205     }
0206 
0207     return status;
0208 
0209 }
0210 
0211 JabberCapabilitiesManager *JabberProtocol::capabilitiesManager()
0212 {
0213     return capsManager;
0214 }
0215 
0216 JabberProtocol *JabberProtocol::protocol ()
0217 {
0218     // return current instance
0219     return protocolInstance;
0220 }
0221 
0222 Kopete::Contact *JabberProtocol::deserializeContact (Kopete::MetaContact * metaContact,
0223                                          const QMap < QString, QString > &serializedData, const QMap < QString, QString > & /* addressBookData */ )
0224 {
0225 //  qCDebug(JABBER_PROTOCOL_LOG) << "Deserializing data for metacontact " << metaContact->displayName () << "\n";
0226 
0227     QString contactId = serializedData["contactId"];
0228     QString displayName = serializedData["displayName"];
0229     QString accountId = serializedData["accountId"];
0230     QString jid = serializedData["JID"];
0231 
0232     QList<Kopete::Account*> accounts = Kopete::AccountManager::self ()->accounts (this);
0233     Kopete::Account *account = 0;
0234     QList<Kopete::Account*>::Iterator accountIt, accountItEnd = accounts.end();
0235     for(accountIt = accounts.begin(); accountIt != accountItEnd; ++accountIt)
0236     {
0237         if((*accountIt)->accountId() == accountId)
0238             account = *accountIt;
0239     }
0240 
0241     if (!account)
0242     {
0243         qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: Account for contact does not exist, skipping.";
0244         return 0;
0245     }
0246     
0247     JabberTransport *transport = dynamic_cast<JabberTransport*>(account);
0248     if( transport )
0249         transport->account()->addContact ( jid.isEmpty() ? contactId : jid ,  metaContact);
0250     else
0251         account->addContact (contactId,  metaContact);
0252     return account->contacts()[contactId];
0253 }
0254 
0255 XMPP::Status JabberProtocol::kosToStatus( const Kopete::OnlineStatus & status , const QString & message )
0256 {
0257     XMPP::Status xmppStatus ( "", message );
0258 
0259     if( status.status() == Kopete::OnlineStatus::Offline )
0260     {
0261         xmppStatus.setIsAvailable( false );
0262     }
0263 
0264     switch ( status.internalStatus () )
0265     {
0266         case JabberProtocol::JabberFreeForChat:
0267             xmppStatus.setShow ( "chat" );
0268             break;
0269 
0270         case JabberProtocol::JabberOnline:
0271             xmppStatus.setShow ( "" );
0272             break;
0273 
0274         case JabberProtocol::JabberAway:
0275             xmppStatus.setShow ( "away" );
0276             break;
0277 
0278         case JabberProtocol::JabberXA:
0279             xmppStatus.setShow ( "xa" );
0280             break;
0281 
0282         case JabberProtocol::JabberDND:
0283             xmppStatus.setShow ( "dnd" );
0284             break;
0285 
0286         case JabberProtocol::JabberInvisible:
0287             xmppStatus.setIsInvisible ( true );
0288             break;
0289     }
0290     return xmppStatus;
0291 }
0292 
0293 #include <accountselector.h>
0294 #include <kopeteuiglobal.h>
0295 #include <KVBox>
0296 #include "jabbercontactpool.h"
0297 #include <kopeteview.h>
0298 
0299 #include <KMessageBox>
0300 #include <KInputDialog>
0301 
0302 void JabberProtocol::handleURL(const KUrl & kurl) const
0303 {
0304     QUrl url=kurl; //QUrl has better query handling.
0305     if(url.scheme() != "xmpp" && !url.scheme().isEmpty() )
0306         return;
0307 
0308     url.setQueryDelimiters( '=' , ';' );
0309     QString accountid=url.authority();
0310     QString jid_str=url.path();
0311     if(jid_str.startsWith('/'))
0312         jid_str=jid_str.mid(1);
0313     XMPP::Jid jid = jid_str;
0314     QString action=url.queryItems().isEmpty() ? QString() : url.queryItems().first().first;
0315      
0316     qCDebug(JABBER_PROTOCOL_LOG) << url.queryItemValue("body");
0317 
0318     if(jid.isEmpty())
0319     {
0320         return;
0321     }
0322     
0323     JabberAccount *account=0L;
0324     if(!accountid.isEmpty())
0325     {
0326         account=static_cast<JabberAccount*>(Kopete::AccountManager::self()->findAccount("JabberProtocol" , accountid));
0327     }
0328     if(!account)
0329     {
0330         QList<Kopete::Account*> accounts = Kopete::AccountManager::self()->accounts(const_cast<JabberProtocol*>(this));
0331         if (accounts.count() == 1)
0332             account = static_cast<JabberAccount*>(accounts.first());
0333         else
0334         {
0335             KDialog chooser(Kopete::UI::Global::mainWidget());
0336             chooser.setCaption( i18n("Choose Account") );
0337             chooser.setButtons( KDialog::Ok | KDialog::Cancel );
0338             chooser.setDefaultButton(KDialog::Ok);
0339             KVBox vb(&chooser);
0340             chooser.setMainWidget(&vb);
0341             QLabel label(&vb);
0342             label.setText(i18n("Choose an account to handle the URL %1" , kurl.prettyUrl()));
0343 //          label.setSizePolicy(QSizePolicy::Minimum , QSizePolicy::MinimumExpanding);
0344             label.setWordWrap(true);
0345             AccountSelector accSelector(const_cast<JabberProtocol*>(this), &vb);
0346     //      accSelector.setSizePolicy(QSizePolicy::MinimumExpanding , QSizePolicy::MinimumExpanding);
0347             int ret = chooser.exec();
0348             account=qobject_cast<JabberAccount*>(accSelector.selectedItem());
0349             if (ret == QDialog::Rejected || account == 0)
0350                 return;
0351         }
0352     }
0353     
0354     if(jid.isEmpty())
0355     {
0356         return;
0357     }
0358     
0359     JabberBaseContact *contact=account->contactPool()->findRelevantRecipient( jid );
0360         
0361     if(action.isEmpty() || action=="message")
0362     {
0363         if(!contact)
0364         {
0365             Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
0366             metaContact->setTemporary (true);
0367             contact = account->contactPool()->addContact ( XMPP::RosterItem ( jid ), metaContact, false );
0368             Kopete::ContactList::self()->addMetaContact(metaContact);
0369         }
0370         contact->execute();
0371         
0372         if(url.hasQueryItem("body") || url.hasQueryItem("subject"))
0373         { //TODO "thread"
0374             Kopete::ChatSession *kcs=contact->manager(Kopete::Contact::CanCreate);
0375             if(!kcs)
0376                 return;
0377 
0378             Kopete::Message msg(account->myself(), kcs->members());
0379             msg.setPlainBody( url.queryItemValue("body") );
0380             msg.setSubject( url.queryItemValue("subject") );
0381             msg.setDirection( Kopete::Message::Outbound );
0382 
0383             KopeteView *kv=kcs->view(true);
0384             if(kv)
0385                 kv->setCurrentMessage(msg);
0386         }
0387     }
0388     else if(action == "roster" || action == "subscribe")
0389     {
0390         if (KMessageBox::questionTwoActions(Kopete::UI::Global::mainWidget(),
0391             i18n("Do you want to add '%1' to your contact list?", jid.full()),
0392             QString(), KGuiItem( i18n("Add") ), KGuiItem( i18n("Do Not Add") ))
0393                   != KMessageBox::PrimaryAction)
0394         {
0395             return;
0396         }
0397         Kopete::Group *group=0L;
0398         if( url.hasQueryItem("group"))
0399             group=Kopete::ContactList::self()->findGroup( url.queryItemValue("group") );
0400         account->addContact( jid.full() ,  url.queryItemValue("name") , group );
0401     }
0402     else if(action == "remove" || action== "unsubscribe")
0403     {
0404         if(!contact)
0405             return;
0406     
0407         if (KMessageBox::questionTwoActions(Kopete::UI::Global::mainWidget(),
0408             i18n("Do you want to remove '%1' from your contact list?", jid.full()),
0409             QString(), KGuiItem( i18n("Remove") ), KGuiItem( i18n("Do Not Remove") ))
0410                         != KMessageBox::PrimaryAction)
0411         {
0412             return;
0413         }
0414         
0415         contact->deleteContact();
0416     }//TODO: probe
0417     else if(action == "join" || action=="invite")
0418     {
0419         if(!account->isConnected() || !account->client())
0420         {
0421             account->errorConnectFirst();
0422             return;
0423         }
0424         
0425         if(!contact)
0426         {
0427             QString nick=jid.resource();
0428             if(nick.isEmpty())
0429             {
0430                 bool ok=true;
0431                 nick = KInputDialog::getText(i18n("Please enter your nickname for the room %1", jid.bare()),
0432                         i18n("Provide your nickname"),
0433                         QString(),
0434                         &ok);
0435                 if (!ok)
0436                     return;
0437             }
0438             if(action=="join" && url.hasQueryItem( QLatin1String( "password" )))
0439                 account->client()->joinGroupChat( jid.host() , jid.user() , nick, url.queryItemValue("password") );
0440             else
0441                 account->client()->joinGroupChat( jid.host() , jid.user() , nick );
0442         }
0443         
0444         if(action=="invite" && url.hasQueryItem("jid") )
0445         {
0446             //NOTE: this is the obsolete, NOT RECOMMENDED protocol.
0447             //      iris doesn't implement groupchat yet
0448             //NOTE: This code is duplicated in JabberGroupChatManager::inviteContact
0449             XMPP::Message jabberMessage;
0450             XMPP::Jid jid = static_cast<const JabberBaseContact*>(account->myself())->rosterItem().jid() ;
0451             jabberMessage.setFrom ( jid );
0452             jabberMessage.setTo ( url.queryItemValue("jid") );
0453             jabberMessage.setInvite( jid.bare() );
0454             jabberMessage.setBody( i18n("You have been invited to %1", jid.bare() ) );
0455 
0456             // send the message
0457             account->client()->sendMessage ( jabberMessage );
0458         }
0459     }
0460     else if(action=="sendfile")
0461     {
0462         if(!contact)
0463         {
0464             Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
0465             metaContact->setTemporary (true);
0466             contact = account->contactPool()->addContact ( XMPP::RosterItem ( jid ), metaContact, false );
0467             Kopete::ContactList::self()->addMetaContact(metaContact);
0468         }
0469         contact->sendFile();
0470     }//TODO: recvfile
0471     else
0472     {
0473         qCWarning(JABBER_PROTOCOL_LOG) << "unable to handle URL "<< kurl.prettyUrl();
0474     }
0475 
0476 }
0477 
0478 #include "moc_jabberprotocol.cpp"