File indexing completed on 2024-04-21 04:04:43
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@kde.org> 0006 * 0007 * Kopete (c) 2002-2007 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 0020 #include "jabbergroupcontact.h" 0021 #include "jabber_protocol_debug.h" 0022 0023 #include <KLocalizedString> 0024 #include <KFileDialog> 0025 #include <KInputDialog> 0026 #include <QAction> 0027 0028 #include "jabberprotocol.h" 0029 #include "jabberaccount.h" 0030 #include "jabberclient.h" 0031 #include "jabberfiletransfer.h" 0032 #include "jabbergroupchatmanager.h" 0033 #include "jabbergroupmembercontact.h" 0034 #include "jabbercontactpool.h" 0035 #include "kopetemetacontact.h" 0036 #include "xmpp_tasks.h" 0037 0038 /** 0039 * JabberGroupContact constructor 0040 */ 0041 JabberGroupContact::JabberGroupContact (const XMPP::RosterItem &rosterItem, JabberAccount *account, Kopete::MetaContact * mc) 0042 : JabberBaseContact ( XMPP::RosterItem ( rosterItem.jid().userHost () ), account, mc) , mNick( rosterItem.jid().resource() ) 0043 { 0044 setIcon( "jabber_group" ); 0045 0046 // initialize here, we need it set before we instantiate the manager below 0047 mManager = 0; 0048 0049 setFileCapable ( false ); 0050 0051 /** 0052 * Add our own nick as first subcontact (we need to do that here 0053 * because we need to set this contact as myself() of the message 0054 * manager). 0055 */ 0056 mSelfContact = addSubContact ( rosterItem ); 0057 0058 /** 0059 * Instantiate a new message manager without members. 0060 */ 0061 mManager = new JabberGroupChatManager ( protocol (), mSelfContact, 0062 Kopete::ContactPtrList (), XMPP::Jid ( rosterItem.jid().userHost () ) ); 0063 0064 connect ( mManager, SIGNAL (closing(Kopete::ChatSession*)), this, SLOT (slotChatSessionDeleted()) ); 0065 0066 connect ( account->myself() , SIGNAL(onlineStatusChanged(Kopete::Contact*,Kopete::OnlineStatus,Kopete::OnlineStatus)) , 0067 this , SLOT(slotStatusChanged()) ) ; 0068 0069 /** 0070 * FIXME: The first contact in the list of the message manager 0071 * needs to be our own contact. This is a flaw in the Kopete 0072 * API because it can't deal with groupchat properly. 0073 * If we are alone in a room, we are myself() already and members() 0074 * is empty. This makes at least the history plugin crash. 0075 */ 0076 mManager->addContact ( this ); 0077 0078 0079 0080 /** 0081 * Let's construct the window: 0082 * otherwise, the ref count of maznager is equal to zero. 0083 * and if we receive a message before the window is shown, 0084 * it will be deleted and we will be out of the channel 0085 * In all case, there are no reason to don't show it. 0086 */ 0087 mManager->view( true , "kopete_chatwindow" ); 0088 } 0089 0090 JabberGroupContact::~JabberGroupContact () 0091 { 0092 0093 qCDebug(JABBER_PROTOCOL_LOG) ; 0094 0095 if(mManager) 0096 { 0097 mManager->deleteLater(); 0098 } 0099 0100 foreach ( Kopete::Contact *contact, mContactList ) 0101 { 0102 /*if(mManager) 0103 mManager->removeContact( contact );*/ 0104 qCDebug(JABBER_PROTOCOL_LOG) << "Deleting KC " << contact->contactId (); 0105 contact->deleteLater(); 0106 } 0107 0108 foreach ( Kopete::MetaContact *metaContact, mMetaContactList ) 0109 { 0110 qCDebug(JABBER_PROTOCOL_LOG) << "Deleting KMC " << metaContact->metaContactId (); 0111 metaContact->deleteLater(); 0112 } 0113 } 0114 0115 QList<QAction *> *JabberGroupContact::customContextMenuActions () 0116 { 0117 QList<QAction *> *actionCollection = new QList<QAction*>(); 0118 0119 QAction *actionSetNick = new QAction(this); 0120 actionSetNick->setText( i18n ("Change nickname") ); 0121 actionSetNick->setIcon( QIcon::fromTheme(QLatin1String( "jabber_changenick" )) ); 0122 connect(actionSetNick, SIGNAL(triggered(bool)), this, SLOT(slotChangeNick())); 0123 0124 actionCollection->append( actionSetNick ); 0125 0126 return actionCollection; 0127 } 0128 0129 Kopete::ChatSession *JabberGroupContact::manager ( Kopete::Contact::CanCreateFlags canCreate ) 0130 { 0131 if(!mManager && canCreate == Kopete::Contact::CanCreate) 0132 { 0133 qCWarning(JABBER_PROTOCOL_LOG) << "somehow, the chat manager was removed, and the contact is still there"; 0134 mManager = new JabberGroupChatManager ( protocol (), mSelfContact, 0135 Kopete::ContactPtrList (), XMPP::Jid ( rosterItem().jid().userHost() ) ); 0136 0137 mManager->addContact ( this ); 0138 0139 connect ( mManager, SIGNAL (closing(Kopete::ChatSession*)), this, SLOT (slotChatSessionDeleted()) ); 0140 0141 //if we have to recreate the manager, we probably have to connect again to the chat. 0142 slotStatusChanged(); 0143 } 0144 return mManager; 0145 0146 } 0147 0148 void JabberGroupContact::handleIncomingMessage (const XMPP::Message & message) 0149 { 0150 // message type is always chat in a groupchat 0151 QString viewType = "kopete_chatwindow"; 0152 Kopete::Message *newMessage = 0L; 0153 0154 qCDebug(JABBER_PROTOCOL_LOG) << "Received a message"; 0155 0156 /** 0157 * Don't display empty messages, these were most likely just carrying 0158 * event notifications or other payload. 0159 */ 0160 if ( message.body().isEmpty () ) 0161 return; 0162 0163 manager(CanCreate); //force to create mManager 0164 0165 Kopete::ContactPtrList contactList = manager()->members(); 0166 0167 // check for errors 0168 if ( message.type () == "error" ) 0169 { 0170 newMessage = new Kopete::Message( this, contactList ); 0171 newMessage->setPlainBody( i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"", 0172 message.body (), message.error().text ) ); 0173 newMessage->setTimestamp( message.timeStamp() ); 0174 newMessage->setSubject( message.subject() ); 0175 newMessage->setDirection( Kopete::Message::Inbound ); 0176 newMessage->setRequestedPlugin( viewType ); 0177 } 0178 else 0179 { 0180 // retrieve and reformat body 0181 QString body = message.body (); 0182 0183 if( !message.xencrypted().isEmpty () ) 0184 { 0185 body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n"); 0186 } 0187 0188 // locate the originating contact 0189 JabberBaseContact *subContact = account()->contactPool()->findExactMatch ( message.from () ); 0190 0191 if ( !subContact ) 0192 { 0193 qCWarning(JABBER_PROTOCOL_LOG) << "the contact is not in the list : " << message.from().full(); 0194 return; 0195 /** 0196 * We couldn't find the contact for this message. That most likely means 0197 * that it originated from a history backlog or something similar and 0198 * the sending person is not in the channel anymore. We need to create 0199 * a new contact for this which does not show up in the manager. 0200 */ 0201 subContact = addSubContact ( XMPP::RosterItem ( message.from () ), false ); 0202 } 0203 0204 // convert XMPP::Message into Kopete::Message 0205 newMessage = new Kopete::Message ( subContact, contactList ); 0206 newMessage->setDirection( subContact != mManager->myself() ? Kopete::Message::Inbound : Kopete::Message::Outbound ); 0207 newMessage->setTimestamp( message.timeStamp() ); 0208 newMessage->setPlainBody( body ); 0209 newMessage->setRequestedPlugin( viewType ); 0210 } 0211 0212 // append message to manager 0213 mManager->appendMessage ( *newMessage ); 0214 0215 delete newMessage; 0216 0217 } 0218 0219 JabberBaseContact *JabberGroupContact::addSubContact ( const XMPP::RosterItem &rosterItem, bool addToManager ) 0220 { 0221 qCDebug(JABBER_PROTOCOL_LOG) << "Adding new subcontact " << rosterItem.jid().full () << " to room " << mRosterItem.jid().full (); 0222 0223 // see if this contact already exists, skip creation otherwise 0224 JabberBaseContact *subContact = dynamic_cast<JabberGroupMemberContact *>( account()->contactPool()->findExactMatch ( rosterItem.jid () ) ); 0225 0226 if ( subContact ) 0227 { 0228 qCDebug(JABBER_PROTOCOL_LOG) << "Contact already exists, not adding again."; 0229 return subContact; 0230 } 0231 0232 // Create new meta contact that holds the groupchat contact. 0233 Kopete::MetaContact *metaContact = new Kopete::MetaContact (); 0234 metaContact->setTemporary ( true ); 0235 mMetaContactList.append ( metaContact ); 0236 0237 // now add contact to the pool, no dirty flag 0238 subContact = account()->contactPool()->addGroupContact ( rosterItem, false, metaContact, false ); 0239 0240 /** 0241 * Add the contact to our message manager first. We need 0242 * to check the pointer for validity, because this method 0243 * gets called from the constructor, where the manager 0244 * does not exist yet. 0245 */ 0246 if ( mManager && addToManager ) 0247 mManager->addContact ( subContact ); 0248 0249 // now, add the contact also to our own list 0250 mContactList.append ( subContact ); 0251 0252 connect(subContact , SIGNAL(contactDestroyed(Kopete::Contact*)) , this , SLOT(slotSubContactDestroyed(Kopete::Contact*))); 0253 0254 return subContact; 0255 0256 } 0257 0258 void JabberGroupContact::removeSubContact ( const XMPP::RosterItem &rosterItem ) 0259 { 0260 qCDebug(JABBER_PROTOCOL_LOG) << "Removing subcontact " << rosterItem.jid().full () << " from room " << mRosterItem.jid().full (); 0261 0262 // make sure that subcontacts are only removed from the room contact, which has no resource 0263 if ( !mRosterItem.jid().resource().isEmpty () ) 0264 { 0265 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: Trying to remove subcontact from subcontact!"; 0266 return; 0267 } 0268 0269 // find contact in the pool 0270 JabberGroupMemberContact *subContact = dynamic_cast<JabberGroupMemberContact *>( account()->contactPool()->findExactMatch ( rosterItem.jid () ) ); 0271 0272 if ( !subContact ) 0273 { 0274 qCDebug(JABBER_PROTOCOL_LOG) << "WARNING: Subcontact couldn't be located!"; 0275 return; 0276 } 0277 0278 if(mManager && subContact->contactId() == mManager->myself()->contactId() ) 0279 { 0280 //HACK WORKAROUND FIXME KDE4 0281 //impossible to remove myself, or we will die 0282 //subContact->setNickName( mNick ); //this is even worse than nothing 0283 return; 0284 } 0285 0286 // remove the contact from the message manager first 0287 if(mManager) 0288 mManager->removeContact ( subContact ); 0289 0290 // remove the contact's meta contact from our internal list 0291 mMetaContactList.removeAll ( subContact->metaContact () ); 0292 0293 // remove the contact from our internal list 0294 mContactList.removeAll ( subContact ); 0295 0296 // delete the meta contact first 0297 delete subContact->metaContact (); 0298 0299 // finally, delete the contact itself from the pool 0300 account()->contactPool()->removeContact ( rosterItem.jid () ); 0301 0302 } 0303 0304 void JabberGroupContact::sendFile ( const KUrl &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ ) 0305 { 0306 QString filePath; 0307 0308 // if the file location is null, then get it from a file open dialog 0309 if ( !sourceURL.isValid () ) 0310 filePath = KFileDialog::getOpenFileName( KUrl(), "*", 0L, i18n ( "Kopete File Transfer" ) ); 0311 else 0312 filePath = sourceURL.path(KUrl::RemoveTrailingSlash); 0313 0314 QFile file ( filePath ); 0315 0316 if ( file.exists () ) 0317 { 0318 // send the file 0319 new JabberFileTransfer ( account (), this, filePath ); 0320 } 0321 0322 } 0323 0324 void JabberGroupContact::slotChatSessionDeleted () 0325 { 0326 0327 mManager = 0; 0328 0329 if ( account()->isConnected () ) 0330 { 0331 account()->client()->leaveGroupChat ( mRosterItem.jid().host (), mRosterItem.jid().user () ); 0332 } 0333 0334 //deleteLater(); //we will be deleted later when the account will know we have left 0335 0336 } 0337 0338 void JabberGroupContact::slotStatusChanged( ) 0339 { 0340 if( !account()->isConnected() ) 0341 { 0342 //we need to remove all contact, because when we connect again, we will not receive the notificaion they are gone. 0343 QList<Kopete::Contact*> copy_contactlist=mContactList; 0344 foreach ( Kopete::Contact *contact, copy_contactlist ) 0345 { 0346 removeSubContact( XMPP::Jid(contact->contactId()) ); 0347 } 0348 return; 0349 } 0350 0351 0352 if( !isOnline() ) 0353 { 0354 //HACK WORKAROUND XMPP::client->d->groupChatList must contains us. 0355 account()->client()->joinGroupChat( rosterItem().jid().host() , rosterItem().jid().user() , mNick ); 0356 } 0357 0358 //TODO: away message 0359 XMPP::Status newStatus = account()->protocol()->kosToStatus( account()->myself()->onlineStatus() ); 0360 account()->client()->setGroupChatStatus( rosterItem().jid().host() , rosterItem().jid().user() , newStatus ); 0361 } 0362 0363 void JabberGroupContact::slotChangeNick( ) 0364 { 0365 0366 bool ok; 0367 QString futureNewNickName = KInputDialog::getText( i18n( "Change nickname - Jabber Plugin" ), 0368 i18n( "Please enter the new nickname you want to have in the room <i>%1</i>" , rosterItem().jid().userHost()), 0369 mNick, &ok ); 0370 if ( !ok || !account()->isConnected()) 0371 return; 0372 0373 mNick=futureNewNickName; 0374 0375 XMPP::Status status = account()->protocol()->kosToStatus( account()->myself()->onlineStatus() ); 0376 account()->client()->changeGroupChatNick( rosterItem().jid().host() , rosterItem().jid().user() , mNick , status); 0377 0378 } 0379 0380 void JabberGroupContact::slotSubContactDestroyed( Kopete::Contact * deadContact ) 0381 { 0382 qCDebug(JABBER_PROTOCOL_LOG) << "cleaning dead subcontact " << deadContact->contactId() << " from room " << mRosterItem.jid().full (); 0383 0384 mMetaContactList.removeAll ( deadContact->metaContact () ); 0385 mContactList.removeAll ( deadContact ); 0386 0387 } 0388 0389 #include "moc_jabbergroupcontact.cpp"