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"