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"