/* msnaccount.h - Manages a single MSN account Copyright (c) 2003-2005 by Olivier Goffart Copyright (c) 2003 by Martijn Klingens Copyright (c) 2005 by Michaƫl Larouche Kopete (c) 2002-2005 by the Kopete developers ************************************************************************* * * * 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 "msnaccount.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "msncontact.h" #include "msnnotifysocket.h" #include "msnchatsession.h" #include "kopetecontactlist.h" #include "kopetegroup.h" #include "kopetemetacontact.h" #include "kopetepassword.h" #include "kopeteuiglobal.h" #include "kopeteglobal.h" #include "kopetechatsessionmanager.h" #include "contactaddednotifydialog.h" #include "kopeteutils.h" #include "sha1.h" #if !defined NDEBUG #include "msndebugrawcmddlg.h" #include #endif #if MSN_WEBCAM #include "avdevice/videodevicepool.h" #endif MSNAccount::MSNAccount( MSNProtocol *parent, const TQString& AccountID, const char *name ) : Kopete::PasswordedAccount ( parent, AccountID.lower(), 0, name ) { m_notifySocket = 0L; m_connectstatus = MSNProtocol::protocol()->NLN; m_addWizard_metaContact = 0L; m_newContactList=false; // Init the myself contact setMyself( new MSNContact( this, accountId(), Kopete::ContactList::self()->myself() ) ); //myself()->setOnlineStatus( MSNProtocol::protocol()->FLN ); TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( groupRenamed( Kopete::Group *, const TQString & ) ), TQT_SLOT( slotKopeteGroupRenamed( Kopete::Group * ) ) ); TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( groupRemoved( Kopete::Group * ) ), TQT_SLOT( slotKopeteGroupRemoved( Kopete::Group * ) ) ); TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( globalIdentityChanged(const TQString&, const TQVariant& ) ), TQT_SLOT( slotGlobalIdentityChanged(const TQString&, const TQVariant& ) )); m_openInboxAction = new KAction( i18n( "Open Inbo&x..." ), "mail_generic", 0, this, TQT_SLOT( slotOpenInbox() ), this, "m_openInboxAction" ); m_changeDNAction = new KAction( i18n( "&Change Display Name..." ), TQString(), 0, this, TQT_SLOT( slotChangePublicName() ), this, "renameAction" ); m_startChatAction = new KAction( i18n( "&Start Chat..." ), "mail_generic", 0, this, TQT_SLOT( slotStartChat() ), this, "startChatAction" ); KConfigGroup *config=configGroup(); m_blockList = config->readListEntry( "blockList" ) ; m_allowList = config->readListEntry( "allowList" ) ; m_reverseList = config->readListEntry( "reverseList" ) ; // Load the avatar m_pictureFilename = locateLocal( "appdata", "msnpicture-"+ accountId().lower().replace(TQRegExp("[./~]"),"-") +".png" ); resetPictureObject(true); static_cast( myself() )->setInfo( "PHH", config->readEntry("PHH") ); static_cast( myself() )->setInfo( "PHM", config->readEntry("PHM") ); static_cast( myself() )->setInfo( "PHW", config->readEntry("PHW") ); //this is the display name static_cast( myself() )->setInfo( "MFN", config->readEntry("MFN") ); //construct the group list //Before 2003-11-14 the MSN server allowed us to download the group list without downloading the whole contactlist, but it's not possible anymore TQPtrList groupList = Kopete::ContactList::self()->groups(); for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() ) { TQString groupGuid=g->pluginData( protocol(), accountId() + " id" ); if ( !groupGuid.isEmpty() ) m_groupList.insert( groupGuid , g ); } // Set the client Id for the myself contact. It sets what MSN feature we support. m_clientId = MSNProtocol::MSNC4 | MSNProtocol::InkFormatGIF | MSNProtocol::SupportMultiPacketMessaging; #if MSN_WEBCAM Kopete::AV::VideoDevicePool::self()->scanDevices(); if( Kopete::AV::VideoDevicePool::self()->hasDevices() ) { m_clientId |= MSNProtocol::SupportWebcam; } #endif } TQString MSNAccount::serverName() { return configGroup()->readEntry( "serverName" , "messenger.hotmail.com" ); } uint MSNAccount::serverPort() { return configGroup()->readNumEntry( "serverPort" , 1863 ); } bool MSNAccount::useHttpMethod() const { return configGroup()->readBoolEntry( "useHttpMethod" , false ); } TQString MSNAccount::myselfClientId() const { return TQString::number(m_clientId, 10); } void MSNAccount::connectWithPassword( const TQString &passwd ) { m_newContactList=false; if ( isConnected() ) { kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request " << "(Already Connected)" << endl; return; } if ( m_notifySocket ) { kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request (Already connecting)" << endl; return; } m_password = passwd; if ( m_password.isNull() ) { kdDebug( 14140 ) << k_funcinfo <<"Abort connection (null password)" << endl; return; } if ( contacts().count() <= 1 ) { // Maybe the contactlist.xml has been removed, and the serial number not updated // ( the 1 is for the myself contact ) configGroup()->writeEntry( "serial", 0 ); } m_openInboxAction->setEnabled( false ); createNotificationServer(serverName(), serverPort()); } void MSNAccount::createNotificationServer( const TQString &host, uint port ) { if(m_notifySocket) //we are switching from one to another notifysocket. { //remove every slots to that socket, so we won't delete receive signals // from the old socket thinking they are from the new one TQObject::disconnect( m_notifySocket , 0, this, 0 ); m_notifySocket->deleteLater(); //be sure it will be deleted m_notifySocket=0L; } m_msgHandle.clear(); myself()->setOnlineStatus( MSNProtocol::protocol()->CNT ); m_notifySocket = new MSNNotifySocket( this, accountId() , m_password); m_notifySocket->setUseHttpMethod( useHttpMethod() ); TQObject::connect( m_notifySocket, TQT_SIGNAL( groupAdded( const TQString&, const TQString& ) ), TQT_SLOT( slotGroupAdded( const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( groupRenamed( const TQString&, const TQString& ) ), TQT_SLOT( slotGroupRenamed( const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( groupListed( const TQString&, const TQString& ) ), TQT_SLOT( slotGroupAdded( const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( groupRemoved( const TQString& ) ), TQT_SLOT( slotGroupRemoved( const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( contactList(const TQString&, const TQString&, const TQString&, uint, const TQString& ) ), TQT_SLOT( slotContactListed(const TQString&, const TQString&, const TQString&, uint, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL(contactAdded(const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ), TQT_SLOT( slotContactAdded(const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( contactRemoved(const TQString&, const TQString&, const TQString&, const TQString& ) ), TQT_SLOT( slotContactRemoved(const TQString&, const TQString&, const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( statusChanged( const Kopete::OnlineStatus & ) ), TQT_SLOT( slotStatusChanged( const Kopete::OnlineStatus & ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( invitedToChat( const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ), TQT_SLOT( slotCreateChat( const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( startChat( const TQString&, const TQString& ) ), TQT_SLOT( slotCreateChat( const TQString&, const TQString& ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( socketClosed() ), TQT_SLOT( slotNotifySocketClosed() ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( newContactList() ), TQT_SLOT( slotNewContactList() ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( receivedNotificationServer(const TQString&, uint ) ), TQT_SLOT(createNotificationServer(const TQString&, uint ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( hotmailSeted( bool ) ), m_openInboxAction, TQT_SLOT( setEnabled( bool ) ) ); TQObject::connect( m_notifySocket, TQT_SIGNAL( errorMessage(int, const TQString& ) ), TQT_SLOT( slotErrorMessageReceived(int, const TQString& ) ) ); m_notifySocket->setStatus( m_connectstatus ); m_notifySocket->connect(host, port); } void MSNAccount::disconnect() { if ( m_notifySocket ) m_notifySocket->disconnect(); } KActionMenu * MSNAccount::actionMenu() { KActionMenu *m_actionMenu=Kopete::Account::actionMenu(); if ( isConnected() ) { m_openInboxAction->setEnabled( true ); m_startChatAction->setEnabled( true ); m_changeDNAction->setEnabled( true ); } else { m_openInboxAction->setEnabled( false ); m_startChatAction->setEnabled( false ); m_changeDNAction->setEnabled( false ); } m_actionMenu->popupMenu()->insertSeparator(); m_actionMenu->insert( m_changeDNAction ); m_actionMenu->insert( m_startChatAction ); // m_actionMenu->popupMenu()->insertSeparator(); m_actionMenu->insert( m_openInboxAction ); #if !defined NDEBUG KActionMenu *debugMenu = new KActionMenu( "Debug", m_actionMenu ); debugMenu->insert( new KAction( i18n( "Send Raw C&ommand..." ), 0, this, TQT_SLOT( slotDebugRawCommand() ), debugMenu, "m_debugRawCommand" ) ); m_actionMenu->popupMenu()->insertSeparator(); m_actionMenu->insert( debugMenu ); #endif return m_actionMenu; } MSNNotifySocket *MSNAccount::notifySocket() { return m_notifySocket; } void MSNAccount::setOnlineStatus( const Kopete::OnlineStatus &status , const TQString &reason) { kdDebug( 14140 ) << k_funcinfo << status.description() << endl; // HACK: When changing song, do don't anything while connected if( reason.contains("[Music]") && ( status == MSNProtocol::protocol()->UNK || status == MSNProtocol::protocol()->CNT ) ) return; // Only send personal message when logged. if( m_notifySocket && m_notifySocket->isLogged() ) { // Only update the personal/status message, don't change the online status // since it's the same. if( reason.contains("[Music]") ) { TQString personalMessage = reason.section("[Music]", 1); setPersonalMessage( MSNProtocol::PersonalMessageMusic, personalMessage ); // Don't send un-needed status change. return; } else { setPersonalMessage( MSNProtocol::PersonalMessageNormal, reason ); } } if(status.status()== Kopete::OnlineStatus::Offline) disconnect(); else if ( m_notifySocket ) { m_notifySocket->setStatus( status ); } else { m_connectstatus = status; connect(); } } void MSNAccount::slotStartChat() { bool ok; TQString handle = KInputDialog::getText( i18n( "Start Chat - MSN Plugin" ), i18n( "Please enter the email address of the person with whom you want to chat:" ), TQString(), &ok ).lower(); if ( ok ) { if ( MSNProtocol::validContactId( handle ) ) { if ( !contacts()[ handle ] ) addContact( handle, handle, 0L, Kopete::Account::Temporary ); contacts()[ handle ]->execute(); } else { KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, i18n( "You must enter a valid email address." ), i18n( "MSN Plugin" ) ); } } } void MSNAccount::slotDebugRawCommand() { #if !defined NDEBUG if ( !isConnected() ) return; MSNDebugRawCmdDlg *dlg = new MSNDebugRawCmdDlg( 0L ); int result = dlg->exec(); if ( result == TQDialog::Accepted && m_notifySocket ) { m_notifySocket->sendCommand( dlg->command(), dlg->params(), dlg->addId(), dlg->msg().replace( "\n", "\r\n" ).utf8() ); } delete dlg; #endif } void MSNAccount::slotChangePublicName() { if ( !isConnected() ) { return; //TODO: change it anyway, and sync at the next connection } bool ok; TQString name = KInputDialog::getText( i18n( "Change Display Name - MSN Plugin" ), i18n( "Enter the new display name by which you want to be visible to your friends on MSN:" ), myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(), &ok ); if ( ok ) { if ( name.length() > 387 ) { KMessageBox::error( Kopete::UI::Global::mainWidget(), i18n( "The display name you entered is too long. Please use a shorter name.\n" "Your display name has not been changed." ), i18n( "Change Display Name - MSN Plugin" ) ); return; } setPublicName( name ); } } void MSNAccount::slotOpenInbox() { if ( m_notifySocket ) m_notifySocket->slotOpenInbox(); } void MSNAccount::slotNotifySocketClosed() { kdDebug( 14140 ) << k_funcinfo << endl; Kopete::Account::DisconnectReason reason=(Kopete::Account::DisconnectReason)(m_notifySocket->disconnectReason()); m_notifySocket->deleteLater(); m_notifySocket = 0l; myself()->setOnlineStatus( MSNProtocol::protocol()->FLN ); setAllContactsStatus( MSNProtocol::protocol()->FLN ); disconnected(reason); if(reason == Kopete::Account::OtherClient) { //close all chat sessions, so new message will arive to the other client. TQValueList sessions = Kopete::ChatSessionManager::self()->sessions(); TQValueList::Iterator it; for (it=sessions.begin() ; it != sessions.end() ; it++ ) { MSNChatSession *msnCS = dynamic_cast( *it ); if ( msnCS && msnCS->account() == this ) { msnCS->slotCloseSession(); } } } #if 0 else if ( state == 0x10 ) // connection died unexpectedly { KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error , i18n( "The connection with the MSN server was lost unexpectedly.\n" "If you cannot reconnect now, the server might be down. In that case, please try again later." ), i18n( "Connection Lost - MSN Plugin" ), KMessageBox::Notify ); } #endif m_msgHandle.clear(); // kdDebug( 14140 ) << "MSNAccount::slotNotifySocketClosed - done" << endl; } void MSNAccount::slotStatusChanged( const Kopete::OnlineStatus &status ) { // kdDebug( 14140 ) << k_funcinfo << status.internalStatus() << endl; myself()->setOnlineStatus( status ); if(m_newContactList) { m_newContactList=false; TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { MSNContact *c = static_cast( *it ); if(c && c->isDeleted() && c->metaContact() && !c->metaContact()->isTemporary() && c!=myself()) { if(c->serverGroups().isEmpty()) { //the contact is new, add it on the server c->setOnlineStatus( MSNProtocol::protocol()->FLN ); addContactServerside( c->contactId() , c->metaContact()->groups() ); } else //the contact had been deleted, remove it. { c->deleteLater(); } } } } } void MSNAccount::slotPersonalMessageChanged( const TQString& personalMessage ) { TQString oldPersonalMessage=myself()->property(MSNProtocol::protocol()->propPersonalMessage).value().toString() ; if ( personalMessage != oldPersonalMessage ) { myself()->setProperty( MSNProtocol::protocol()->propPersonalMessage, personalMessage ); configGroup()->writeEntry( "personalMessage" , personalMessage ); } } void MSNAccount::setPublicName( const TQString &publicName ) { if ( m_notifySocket ) { m_notifySocket->changePublicName( publicName, TQString() ); } } void MSNAccount::setPersonalMessage( MSNProtocol::PersonalMessageType type, const TQString &personalMessage ) { if ( m_notifySocket ) { m_notifySocket->changePersonalMessage( type, personalMessage ); } /* Eh, if we can't change the display name, don't let make the user think it has changed else if(type == MSNProtocol::PersonalMessageNormal) // Normal personalMessage, not a dynamic one that need formatting. { slotPersonalMessageChanged( personalMessage ); }*/ } void MSNAccount::slotGroupAdded( const TQString& groupName, const TQString &groupGuid ) { if ( m_groupList.contains( groupGuid ) ) { // Group can already be in the list since the idle timer does a 'List Groups' // command. Simply return, don't issue a warning. // kdDebug( 14140 ) << k_funcinfo << "Group " << groupName << " already in list, skipped." << endl; return; } //--------- Find the appropriate Kopete::Group, or create one ---------// TQPtrList groupList = Kopete::ContactList::self()->groups(); Kopete::Group *fallBack = 0L; //check if we have one in the old group list. if yes, update the id translate map. for(TQMap::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it ) { Kopete::Group *g=it.data(); if (g && g->pluginData( protocol(), accountId() + " displayName" ) == groupName && g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) { //it has the same name! we got it. (and it is not yet an msn group) fallBack=g; /*if ( g->displayName() != groupName ) { // The displayName was changed in Kopete while we were offline // FIXME: update the server right now }*/ break; } } if(!fallBack) { //it's certenly a new group ! search if one already exist with the same displayname. for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() ) { /* --This has been replaced by the loop right before. if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) { if ( g->pluginData( protocol(), accountId() + " id" ).toUInt() == groupNumber ) { m_groupList.insert( groupNumber, g ); TQString oldGroupName; if ( g->pluginData( protocol(), accountId() + " displayName" ) != groupName ) { // The displayName of the group has been modified by another client slotGroupRenamed( groupName, groupNumber ); } return; } } else {*/ if ( g->displayName() == groupName && (groupGuid.isEmpty()|| g->type()==Kopete::Group::Normal) && g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) { fallBack = g; kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" in the old MSN group. But kopete has already one with the same name." << endl; break; } } } if ( !fallBack ) { if( groupGuid.isEmpty() ) { // The group #0 is an unremovable group. his default name is "~" , // but the official client rename it i18n("others contact") at the first // connection. // In many case, the users don't use that group as a real group, or just as // a group to put all contact that are not sorted. fallBack = Kopete::Group::topLevel(); } else { fallBack = new Kopete::Group( groupName ); Kopete::ContactList::self()->addGroup( fallBack ); kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" So we're creating a new one." << endl; } } fallBack->setPluginData( protocol(), accountId() + " id", groupGuid ); fallBack->setPluginData( protocol(), accountId() + " displayName", groupName ); m_groupList.insert( groupGuid, fallBack ); // We have pending groups that we need add a contact to if ( tmp_addToNewGroup.contains(groupName) ) { TQStringList list=tmp_addToNewGroup[groupName]; for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { TQString contactId = *it; kdDebug( 14140 ) << k_funcinfo << "Adding to new group: " << contactId << endl; MSNContact *c = static_cast(contacts()[contactId]); if(c && c->hasProperty(MSNProtocol::protocol()->propGuid.key()) ) notifySocket()->addContact( contactId, MSNProtocol::FL, TQString(), c->guid(), groupGuid ); else { // If we get to here, we're currently adding a new contact, add the groupGUID to the groupList // to add when contact will be added to contactlist. if( tmp_addNewContactToGroup.contains( contactId ) ) tmp_addNewContactToGroup[contactId].append(groupGuid); else tmp_addNewContactToGroup.insert(contactId, TQStringList(groupGuid) ); } } tmp_addToNewGroup.remove(groupName); } } void MSNAccount::slotGroupRenamed( const TQString &groupGuid, const TQString& groupName ) { if ( m_groupList.contains( groupGuid ) ) { m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " id", groupGuid ); m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " displayName", groupName ); m_groupList[ groupGuid ]->setDisplayName( groupName ); } else { slotGroupAdded( groupName, groupGuid ); } } void MSNAccount::slotGroupRemoved( const TQString& groupGuid ) { if ( m_groupList.contains( groupGuid ) ) { m_groupList[ groupGuid ]->setPluginData( protocol(), TQMap() ); m_groupList.remove( groupGuid ); } } void MSNAccount::addGroup( const TQString &groupName, const TQString& contactToAdd ) { if ( !contactToAdd.isNull() ) { if( tmp_addToNewGroup.contains(groupName) ) { tmp_addToNewGroup[groupName].append(contactToAdd); //A group with the same name is about to be added, // we don't need to add a second group with the same name kdDebug( 14140 ) << k_funcinfo << "no need to re-add " << groupName << " for " << contactToAdd << endl; return; } else { tmp_addToNewGroup.insert(groupName,TQStringList(contactToAdd)); kdDebug( 14140 ) << k_funcinfo << "preparing to add " << groupName << " for " << contactToAdd << endl; } } if ( m_notifySocket ) m_notifySocket->addGroup( groupName ); } void MSNAccount::slotKopeteGroupRenamed( Kopete::Group *g ) { if ( notifySocket() && g->type() == Kopete::Group::Normal ) { if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() && g->displayName() != g->pluginData( protocol(), accountId() + " displayName" ) && m_groupList.contains( g->pluginData( protocol(), accountId() + " id" ) ) ) { notifySocket()->renameGroup( g->displayName(), g->pluginData( protocol(), accountId() + " id" ) ); } } } void MSNAccount::slotKopeteGroupRemoved( Kopete::Group *g ) { //The old gorup list is only used whe syncing the contactlist. //We can assume the contactlist is already fully synced at this time. //The group g is maybe in the oldGroupList. We remove everithing since //we don't need it anymore, no need to search it m_oldGroupList.clear(); if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) { TQString groupGuid = g->pluginData( protocol(), accountId() + " id" ); if ( !m_groupList.contains( groupGuid ) ) { // the group is maybe already removed in the server slotGroupRemoved( groupGuid ); return; } //this is also done later, but he have to do it now! // (in slotGroupRemoved) m_groupList.remove(groupGuid); if ( groupGuid.isEmpty() ) { // the group #0 can't be deleted // then we set it as the top-level group if ( g->type() == Kopete::Group::TopLevel ) return; Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " id", "" ); Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " displayName", g->pluginData( protocol(), accountId() + " displayName" ) ); g->setPluginData( protocol(), accountId() + " id", TQString() ); // the group should be soon deleted, but make sure return; } if ( m_notifySocket ) { bool still_have_contact=false; // if contact are contains only in the group we are removing, abort the TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { MSNContact *c = static_cast( it.current() ); if ( c && c->serverGroups().contains( groupGuid ) ) { /** don't do that becasue theses may already have been sent m_notifySocket->removeContact( c->contactId(), groupNumber, MSNProtocol::FL ); */ still_have_contact=true; break; } } if(!still_have_contact) m_notifySocket->removeGroup( groupGuid ); } } } void MSNAccount::slotNewContactList() { m_oldGroupList=m_groupList; for(TQMap::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it ) { //they are about to be changed if(it.data()) it.data()->setPluginData( protocol(), accountId() + " id", TQString() ); } m_allowList.clear(); m_blockList.clear(); m_reverseList.clear(); m_groupList.clear(); KConfigGroup *config=configGroup(); config->writeEntry( "blockList" , TQString() ) ; config->writeEntry( "allowList" , TQString() ); config->writeEntry( "reverseList" , TQString() ); // clear all date information which will be received. // if the information is not anymore on the server, it will not be received TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { MSNContact *c = static_cast( *it ); c->setBlocked( false ); c->setAllowed( false ); c->setReversed( false ); c->setDeleted( true ); c->setInfo( "PHH", TQString() ); c->setInfo( "PHW", TQString() ); c->setInfo( "PHM", TQString() ); c->removeProperty( MSNProtocol::protocol()->propGuid ); } m_newContactList=true; } void MSNAccount::slotContactListed( const TQString& handle, const TQString& publicName, const TQString &contactGuid, uint lists, const TQString& groups ) { // On empty lists handle might be empty, ignore that // ignore also the myself contact. if ( handle.isEmpty() || handle==accountId()) return; MSNContact *c = static_cast( contacts()[ handle ] ); if ( lists & 1 ) // FL { TQStringList contactGroups = TQStringList::split( ",", groups, false ); if ( c ) { if( !c->metaContact() ) { kdWarning( 14140 ) << k_funcinfo << "the contact " << c->contactId() << " has no meta contact" <setMetaContact(metaContact); Kopete::ContactList::self()->addMetaContact( metaContact ); } // Contact exists, update data. // Merging difference between server contact list and Kopete::Contact's contact list into MetaContact's contact-list c->setOnlineStatus( MSNProtocol::protocol()->FLN ); if(!publicName.isEmpty() && publicName!=handle) c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); else c->removeProperty( Kopete::Global::Properties::self()->nickName() ); c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid); const TQMap oldServerGroups = c->serverGroups(); c->clearServerGroups(); for ( TQStringList::ConstIterator it = contactGroups.begin(); it != contactGroups.end(); ++it ) { TQString newServerGroupID = *it; if(m_groupList.contains(newServerGroupID)) { Kopete::Group *newServerGroup=m_groupList[ newServerGroupID ] ; c->contactAddedToGroup( newServerGroupID, newServerGroup ); if( !c->metaContact()->groups().contains(newServerGroup) ) { // The contact has been added in a group by another client c->metaContact()->addToGroup( newServerGroup ); } } } for ( TQMap::ConstIterator it = oldServerGroups.begin(); it != oldServerGroups.end(); ++it ) { Kopete::Group *old_group=m_oldGroupList[it.key()]; if(old_group) { TQString oldnewID=old_group->pluginData(protocol() , accountId() +" id"); if ( !oldnewID.isEmpty() && contactGroups.contains( oldnewID ) ) continue; //ok, it's correctn no need to do anything. c->metaContact()->removeFromGroup( old_group ); } } c->setDeleted(false); // Update server if the contact has been moved to another group while MSN was offline c->sync(); } else { Kopete::MetaContact *metaContact = new Kopete::MetaContact(); c = new MSNContact( this, handle, metaContact ); c->setDeleted(true); //we don't want to sync c->setOnlineStatus( MSNProtocol::protocol()->FLN ); if(!publicName.isEmpty() && publicName!=handle) c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); else c->removeProperty( Kopete::Global::Properties::self()->nickName() ); c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); for ( TQStringList::Iterator it = contactGroups.begin(); it != contactGroups.end(); ++it ) { TQString groupGuid = *it; if(m_groupList.contains(groupGuid)) { c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); metaContact->addToGroup( m_groupList[ groupGuid ] ); } } Kopete::ContactList::self()->addMetaContact( metaContact ); c->setDeleted(false); } } else //the contact is _not_ in the FL, it has been removed { if(c) { c->setOnlineStatus( static_cast(protocol())->UNK ); c->clearServerGroups(); //TODO: display a message and suggest to remove the contact. // but i fear a simple messageBox QuestionYesNo here gives a nice crash. //delete ct; } } if ( lists & 2 ) slotContactAdded( handle, "AL", publicName, TQString(), TQString() ); else if(c) c->setAllowed(false); if ( lists & 4 ) slotContactAdded( handle, "BL", publicName, TQString(), TQString() ); else if(c) c->setBlocked(false); if ( lists & 8 ) slotContactAdded( handle, "RL", publicName, TQString(), TQString() ); else if(c) c->setReversed(false); if ( lists & 16 ) // This contact is on the pending list. Add to the reverse list and delete from the pending list { notifySocket()->addContact( handle, MSNProtocol::RL, TQString(), TQString(), TQString() ); notifySocket()->removeContact( handle, MSNProtocol::PL, TQString(), TQString() ); } } void MSNAccount::slotContactAdded( const TQString& handle, const TQString& list, const TQString& publicName, const TQString& contactGuid, const TQString &groupGuid ) { if ( list == "FL" ) { bool new_contact = false; if ( !contacts()[ handle ] ) { new_contact = true; Kopete::MetaContact *m= m_addWizard_metaContact ? m_addWizard_metaContact : new Kopete::MetaContact(); MSNContact *c = new MSNContact( this, handle, m ); if(!publicName.isEmpty() && publicName!=handle) c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); else c->removeProperty( Kopete::Global::Properties::self()->nickName() ); c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); // Add the new contact to the group he belongs. if ( tmp_addNewContactToGroup.contains(handle) ) { TQStringList list = tmp_addNewContactToGroup[handle]; for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { TQString groupGuid = *it; // If the group didn't exist yet (yay for async operations), don't add the contact to the group // Let slotGroupAdded do it. if( m_groupList.contains(groupGuid) ) { kdDebug( 14140 ) << k_funcinfo << "Adding " << handle << " to group: " << groupGuid << endl; notifySocket()->addContact( handle, MSNProtocol::FL, TQString(), contactGuid, groupGuid ); c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); m->addToGroup( m_groupList[ groupGuid ] ); } if ( !m_addWizard_metaContact ) { Kopete::ContactList::self()->addMetaContact( m ); } } tmp_addNewContactToGroup.remove(handle); } c->setOnlineStatus( MSNProtocol::protocol()->FLN ); m_addWizard_metaContact = 0L; } if ( !new_contact ) { // Contact has been added to a group MSNContact *c = findContactByGuid(contactGuid); if(c != 0L) { // Make sure that the contact has always his contactGUID. if( !c->hasProperty(MSNProtocol::protocol()->propGuid.key()) ) c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); if ( c->onlineStatus() == MSNProtocol::protocol()->UNK ) c->setOnlineStatus( MSNProtocol::protocol()->FLN ); if ( c->metaContact() && c->metaContact()->isTemporary() ) c->metaContact()->setTemporary( false, m_groupList.contains( groupGuid ) ? m_groupList[ groupGuid ] : 0L ); else { if(m_groupList.contains(groupGuid)) { if( c->metaContact() ) c->metaContact()->addToGroup( m_groupList[groupGuid] ); c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); } } } } if ( !handle.isEmpty() && !m_allowList.contains( handle ) && !m_blockList.contains( handle ) ) { kdDebug(14140) << k_funcinfo << "Trying to add contact to AL. " << endl; notifySocket()->addContact(handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); } } else if ( list == "BL" ) { if ( contacts()[ handle ] ) static_cast( contacts()[ handle ] )->setBlocked( true ); if ( !m_blockList.contains( handle ) ) { m_blockList.append( handle ); configGroup()->writeEntry( "blockList" , m_blockList ) ; } } else if ( list == "AL" ) { if ( contacts()[ handle ] ) static_cast( contacts()[ handle ] )->setAllowed( true ); if ( !m_allowList.contains( handle ) ) { m_allowList.append( handle ); configGroup()->writeEntry( "allowList" , m_allowList ) ; } } else if ( list == "RL" ) { // search for new Contacts Kopete::Contact *ct=contacts()[ handle ]; if ( !ct || !ct->metaContact() || ct->metaContact()->isTemporary() ) { // Users in the allow list or block list now never trigger the // 'new user' dialog, which makes it impossible to add those here. // Not necessarily bad, but the usability effects need more thought // before I declare it good :- ) if ( !m_allowList.contains( handle ) && !m_blockList.contains( handle ) ) { TQString nick; //in most case, the public name is not know if(publicName!=handle) // so we don't whos it if it is not know nick=publicName; Kopete::UI::ContactAddedNotifyDialog *dialog= new Kopete::UI::ContactAddedNotifyDialog( handle,nick,this, Kopete::UI::ContactAddedNotifyDialog::InfoButton ); TQObject::connect(dialog,TQT_SIGNAL(applyClicked(const TQString&)), this,TQT_SLOT(slotContactAddedNotifyDialogClosed(const TQString& ))); dialog->show(); } } else { static_cast( ct )->setReversed( true ); } m_reverseList.append( handle ); configGroup()->writeEntry( "reverseList" , m_reverseList ) ; } } void MSNAccount::slotContactRemoved( const TQString& handle, const TQString& list, const TQString& contactGuid, const TQString& groupGuid ) { kdDebug( 14140 ) << k_funcinfo << "handle: " << handle << " list: " << list << " contact-uid: " << contactGuid << endl; MSNContact *c=static_cast( contacts()[ handle ] ); if ( list == "BL" ) { m_blockList.remove( handle ); configGroup()->writeEntry( "blockList" , m_blockList ) ; if ( !m_allowList.contains( handle ) ) notifySocket()->addContact( handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); if(c) c->setBlocked( false ); } else if ( list == "AL" ) { m_allowList.remove( handle ); configGroup()->writeEntry( "allowList" , m_allowList ) ; if ( !m_blockList.contains( handle ) ) notifySocket()->addContact( handle, MSNProtocol::BL, TQString(), TQString(), TQString() ); if(c) c->setAllowed( false ); } else if ( list == "RL" ) { m_reverseList.remove( handle ); configGroup()->writeEntry( "reverseList" , m_reverseList ) ; if ( c ) { // Contact is removed from the reverse list // only MSN can do this, so this is currently not supported c->setReversed( false ); /* InfoWidget *info = new InfoWidget( 0 ); info->title->setText( "" + i18n( "Contact removed!" ) +"" ); TQString dummy; dummy = "
" + imContact->getPublicName() + "( " +imContact->getHandle() +" )

"; dummy += i18n( "has removed you from his contact list!" ) + "
"; dummy += i18n( "This contact is now removed from your contact list" ); info->infoText->setText( dummy ); info->setCaption( "KMerlin - Info" ); info->show(); */ } } else if ( list == "FL" ) { // The FL list only use the contact GUID, use the contact referenced by the GUID. MSNContact *contactRemoved = findContactByGuid(contactGuid); TQStringList groupGuidList; bool deleteContact = groupGuid.isEmpty() ? true : false; // Delete the contact when the group GUID is empty. // Remove the contact from the contact list for all the group he is a member. if( groupGuid.isEmpty() ) { if(contactRemoved) { TQPtrList groupList = contactRemoved->metaContact()->groups(); for( TQPtrList::Iterator it = groupList.begin(); it != groupList.end(); ++it ) { Kopete::Group *group = *it; if ( !group->pluginData( protocol(), accountId() + " id" ).isEmpty() ) { groupGuidList.append( group->pluginData( protocol(), accountId() + " id" ) ); } } } } else { groupGuidList.append( groupGuid ); } if( !groupGuidList.isEmpty() ) { TQStringList::const_iterator stringIt; for( stringIt = groupGuidList.begin(); stringIt != groupGuidList.end(); ++stringIt ) { // Contact is removed from the FL list, remove it from the group if(contactRemoved != 0L) contactRemoved->contactRemovedFromGroup( *stringIt ); //check if the group is now empty to remove it if ( m_notifySocket ) { bool still_have_contact=false; // if contact are contains only in the group we are removing, abort the TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { MSNContact *c2 = static_cast( it.current() ); if ( c2->serverGroups().contains( *stringIt ) ) { still_have_contact=true; break; } } if(!still_have_contact) m_notifySocket->removeGroup( *stringIt ); } } } if(deleteContact && contactRemoved) { kdDebug(14140) << k_funcinfo << "Deleting the MSNContact " << contactRemoved->contactId() << endl; contactRemoved->deleteLater(); } } } void MSNAccount::slotCreateChat( const TQString& address, const TQString& auth ) { slotCreateChat( 0L, address, auth, m_msgHandle.first(), m_msgHandle.first() ); } void MSNAccount::slotCreateChat( const TQString& ID, const TQString& address, const TQString& auth, const TQString& handle_, const TQString& publicName ) { TQString handle = handle_.lower(); if ( handle.isEmpty() ) { // we have lost the handle? kdDebug(14140) << k_funcinfo << "Impossible to open a chat session, I forgot the contact to invite" <( contacts()[ handle ] ); if ( c && myself() ) { // we can't use simply c->manager(true) here to get the manager, because this will re-open // another chat session, and then, close this new one. We have to re-create the manager manualy. MSNChatSession *manager = dynamic_cast( c->manager( Kopete::Contact::CannotCreate ) ); if(!manager) { Kopete::ContactPtrList chatmembers; chatmembers.append(c); manager = new MSNChatSession( protocol(), myself(), chatmembers ); } manager->createChat( handle, address, auth, ID ); /** * This code should open a chatwindow when a socket is open * It has been disabled because gaim open switchboeard too often * * the solution is to open the window only when the contact start typing * see MSNChatSession::receivedTypingMsg * KGlobal::config()->setGroup( "MSN" ); bool notifyNewChat = KGlobal::config()->readBoolEntry( "NotifyNewChat", false ); if ( !ID.isEmpty() && notifyNewChat ) { // this temporary message should open the window if they not exist TQString body = i18n( "%1 has started a chat with you" ).arg( c->metaContact()->displayName() ); Kopete::Message tmpMsg = Kopete::Message( c, manager->members(), body, Kopete::Message::Internal, Kopete::Message::PlainText ); manager->appendMessage( tmpMsg ); } */ } if(!m_msgHandle.isEmpty()) m_msgHandle.pop_front(); } void MSNAccount::slotStartChatSession( const TQString& handle ) { // First create a message manager, because we might get an existing // manager back, in which case we likely also have an active switchboard // connection to reuse... MSNContact *c = static_cast( contacts()[ handle ] ); // if ( isConnected() && c && myself() && handle != m_msnId ) if ( m_notifySocket && c && myself() && handle != accountId() ) { if ( !c->manager(Kopete::Contact::CannotCreate) || !static_cast( c->manager( Kopete::Contact::CanCreate ) )->service() ) { m_msgHandle.prepend(handle); m_notifySocket->createChatSession(); } } } void MSNAccount::slotContactAddedNotifyDialogClosed(const TQString& handle) { const Kopete::UI::ContactAddedNotifyDialog *dialog = dynamic_cast(sender()); if(!dialog || !m_notifySocket) return; if(dialog->added()) { Kopete::MetaContact *mc=dialog->addContact(); if(mc) { //if the contact has been added this way, it's because the other user added us. // don't forgot to set the reversed flag (Bug 114400) MSNContact *c=dynamic_cast(mc->contacts().first()); if(c && c->contactId() == handle ) { c->setReversed( true ); } } } if ( !dialog->authorized() ) { if ( m_allowList.contains( handle ) ) m_notifySocket->removeContact( handle, MSNProtocol::AL, TQString(), TQString() ); else if ( !m_blockList.contains( handle ) ) m_notifySocket->addContact( handle, MSNProtocol::BL, TQString(), TQString(), TQString() ); } else { if ( m_blockList.contains( handle ) ) m_notifySocket->removeContact( handle, MSNProtocol::BL, TQString(), TQString() ); else if ( !m_allowList.contains( handle ) ) m_notifySocket->addContact( handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); } } void MSNAccount::slotGlobalIdentityChanged( const TQString &key, const TQVariant &value ) { if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ) { if(key == Kopete::Global::Properties::self()->nickName().key()) { TQString oldNick = myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(); TQString newNick = value.toString(); if(newNick != oldNick) { setPublicName( value.toString() ); } } else if(key == Kopete::Global::Properties::self()->photo().key()) { m_pictureFilename = value.toString(); kdDebug( 14140 ) << k_funcinfo << m_pictureFilename << endl; resetPictureObject(false, true); } } } void MSNAccount::slotErrorMessageReceived( int type, const TQString &msg ) { TQString caption = i18n( "MSN Plugin" ); // Use different notification type based on the error context. switch(type) { case MSNSocket::ErrorConnectionLost: { Kopete::Utils::notifyConnectionLost( this, caption, msg ); break; } case MSNSocket::ErrorConnectionError: { Kopete::Utils::notifyConnectionError( this, caption, msg ); break; } case MSNSocket::ErrorCannotConnect: { Kopete::Utils::notifyCannotConnect( this ); break; } case MSNSocket::ErrorInformation: { KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, msg, caption ); break; } case MSNSocket::ErrorServerError: default: { Kopete::Utils::notifyServerError( this, caption, msg ); break; } } } bool MSNAccount::createContact( const TQString &contactId, Kopete::MetaContact *metaContact ) { if ( !metaContact->isTemporary() && m_notifySocket) { m_addWizard_metaContact = metaContact; addContactServerside(contactId, metaContact->groups()); // FIXME: Find out if this contact was really added or not! return true; } else { // This is a temporary contact. ( a person who messaged us but is not on our conntact list. // We don't want to create it on the server.Just create the local contact object and add it // Or we are diconnected, and in that case, the contact will be added when connecting MSNContact *newContact = new MSNContact( this, contactId, metaContact ); newContact->setDeleted(true); return true; } } void MSNAccount::addContactServerside(const TQString &contactId, TQPtrList groupList) { // First of all, fill the temporary group list. The contact will be moved to his group(s). // When we receive back his contact GUID(required to move a contact between groups) for( Kopete::Group *group = groupList.first(); group; group = groupList.next() ) { // TODO: It it time that libkopete generate a unique ID that contains protocols, account and contact id. TQString groupId = group->pluginData( protocol(), accountId() + " id" ); // If the groupId is empty, that's mean the Kopete group is not on the MSN server. if( !groupId.isEmpty() ) { // Something got corrupted on contactlist.xml if( !m_groupList.contains(groupId) ) { // Clear the group plugin data. group->setPluginData( protocol() , accountId() + " id" , TQString()); group->setPluginData( protocol() , accountId() + " displayName" , TQString()); kdDebug( 14140 ) << k_funcinfo << " Group " << group->displayName() << " marked with id #" << groupId << " does not seems to be anymore on the server" << endl; // Add the group on MSN server, will fix the corruption. kdDebug(14140) << k_funcinfo << "Fixing group corruption, re-adding " << group->displayName() << "to the server." << endl; addGroup( group->displayName(), contactId); } else { // Add the group that the contact belong to add it when we will receive the contact GUID. if( tmp_addNewContactToGroup.contains( contactId ) ) tmp_addNewContactToGroup[contactId].append(groupId); else tmp_addNewContactToGroup.insert(contactId, TQStringList(groupId) ); } } else { if( !group->displayName().isEmpty() && group->type() == Kopete::Group::Normal ) { kdDebug(14140) << k_funcinfo << "Group not on MSN server, add it" << endl; addGroup( group->displayName(), contactId ); } } } // After add the contact to the top-level, it will be moved to required groups later. kdDebug( 14140 ) << k_funcinfo << "Add the contact on the server " << endl; m_notifySocket->addContact( contactId, MSNProtocol::FL, contactId, TQString(), TQString() ); } MSNContact *MSNAccount::findContactByGuid(const TQString &contactGuid) { kdDebug(14140) << k_funcinfo << "Looking for " << contactGuid << endl; TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { MSNContact *c = dynamic_cast( it.current() ); if(c && c->guid() == contactGuid ) { kdDebug(14140) << k_funcinfo << "OK found a contact. " << endl; // Found the contact GUID return c; } } return 0L; } bool MSNAccount::isHotmail() const { if ( !m_openInboxAction ) return false; return m_openInboxAction->isEnabled(); } TQString MSNAccount::pictureUrl() { return m_pictureFilename; } void MSNAccount::setPictureUrl(const TQString &url) { m_pictureFilename = url; } TQString MSNAccount::pictureObject() { if(m_pictureObj.isNull()) resetPictureObject(true); //silent=true to keep infinite loop away return m_pictureObj; } void MSNAccount::resetPictureObject(bool silent, bool force) { TQString old=m_pictureObj; if(!configGroup()->readBoolEntry("exportCustomPicture") && !force) { m_pictureObj=""; myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); } else { // Check if the picture is a 96x96 image, if not scale, crop and save. TQImage picture(m_pictureFilename); if(picture.isNull()) { m_pictureObj=""; myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); } else { if(picture.width() != 96 || picture.height() != 96) { // Save to a new location in msnpictures. TQString newLocation( locateLocal( "appdata", "msnpictures/"+ KURL(m_pictureFilename).fileName().lower() ) ); // Scale and crop the picture. picture = MSNProtocol::protocol()->scalePicture(picture); // Use the cropped/scaled image now. if(!picture.save(newLocation, "PNG")) { m_pictureObj=""; myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); } m_pictureFilename = newLocation; } } TQFile pictFile( m_pictureFilename ); if(!pictFile.open(IO_ReadOnly)) { m_pictureObj=""; myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); } else { TQByteArray ar=pictFile.readAll(); TQString sha1d= TQString((KCodecs::base64Encode(SHA1::hash(ar)))); TQString size=TQString::number( pictFile.size() ); TQString all= "Creator"+accountId()+"Size"+size+"Type3Locationkopete.tmpFriendlyAAA=SHA1D"+ sha1d; m_pictureObj=""; myself()->setProperty( Kopete::Global::Properties::self()->photo() , m_pictureFilename ); } } if(old!=m_pictureObj && isConnected() && m_notifySocket && !silent) { //update the msn pict m_notifySocket->setStatus( myself()->onlineStatus() ); } } #include "msnaccount.moc" // vim: set noet ts=4 sts=4 sw=4: