/* gwaccount.cpp - Kopete GroupWise Protocol Copyright (c) 2004 SUSE Linux AG http://www.suse.com Based on Testbed Copyright (c) 2003 by Will Stephenson Kopete (c) 2002-2003 by the Kopete developers ************************************************************************* * * * This library 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" #include #include "gwcontact.h" #include "gwcontactlist.h" #include "gwprotocol.h" #include "gwconnector.h" #include "gwmessagemanager.h" #include "privacymanager.h" #include "qcatlshandler.h" #include "userdetailsmanager.h" #include "tasks/createcontacttask.h" #include "tasks/createcontactinstancetask.h" #include "tasks/deleteitemtask.h" #include "tasks/movecontacttask.h" #include "tasks/updatecontacttask.h" #include "tasks/updatefoldertask.h" #include "ui/gwchatsearchdialog.h" #include "ui/gwprivacy.h" #include "ui/gwprivacydialog.h" #include "ui/gwreceiveinvitationdialog.h" #include "gwaccount.h" GroupWiseAccount::GroupWiseAccount( GroupWiseProtocol *parent, const TQString& accountID, const char *name ) : Kopete::ManagedConnectionAccount ( parent, accountID, 0, "groupwiseaccount" ) { Q_UNUSED( name ); // Init the myself contact setMyself( new GroupWiseContact( this, accountId(), Kopete::ContactList::self()->myself(), 0, 0, 0 ) ); myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline ); // Contact list management 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 * ) ) ); m_actionAutoReply = new TDEAction ( i18n( "&Set Auto-Reply..." ), TQString(), 0, this, TQT_SLOT( slotSetAutoReply() ), this, "actionSetAutoReply"); m_actionJoinChatRoom = new TDEAction ( i18n( "&Join Channel..." ), TQString(), 0, this, TQT_SLOT( slotJoinChatRoom() ), this, "actionJoinChatRoom"); m_actionManagePrivacy = new TDEAction ( i18n( "&Manage Privacy..." ), TQString(), 0, this, TQT_SLOT( slotPrivacy() ), this, "actionPrivacy"); m_connector = 0; m_TQCATLS = 0; m_tlsHandler = 0; m_clientStream = 0; m_client = 0; m_dontSync = false; m_serverListModel = 0; } GroupWiseAccount::~GroupWiseAccount() { cleanup(); } TDEActionMenu* GroupWiseAccount::actionMenu() { TDEActionMenu *m_actionMenu=Kopete::Account::actionMenu(); m_actionAutoReply->setEnabled( isConnected() ); m_actionManagePrivacy->setEnabled( isConnected() ); m_actionJoinChatRoom->setEnabled( isConnected() ); m_actionMenu->insert( m_actionManagePrivacy ); m_actionMenu->insert( m_actionAutoReply ); m_actionMenu->insert( m_actionJoinChatRoom ); /* Used for debugging */ /* theActionMenu->insert( new TDEAction ( "Test rtfize()", TQString(), 0, this, TQT_SLOT( slotTestRTFize() ), this, "actionTestRTFize") ); */ return m_actionMenu; } int GroupWiseAccount::port() const { return configGroup()->readNumEntry( "Port" ); } const TQString GroupWiseAccount::server() const { return configGroup()->readEntry( "Server" ); } Client * GroupWiseAccount::client() const { return m_client; } GroupWiseProtocol *GroupWiseAccount::protocol() const { return static_cast( Kopete::Account::protocol() ); } GroupWiseChatSession * GroupWiseAccount::chatSession( Kopete::ContactPtrList others, const GroupWise::ConferenceGuid & guid, Kopete::Contact::CanCreateFlags canCreate ) { GroupWiseChatSession * chatSession = 0; do // one iteration misuse of do...while to enable an easy drop-out once we locate a manager { // do we have a manager keyed by GUID? if ( !guid.isEmpty() ) { chatSession = findChatSessionByGuid( guid ); if ( chatSession ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " found a message manager by GUID: " << guid << endl; break; } } // does the factory know about one, going on the chat members? chatSession = dynamic_cast( Kopete::ChatSessionManager::self()->findChatSession( myself(), others, protocol() ) ); if ( chatSession ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " found a message manager by members with GUID: " << chatSession->guid() << endl; // re-add the returning contact(s) (very likely only one) to the chat Kopete::Contact * returningContact; for ( returningContact = others.first(); returningContact; returningContact = others.next() ) chatSession->joined( static_cast( returningContact ) ); if ( !guid.isEmpty() ) chatSession->setGuid( guid ); break; } // we don't have an existing message manager for this chat, so create one if we may if ( canCreate ) { chatSession = new GroupWiseChatSession( myself(), others, protocol(), guid ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " created a new message manager with GUID: " << chatSession->guid() << endl; m_chatSessions.append( chatSession ); // listen for the message manager telling us that the user //has left the conference so we remove it from our map TQObject::connect( chatSession, TQT_SIGNAL( leavingConference( GroupWiseChatSession * ) ), TQT_SLOT( slotLeavingConference( GroupWiseChatSession * ) ) ); break; } //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << // " no message manager available." << endl; } while ( 0 ); //dumpManagers(); return chatSession; } GroupWiseChatSession * GroupWiseAccount::findChatSessionByGuid( const GroupWise::ConferenceGuid & guid ) { GroupWiseChatSession * chatSession = 0; TQValueList::ConstIterator it; for ( it = m_chatSessions.begin(); it != m_chatSessions.end(); ++it ) { if ( (*it)->guid() == guid ) { chatSession = *it; break; } } return chatSession; } GroupWiseContact * GroupWiseAccount::contactForDN( const TQString & dn ) { TQDictIterator it( contacts() ); // check if we have a DN for them for( ; it.current(); ++it ) { GroupWiseContact * candidate = static_cast( it.current() ); if ( candidate && candidate->dn() == dn ) return candidate; } // we might have just added the contact with a user ID, try the first section of the dotted dn return static_cast< GroupWiseContact * >( contacts()[ protocol()->dnToDotted( dn ).section( '.', 0, 0 ) ] ); } void GroupWiseAccount::setAway( bool away, const TQString & reason ) { if ( away ) { if ( Kopete::Away::getInstance()->idleTime() > 10 ) // don't go AwayIdle unless the user has actually been idle this long setOnlineStatus( protocol()->groupwiseAwayIdle, TQString() ); else setOnlineStatus( protocol()->groupwiseAway, reason ); } else setOnlineStatus( protocol()->groupwiseAvailable ); } void GroupWiseAccount::performConnectWithPassword( const TQString &password ) { if ( password.isEmpty() ) { disconnect(); return; } // don't try and connect if we are already connected if ( isConnected () ) return; bool sslPossible = TQCA::isSupported(TQCA::CAP_TLS); if (!sslPossible) { KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget (), KMessageBox::Error, i18n ("SSL support could not be initialized for account %1. This is most likely because the TQCA TLS plugin is not installed on your system."). arg(myself()->contactId()), i18n ("GroupWise SSL Error")); return; } if ( m_client ) { m_client->close(); cleanup(); } // set up network classes m_connector = new KNetworkConnector( 0 ); //myConnector->setOptHostPort( "localhost", 8300 ); m_connector->setOptHostPort( server(), port() ); m_connector->setOptSSL( true ); Q_ASSERT( TQCA::isSupported(TQCA::CAP_TLS) ); m_TQCATLS = new TQCA::TLS; m_tlsHandler = new TQCATLSHandler( m_TQCATLS ); m_clientStream = new ClientStream( m_connector, m_tlsHandler, 0); TQObject::connect( m_connector, TQT_SIGNAL( error() ), this, TQT_SLOT( slotConnError() ) ); TQObject::connect( m_connector, TQT_SIGNAL( connected() ), this, TQT_SLOT( slotConnConnected() ) ); TQObject::connect (m_clientStream, TQT_SIGNAL (connectionClosed()), this, TQT_SLOT (slotCSDisconnected())); TQObject::connect (m_clientStream, TQT_SIGNAL (delayedCloseFinished()), this, TQT_SLOT (slotCSDisconnected())); // Notify us when the transport layer is connected TQObject::connect( m_clientStream, TQT_SIGNAL( connected() ), TQT_SLOT( slotCSConnected() ) ); // it's necessary to catch this signal and tell the TLS handler to proceed // even if we don't check cert validity TQObject::connect( m_tlsHandler, TQT_SIGNAL(tlsHandshaken()), TQT_SLOT( slotTLSHandshaken()) ); // starts the client once the security layer is up, but see below TQObject::connect( m_clientStream, TQT_SIGNAL( securityLayerActivated(int) ), TQT_SLOT( slotTLSReady(int) ) ); // we could handle login etc in start(), in which case we would emit this signal after that //TQObject::connect (jabberClientStream, TQT_SIGNAL (authenticated()), // this, TQT_SLOT (slotCSAuthenticated ())); // we could also get do the actual login in response to this.. //TQObject::connect (m_clientStream, TQT_SIGNAL (needAuthParams(bool, bool, bool)), // this, TQT_SLOT (slotCSNeedAuthParams (bool, bool, bool))); // not implemented: warning TQObject::connect( m_clientStream, TQT_SIGNAL( warning(int) ), TQT_SLOT( slotCSWarning(int) ) ); // not implemented: error TQObject::connect( m_clientStream, TQT_SIGNAL( error(int) ), TQT_SLOT( slotCSError(int) ) ); m_client = new Client( 0, CMSGPRES_GW_6_5 ); // NB these are prefixed with TQObject:: to avoid any chance of a clash with our connect() methods. // we connected successfully TQObject::connect( m_client, TQT_SIGNAL( loggedIn() ), TQT_SLOT( slotLoggedIn() ) ); // or connection failed TQObject::connect( m_client, TQT_SIGNAL( loginFailed() ), TQT_SLOT( slotLoginFailed() ) ); // folder listed TQObject::connect( m_client, TQT_SIGNAL( folderReceived( const FolderItem & ) ), TQT_SLOT( receiveFolder( const FolderItem & ) ) ); // contact listed TQObject::connect( m_client, TQT_SIGNAL( contactReceived( const ContactItem & ) ), TQT_SLOT( receiveContact( const ContactItem & ) ) ); // contact details listed TQObject::connect( m_client, TQT_SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ), TQT_SLOT( receiveContactUserDetails( const GroupWise::ContactDetails & ) ) ); // contact status changed TQObject::connect( m_client, TQT_SIGNAL( statusReceived( const TQString &, TQ_UINT16, const TQString & ) ), TQT_SLOT( receiveStatus( const TQString &, TQ_UINT16 , const TQString & ) ) ); // incoming message TQObject::connect( m_client, TQT_SIGNAL( messageReceived( const ConferenceEvent & ) ), TQT_SLOT( handleIncomingMessage( const ConferenceEvent & ) ) ); // auto reply to one of our messages because the recipient is away TQObject::connect( m_client, TQT_SIGNAL( autoReplyReceived( const ConferenceEvent & ) ), TQT_SLOT( handleIncomingMessage( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( ourStatusChanged( GroupWise::Status, const TQString &, const TQString & ) ), TQT_SLOT( changeOurStatus( GroupWise::Status, const TQString &, const TQString & ) ) ); // conference events TQObject::connect( m_client, TQT_SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid & ) ), TQT_SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( conferenceCreationFailed( const int, const int ) ), TQT_SIGNAL( conferenceCreationFailed( const int, const int ) ) ); TQObject::connect( m_client, TQT_SIGNAL( invitationReceived( const ConferenceEvent & ) ), TQT_SLOT( receiveInvitation( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( conferenceLeft( const ConferenceEvent & ) ), TQT_SLOT( receiveConferenceLeft( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent & ) ), TQT_SLOT( receiveConferenceJoinNotify( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( inviteNotifyReceived( const ConferenceEvent & ) ), TQT_SLOT( receiveInviteNotify( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( invitationDeclined( const ConferenceEvent & ) ), TQT_SLOT( receiveInviteDeclined( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( conferenceJoined( const GroupWise::ConferenceGuid &, const TQStringList &, const TQStringList & ) ), TQT_SLOT( receiveConferenceJoin( const GroupWise::ConferenceGuid &, const TQStringList & , const TQStringList & ) ) ); // typing events TQObject::connect( m_client, TQT_SIGNAL( contactTyping( const ConferenceEvent & ) ), TQT_SIGNAL( contactTyping( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( contactNotTyping( const ConferenceEvent & ) ), TQT_SIGNAL( contactNotTyping( const ConferenceEvent & ) ) ); // misc TQObject::connect( m_client, TQT_SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails &) ), TQT_SLOT( receiveAccountDetails( const GroupWise::ContactDetails & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( connectedElsewhere() ), TQT_SLOT( slotConnectedElsewhere() ) ); // privacy - contacts can't connect directly to this signal because myself() is initialised before m_client TQObject::connect( m_client->privacyManager(), TQT_SIGNAL( privacyChanged( const TQString &, bool ) ), TQT_SIGNAL( privacyChanged( const TQString &, bool ) ) ); // GW7 TQObject::connect( m_client, TQT_SIGNAL( broadcastReceived( const ConferenceEvent & ) ), TQT_SLOT( handleIncomingMessage( const ConferenceEvent & ) ) ); TQObject::connect( m_client, TQT_SIGNAL( systemBroadcastReceived( const ConferenceEvent & ) ), TQT_SLOT( handleIncomingMessage( const ConferenceEvent & ) ) ); struct utsname utsBuf; uname (&utsBuf); m_client->setClientName ("Kopete"); m_client->setClientVersion ( kapp->aboutData ()->version () ); m_client->setOSName (TQString ("%1 %2").arg (utsBuf.sysname, 1).arg (utsBuf.release, 2)); kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Connecting to GroupWise server " << server() << ":" << port() << endl; NovellDN dn; dn.dn = "maeuschen"; dn.server = "reiser.suse.de"; m_serverListModel = new GWContactList( this ); myself()->setOnlineStatus( protocol()->groupwiseConnecting ); m_client->connectToServer( m_clientStream, dn, true ); TQObject::connect( m_client, TQT_SIGNAL( messageSendingFailed() ), TQT_SLOT( slotMessageSendingFailed() ) ); } void GroupWiseAccount::slotMessageSendingFailed() { KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, i18n("Message Sending Failed", "Kopete was not able to send the last message sent on account '%1'.\nIf possible, please send the console output from Kopete to for analysis." ).arg( accountId() ) , i18n ("Unable to Send Message on Account '%1'").arg( accountId() ) ); } void GroupWiseAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const TQString &reason ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; if ( status == protocol()->groupwiseUnknown || status == protocol()->groupwiseConnecting || status == protocol()->groupwiseInvalid ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " called with invalid status \"" << status.description() << "\"" << endl; } // going offline else if ( status == protocol()->groupwiseOffline ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " DISCONNECTING" << endl; disconnect(); } // changing status else if ( isConnected() ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "changing status to \"" << status.description() << "\"" << endl; // Appear Offline is achieved by explicitly setting the status to offline, // rather than disconnecting as when really going offline. if ( status == protocol()->groupwiseAppearOffline ) m_client->setStatus( GroupWise::Offline, reason, configGroup()->readEntry( "AutoReply" ) ); else m_client->setStatus( ( GroupWise::Status )status.internalStatus(), reason, configGroup()->readEntry( "AutoReply" ) ); } // going online else { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Must be connected before changing status" << endl; m_initialReason = reason; connect( status ); } } void GroupWiseAccount::disconnect () { disconnect ( Manual ); } void GroupWiseAccount::disconnect( Kopete::Account::DisconnectReason reason ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; if( isConnected () ) { kdDebug (GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << "Still connected, closing connection..." << endl; TQValueList::ConstIterator it; for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it ) (*it)->setClosed(); /* Tell backend class to disconnect. */ m_client->close (); } // clear the model of the server side contact list, so that when we reconnect, there will not be any stale entries to confuse GroupWiseContact::syncGroups() delete m_serverListModel; m_serverListModel = 0; // make sure that the connection animation gets stopped if we're still // in the process of connecting myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline ); disconnected( reason ); kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << "Disconnected." << endl; } void GroupWiseAccount::cleanup() { delete m_client; delete m_clientStream; delete m_TQCATLS; delete m_connector; m_connector = 0; m_TQCATLS = 0; m_clientStream = 0; m_client = 0; } void GroupWiseAccount::createConference( const int clientId, const TQStringList& invitees ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; // TODO: remove this it prevents sending a list of participants with the createconf if ( isConnected() ) m_client->createConference( clientId , invitees ); } void GroupWiseAccount::sendInvitation( const GroupWise::ConferenceGuid & guid, const TQString & dn, const TQString & message ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; if ( isConnected() ) { GroupWise::OutgoingMessage msg; msg.guid = guid; msg.message = message; m_client->sendInvitation( guid, dn, msg ); } } void GroupWiseAccount::slotLoggedIn() { reconcileOfflineChanges(); // set local status display myself()->setOnlineStatus( protocol()->groupwiseAvailable ); // set status on server if ( initialStatus() != Kopete::OnlineStatus(Kopete::OnlineStatus::Online) && ( ( GroupWise::Status )initialStatus().internalStatus() != GroupWise::Unknown ) ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Initial status is not online, setting status to " << initialStatus().internalStatus() << endl; m_client->setStatus( ( GroupWise::Status )initialStatus().internalStatus(), m_initialReason, configGroup()->readEntry( "AutoReply" ) ); } } void GroupWiseAccount::reconcileOfflineChanges() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; m_dontSync = true; //sanity check the server side model vs our contact list. //Contacts might have been removed from some groups or entirely on the server. //Any contact not present on the server should be deleted locally. // for each metacontact group membership: // for each GroupWiseContact // get its contact list instances // get its metacontact's groups // for each group // is there no CLI with the same id? // if MC has no other contacts // if MC's groups size is 1 // remove MC // else // remove from group // else // if MC's groups size is 1 and group is topLevel // remove contact // else // Contact's group membership were changed elsewhere, but we can't change it here without // // affecting other protocols' contacts // set flag to warn user that incompatible changes were made on other client bool conflicts = false; TQDictIterator it( contacts() ); for ( ; it.current(); ++it ) { if ( *it == myself() ) continue; GroupWiseContact * c = static_cast< GroupWiseContact *>( *it ); GWContactInstanceList instances = m_serverListModel->instancesWithDn( c->dn() ); TQPtrList groups = c->metaContact()->groups(); TQPtrListIterator grpIt( groups ); while ( *grpIt ) { TQPtrListIterator candidate = grpIt; ++grpIt; bool found = false; GWContactInstanceList::Iterator instIt = instances.begin(); for ( ; instIt != instances.end(); ++instIt ) { TQString groupId = ( *candidate )->pluginData( protocol(), accountId() + " objectId" ); if ( groupId.isEmpty() ) if ( *candidate == Kopete::Group::topLevel() ) groupId = "0"; // hack the top level's objectId to 0 else continue; GWFolder * folder = ::tqqt_cast( ( *instIt )->parent() ); if ( folder->id == ( unsigned int )groupId.toInt() ) { found = true; instances.remove( instIt ); break; } } if ( !found ) { if ( c->metaContact()->contacts().count() == 1 ) { if ( c->metaContact()->groups().count() == 1 ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found on server side list, deleting metacontact with only this contact, in one group" << c->metaContact()->displayName() << endl; Kopete::ContactList::self()->removeMetaContact( c->metaContact() ); break; } else { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found, removing metacontact " << c->metaContact()->displayName() << " from group " << ( *candidate )->displayName() << endl; c->metaContact()->removeFromGroup( *candidate ); } } else { if ( c->metaContact()->groups().count() == 1 ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "contact instance " << c->dn() << " not found, removing contact " << c->metaContact()->displayName() << " from metacontact with other contacts " << endl; c->deleteLater(); break; } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "metacontact " << c->metaContact()->displayName( ) << "has multiple children and group membership, and contact " << c->dn() << " was removed from one group on the server." << endl; conflicts = true; } } // } //end while, now check the next group membership } //end for, now check the next groupwise contact if ( conflicts ) // show queuedmessagebox KPassivePopup::message( i18n( "Conflicting Changes Made Offline" ), i18n( "A change happened to your GroupWise contact list while you were offline which was impossible to reconcile." ), Kopete::UI::Global::mainWidget() ); m_dontSync = false; } void GroupWiseAccount::slotLoginFailed() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; password().setWrong(); disconnect(); connect(); } void GroupWiseAccount::slotKopeteGroupRenamed( Kopete::Group * renamedGroup ) { if ( isConnected() ) { TQString objectIdString = renamedGroup->pluginData( protocol(), accountId() + " objectId" ); // if this group exists on the server if ( !objectIdString.isEmpty() ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; GroupWise::FolderItem fi; fi.id = objectIdString.toInt(); if ( fi.id != 0 ) { fi.sequence = renamedGroup->pluginData( protocol(), accountId() + " sequence" ).toInt(); fi.name= renamedGroup->pluginData( protocol(), accountId() + " serverDisplayName" ); UpdateFolderTask * uft = new UpdateFolderTask( client()->rootTask() ); uft->renameFolder( renamedGroup->displayName(), fi ); uft->go( true ); // would be safer to do this in a slot fired on uft's finished() signal renamedGroup->setPluginData( protocol(), accountId() + " serverDisplayName", renamedGroup->displayName() ); } } } //else // errornotconnected } void GroupWiseAccount::slotKopeteGroupRemoved( Kopete::Group * group ) { if ( isConnected() ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; // the member contacts should be deleted separately, so just delete the folder here // get the folder object id TQString objectIdString = group->pluginData( protocol(), accountId() + " objectId" ); if ( !objectIdString.isEmpty() ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "deleting folder with objectId: " << objectIdString << endl; int objectId = objectIdString.toInt(); if ( objectId == 0 ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "deleted folder " << group->displayName() << " has root folder objectId 0!" << endl; return; } DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() ); dit->item( 0, objectId ); // the group is deleted synchronously after this slot returns; so there is no point listening for signals dit->go( true ); } } //else // errornotconnected } void GroupWiseAccount::slotConnError() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, i18n( "Error shown when connecting failed", "Kopete was not able to connect to the GroupWise Messenger server for account '%1'.\nPlease check your server and port settings and try again." ).arg( accountId() ) , i18n ("Unable to Connect '%1'").arg( accountId() ) ); disconnect(); } void GroupWiseAccount::slotConnConnected() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; } void GroupWiseAccount::slotCSDisconnected() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Disconnected from Groupwise server." << endl; myself()->setOnlineStatus( protocol()->groupwiseOffline ); TQValueList::ConstIterator it; for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it ) (*it)->setClosed(); setAllContactsStatus( protocol()->groupwiseOffline ); client()->close(); } void GroupWiseAccount::slotCSConnected() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Connected to Groupwise server." << endl; } void GroupWiseAccount::slotCSError( int error ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Got error from ClientStream:" << error << endl; } void GroupWiseAccount::slotCSWarning( int warning ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Got warning from ClientStream:" << warning << endl; } void GroupWiseAccount::slotTLSHandshaken() { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "TLS handshake complete" << endl; int validityResult = m_TQCATLS->certificateValidityResult (); if( validityResult == TQCA::TLS::Valid ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Certificate is valid, continuing." << endl; // valid certificate, continue m_tlsHandler->continueAfterHandshake (); } else { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Certificate is not valid, continuing anyway" << endl; // certificate is not valid, query the user if(handleTLSWarning (validityResult, server (), myself()->contactId ()) == KMessageBox::Continue) { m_tlsHandler->continueAfterHandshake (); } else { disconnect ( Kopete::Account::Manual ); } } } int GroupWiseAccount::handleTLSWarning (int warning, TQString server, TQString accountId) { TQString validityString, code; switch(warning) { case TQCA::TLS::NoCert: validityString = i18n("No certificate was presented."); code = "NoCert"; break; case TQCA::TLS::HostMismatch: validityString = i18n("The host name does not match the one in the certificate."); code = "HostMismatch"; break; case TQCA::TLS::Rejected: validityString = i18n("The Certificate Authority rejected the certificate."); code = "Rejected"; break; case TQCA::TLS::Untrusted: // FIXME: write better error message here validityString = i18n("The certificate is untrusted."); code = "Untrusted"; break; case TQCA::TLS::SignatureFailed: validityString = i18n("The signature is invalid."); code = "SignatureFailed"; break; case TQCA::TLS::InvalidCA: validityString = i18n("The Certificate Authority is invalid."); code = "InvalidCA"; break; case TQCA::TLS::InvalidPurpose: // FIXME: write better error message here validityString = i18n("Invalid certificate purpose."); code = "InvalidPurpose"; break; case TQCA::TLS::SelfSigned: validityString = i18n("The certificate is self-signed."); code = "SelfSigned"; break; case TQCA::TLS::Revoked: validityString = i18n("The certificate has been revoked."); code = "Revoked"; break; case TQCA::TLS::PathLengthExceeded: validityString = i18n("Maximum certificate chain length was exceeded."); code = "PathLengthExceeded"; break; case TQCA::TLS::Expired: validityString = i18n("The certificate has expired."); code = "Expired"; break; case TQCA::TLS::Unknown: default: validityString = i18n("An unknown error occurred trying to validate the certificate."); code = "Unknown"; break; } return KMessageBox::warningContinueCancel(Kopete::UI::Global::mainWidget (), i18n("The certificate of server %1 could not be validated for account %2: %3"). arg(server). arg(accountId). arg(validityString), i18n("GroupWise Connection Certificate Problem"), KStdGuiItem::cont(), TQString("KopeteTLSWarning") + server + code); } void GroupWiseAccount::slotTLSReady( int secLayerCode ) { // i don't know what secLayerCode is for... Q_UNUSED( secLayerCode ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; m_client->start( server(), port(), accountId(), password().cachedValue() ); } void GroupWiseAccount::handleIncomingMessage( const ConferenceEvent & message ) { TQString typeName = "UNKNOWN"; if ( message.type == ReceiveMessage ) typeName = "message"; else if ( message.type == ReceiveAutoReply ) typeName = "autoreply"; else if ( message.type == ReceivedBroadcast ) typeName = "broadcast"; else if ( message.type == ReceivedSystemBroadcast ) typeName = "system broadcast"; kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " received a " << typeName << " from " << message.user << ", to conference: " << message.guid << ", message: " << message.message << endl; GroupWiseContact * sender = contactForDN( message.user ); if ( !sender ) sender = createTemporaryContact( message.user ); // if we receive a message from an Offline contact, they are probably blocking us // but we have to set their status to Unknown so that we can reply to them. kdDebug( GROUPWISE_DEBUG_GLOBAL) << "sender is: " << sender->onlineStatus().description() << endl; if ( sender->onlineStatus() == protocol()->groupwiseOffline ) { sender->setMessageReceivedOffline( true ); } Kopete::ContactPtrList contactList; contactList.append( sender ); // FIND A MESSAGE MANAGER FOR THIS CONTACT GroupWiseChatSession *sess = chatSession( contactList, message.guid, Kopete::Contact::CanCreate ); // add an auto-reply indicator if needed TQString messageMunged = message.message; if ( message.type == ReceiveAutoReply ) { TQString prefix = i18n("Prefix used for automatically generated auto-reply" " messages when the contact is Away, contains contact's name", "Auto reply from %1: " ).arg( sender->metaContact()->displayName() ); messageMunged = prefix + message.message; } if ( message.type == GroupWise::ReceivedBroadcast ) { TQString prefix = i18n("Prefix used for broadcast messages", "Broadcast message from %1: " ).arg( sender->metaContact()->displayName() ); messageMunged = prefix + message.message; } if ( message.type == GroupWise::ReceivedSystemBroadcast ) { TQString prefix = i18n("Prefix used for system broadcast messages", "System Broadcast message from %1: " ).arg( sender->metaContact()->displayName() ); messageMunged = prefix + message.message; } kdDebug(GROUPWISE_DEBUG_GLOBAL) << k_funcinfo << " message before KopeteMessage and appending: " << messageMunged << endl; Kopete::Message * newMessage = new Kopete::Message( message.timeStamp, sender, contactList, messageMunged, Kopete::Message::Inbound, ( message.type == ReceiveAutoReply ) ? Kopete::Message::PlainText : Kopete::Message::RichText ); Q_ASSERT( sess ); sess->appendMessage( *newMessage ); kdDebug(GROUPWISE_DEBUG_GLOBAL) << "message from KopeteMessage: plainbody: " << newMessage->plainBody() << " parsedbody: " << newMessage->parsedBody() << endl; delete newMessage; } void GroupWiseAccount::receiveFolder( const FolderItem & folder ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " objectId: " << folder.id << " sequence: " << folder.sequence << " parentId: " << folder.parentId << " displayName: " << folder.name << endl; if ( folder.parentId != 0 ) { kdWarning( GROUPWISE_DEBUG_GLOBAL ) << " - received a nested folder. These were not supported in GroupWise or Kopete as of Sept 2004, aborting! (parentId = " << folder.parentId << ")" << endl; return; } GWFolder * fld = m_serverListModel->addFolder( folder.id, folder.sequence, folder.name ); Q_ASSERT( fld ); // either find a local group and record these details there, or create a new group to suit Kopete::Group * found = 0; TQPtrList groupList = Kopete::ContactList::self()->groups(); for ( Kopete::Group *grp = groupList.first(); grp; grp = groupList.next() ) { // see if there is already a local group that matches this group TQString groupId = grp->pluginData( protocol(), accountId() + " objectId" ); if ( groupId.isEmpty() ) if ( folder.name == grp->displayName() ) // no match on id, match on display name instead { grp->setPluginData( protocol(), accountId() + " objectId", TQString::number( folder.id ) ); found = grp; break; } if ( folder.id == (unsigned int)groupId.toInt() ) { // was it renamed locally while we were offline? if ( grp->displayName() != folder.name ) { slotKopeteGroupRenamed( grp ); grp->setPluginData( protocol(), accountId() + " serverDisplayName", grp->displayName() ); fld->displayName = grp->displayName(); } found = grp; break; } } if ( !found ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - not found locally, creating Kopete::Group" << endl; Kopete::Group * grp = new Kopete::Group( folder.name ); grp->setPluginData( protocol(), accountId() + " serverDisplayName", folder.name ); grp->setPluginData( protocol(), accountId() + " objectId", TQString::number( folder.id ) ); Kopete::ContactList::self()->addGroup( grp ); } } void GroupWiseAccount::receiveContact( const ContactItem & contact ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " objectId: " << contact.id << ", sequence: " << contact.sequence << ", parentId: " << contact.parentId << ", dn: " << contact.dn << ", displayName: " << contact.displayName << endl; //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "\n dotted notation is '" << protocol()->dnToDotted( contact.dn ) << "'\n" <addContactInstance( contact.id, contact.parentId, contact.sequence, contact.displayName, contact.dn ); Q_ASSERT( gwInst ); GroupWiseContact * c = contactForDN( contact.dn ); // this contact is new to us, create him on the server if ( !c ) { Kopete::MetaContact *metaContact = new Kopete::MetaContact(); metaContact->setDisplayName( contact.displayName ); c = new GroupWiseContact( this, contact.dn, metaContact, contact.id, contact.parentId, contact.sequence ); Kopete::ContactList::self()->addMetaContact( metaContact ); } // add the metacontact to the ContactItem's group, if not there aleady if ( contact.parentId == 0 ) c->metaContact()->addToGroup( Kopete::Group::topLevel() ); else { // check the metacontact is in the group this listing-of-the-contact is in... GWFolder * folder = m_serverListModel->findFolderById( contact.parentId ); if ( !folder ) // inconsistent { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - ERROR - contact's folder doesn't exist on server" << endl; DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() ); dit->item( contact.parentId, contact.id ); // TQObject::connect( dit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); dit->go( true ); return; } Kopete::Group *grp = Kopete::ContactList::self()->findGroup( folder->displayName ); // grp should exist, because we receive the folders from the server before the contacts if ( grp ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - making sure MC is in group " << grp->displayName() << endl; m_dontSync = true; c->metaContact()->addToGroup( grp ); //addToGroup() is safe to call if already a member m_dontSync = false; } } c->setNickName( contact.displayName ); //m_serverListModel->dump(); } void GroupWiseAccount::receiveAccountDetails( const ContactDetails & details ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Auth attribute: " << details.authAttribute << ", Away message: " << details.awayMessage << ", CN" << details.cn << ", DN" << details.dn << ", fullName" << details.fullName << ", surname" << details.surname << ", givenname" << details.givenName << ", status" << details.status << endl; if ( details.cn.lower() == accountId().lower().section('@', 0, 0) ) // incase user set account ID foo@novell.com { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " - got our details in contact list, updating them" << endl; GroupWiseContact * detailsOwner= static_cast( myself() ); detailsOwner->updateDetails( details ); //detailsOwner->setProperty( Kopete::Global::Properties::self()->nickName(), details.fullName ); // Very important, without knowing our DN we can't do much else Q_ASSERT( !details.dn.isEmpty() ); m_client->setUserDN( details.dn ); return; } else { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " - passed someone else's details in contact list!" << endl; } } void GroupWiseAccount::receiveContactUserDetails( const ContactDetails & details ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "Auth attribute: " << details.authAttribute << ", Away message: " << details.awayMessage << ", CN" << details.cn << ", DN" << details.dn << ", fullName" << details.fullName << ", surname" << details.surname << ", givenname" << details.givenName << ", status" << details.status << endl; // HACK: lowercased DN if ( !details.dn.isNull() ) { // are the details for someone in our contact list? GroupWiseContact * detailsOwner = contactForDN( details.dn ); if( detailsOwner ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - updating details for " << details.dn << endl; detailsOwner->updateDetails( details ); } else { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - got details for " << details.dn << ", but they aren't in our contact list!" << endl; } } } GroupWiseContact * GroupWiseAccount::createTemporaryContact( const TQString & dn ) { ContactDetails details = client()->userDetailsManager()->details( dn ); GroupWiseContact * c = static_cast( contacts()[ details.dn.lower() ] ); if ( !c && details.dn != accountId() ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Got a temporary contact DN: " << details.dn << endl; // the client is telling us about a temporary contact we need to know about so add them Kopete::MetaContact *metaContact = new Kopete::MetaContact (); metaContact->setTemporary (true); TQString displayName = details.fullName; if ( displayName.isEmpty() ) displayName = details.givenName + " " + details.surname; metaContact->setDisplayName( displayName ); c = new GroupWiseContact( this, details.dn, metaContact, 0, 0, 0 ); c->updateDetails( details ); c->setProperty( Kopete::Global::Properties::self()->nickName(), protocol()->dnToDotted( details.dn ) ); Kopete::ContactList::self()->addMetaContact( metaContact ); // the contact details probably don't contain status - but we can ask for it if ( details.status == GroupWise::Invalid && isConnected() ) m_client->requestStatus( details.dn ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Notified of existing temporary contact DN: " << details.dn << endl; return c; } void GroupWiseAccount::receiveStatus( const TQString & contactId, TQ_UINT16 status, const TQString &awayMessage ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got status for: " << contactId << ", status: " << status << ", away message: " << awayMessage << endl; GroupWiseContact * c = contactForDN( contactId ); if ( c ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - their KOS is: " << protocol()->gwStatusToKOS( status ).description() << endl; Kopete::OnlineStatus kos = protocol()->gwStatusToKOS( status ); c->setOnlineStatus( kos ); c->setProperty( protocol()->propAwayMessage, awayMessage ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " couldn't find " << contactId << endl; } void GroupWiseAccount::changeOurStatus( GroupWise::Status status, const TQString & awayMessage, const TQString & autoReply ) { if ( status == GroupWise::Offline ) myself()->setOnlineStatus( protocol()->groupwiseAppearOffline ); else myself()->setOnlineStatus( protocol()->gwStatusToKOS( status ) ); myself()->setProperty( protocol()->propAwayMessage, awayMessage ); myself()->setProperty( protocol()->propAutoReply, autoReply ); } void GroupWiseAccount::sendMessage( const GroupWise::ConferenceGuid &guid, const Kopete::Message & message ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; // make an outgoing message if ( isConnected() ) { GroupWise::OutgoingMessage outMsg; outMsg.guid = guid; outMsg.message = message.plainBody(); outMsg.rtfMessage = protocol()->rtfizeText( message.plainBody() ); // make a list of DNs to send to TQStringList addresseeDNs; Kopete::ContactPtrList addressees = message.to(); for ( Kopete::Contact * contact = addressees.first(); contact; contact = addressees.next() ) addresseeDNs.append( static_cast< GroupWiseContact* >( contact )->dn() ); // send the message m_client->sendMessage( addresseeDNs, outMsg ); } } bool GroupWiseAccount::createContact( const TQString& contactId, Kopete::MetaContact* parentContact ) { kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "contactId: " << contactId << endl; // first find all the groups that this contact is a member of // record, in a folderitem, their display names and groupwise object id // Set object id to 0 if not found - they do not exist on the server bool topLevel = false; TQValueList< FolderItem > folders; Kopete::GroupList groupList = parentContact->groups(); for ( Kopete::Group *group = groupList.first(); group; group = groupList.next() ) { if ( group->type() == Kopete::Group::TopLevel ) // no need to create it on the server { topLevel = true; continue; } kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "looking up: " << group->displayName() << endl; GWFolder * fld = m_serverListModel->findFolderByName( group->displayName() ); FolderItem fi; if ( fld ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << fld->displayName << endl; //FIXME - get rid of FolderItem & co fi.parentId = ::tqqt_cast( fld->parent() )->id; fi.id = fld->id; fi.name = fld->displayName; } else { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "folder: " << group->displayName() << "not found in server list model." << endl; fi.parentId = 0; fi.id = 0; fi.name = group->displayName(); } folders.append( fi ); } // find out the sequence number to use for any new folders int highestFreeSequence = m_serverListModel->maxSequenceNumber() + 1; // send this list along with the contact details to the server // CreateContactTask will create the missing folders on the server // and then add the contact to each one // finally it will signal finished(), and we can query it for the details // we gave it earlier and make sure the contact was successfully created. // // Since ToMetaContact expects synchronous contact creation // we have to create the contact optimistically. GroupWiseContact * gc = new GroupWiseContact( this, contactId, parentContact, 0, 0, 0 ); ContactDetails dt = client()->userDetailsManager()->details( contactId ); TQString displayAs; if ( dt.fullName.isEmpty() ) displayAs = dt.givenName + " " + dt.surname; else displayAs = dt.fullName; gc->setNickName( displayAs ); // If the CreateContactTask finishes with an error, we have to // delete the contact we just created, in receiveContactCreated :/ if ( folders.isEmpty() && !topLevel ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "aborting because we didn't find any groups to add them to" << endl; return false; } // get the contact's full name to use as the display name of the created contact CreateContactTask * cct = new CreateContactTask( client()->rootTask() ); cct->contactFromUserId( contactId, parentContact->displayName(), highestFreeSequence, folders, topLevel ); TQObject::connect( cct, TQT_SIGNAL( finished() ), TQT_SLOT( receiveContactCreated() ) ); cct->go( true ); return true; } void GroupWiseAccount::receiveContactCreated() { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; m_serverListModel->dump(); CreateContactTask * cct = ( CreateContactTask * )sender(); if ( cct->success() ) { if ( client()->userDetailsManager()->known( cct->dn() ) ) { ContactDetails dt = client()->userDetailsManager()->details( cct->dn() ); GroupWiseContact * c = contactForDN( cct->dn() ); c->setOnlineStatus( protocol()->gwStatusToKOS( dt.status ) ); c->setNickName( dt.fullName ); c->updateDetails( dt ); } else { client()->requestDetails( TQStringList( cct->dn() ) ); client()->requestStatus( cct->dn() ); } } else { // delete the contact created optimistically using the supplied userid; Kopete::Contact * c = contacts()[ protocol()->dnToDotted( cct->userId() ) ]; if ( c ) { // if the contact creation failed because it already exists on the server, don't delete it if (!cct->statusCode() == NMERR_DUPLICATE_CONTACT ) { if ( c->metaContact()->contacts().count() == 1 ) Kopete::ContactList::self()->removeMetaContact( c->metaContact() ); else delete c; } } KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget (), KMessageBox::Error, i18n ("The contact %1 could not be added to the contact list, with error message: %2"). arg(cct->userId() ).arg( cct->statusString() ), i18n ("Error Adding Contact") ); } } void GroupWiseAccount::deleteContact( GroupWiseContact * contact ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; contact->setDeleting( true ); if ( isConnected() ) { // remove all the instances of this contact from the server's contact list GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() ); GWContactInstanceList::iterator it = instances.begin(); for ( ; it != instances.end(); ++it ) { DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() ); dit->item( ::tqqt_cast( (*it)->parent() )->id, (*it)->id ); TQObject::connect( dit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); dit->go( true ); } } } void GroupWiseAccount::receiveContactDeleted( const ContactItem & instance ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; // an instance of this contact was deleted on the server. // Remove it from the model of the server side list, // and if there are no other instances of this contact, delete the contact m_serverListModel->removeInstanceById( instance.id ); m_serverListModel->dump(); GWContactInstanceList instances = m_serverListModel->instancesWithDn( instance.dn ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - " << instance.dn << " now has " << instances.count() << " instances remaining." << endl; GroupWiseContact * c = contactForDN( instance.dn ); if ( c && instances.count() == 0 && c->deleting() ) { c->deleteLater(); } } void GroupWiseAccount::slotConnectedElsewhere() { KPassivePopup::message( i18n ("Signed in as %1 Elsewhere").arg( accountId() ), i18n( "The parameter is the user's own account id for this protocol", "You have been disconnected from GroupWise Messenger because you signed in as %1 elsewhere" ).arg( accountId() ) , Kopete::UI::Global::mainWidget() ); disconnect(); } void GroupWiseAccount::receiveInvitation( const ConferenceEvent & event ) { // ask the user if they want to accept the invitation or not GroupWiseContact * contactFrom = contactForDN( event.user ); if ( !contactFrom ) contactFrom = createTemporaryContact( event.user ); if ( configGroup()->readEntry( "AlwaysAcceptInvitations" ) == "true" ) { client()->joinConference( event.guid ); } else { ReceiveInvitationDialog * dlg = new ReceiveInvitationDialog( this, event, Kopete::UI::Global::mainWidget(), "invitedialog" ); dlg->show(); } } void GroupWiseAccount::receiveConferenceJoin( const GroupWise::ConferenceGuid & guid, const TQStringList & participants, const TQStringList & invitees ) { // get a new GWMM Kopete::ContactPtrList others; GroupWiseChatSession * sess = chatSession( others, guid, Kopete::Contact::CanCreate); // find each contact and add them to the GWMM, and tell them they are in the conference for ( TQValueList::ConstIterator it = participants.begin(); it != participants.end(); ++it ) { //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " adding participant " << *it << endl; GroupWiseContact * c = contactForDN( *it ); if ( !c ) c = createTemporaryContact( *it ); sess->joined( c ); } // add each invitee too for ( TQValueList::ConstIterator it = invitees.begin(); it != invitees.end(); ++it ) { //kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " adding invitee " << *it << endl; GroupWiseContact * c = contactForDN( *it ); if ( !c ) c = createTemporaryContact( *it ); sess->addInvitee( c ); } sess->view( true )->raise( false ); } void GroupWiseAccount::receiveConferenceJoinNotify( const ConferenceEvent & event ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; GroupWiseChatSession * sess = findChatSessionByGuid( event.guid ); if ( sess ) { GroupWiseContact * c = contactForDN( event.user ); if ( !c ) c = createTemporaryContact( event.user ); sess->joined( c ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl; } void GroupWiseAccount::receiveConferenceLeft( const ConferenceEvent & event ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; GroupWiseChatSession * sess = findChatSessionByGuid( event.guid ); if ( sess ) { GroupWiseContact * c = contactForDN( event.user ); if ( c ) { sess->left( c ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a contact for DN: " << event.user << endl; } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl; } void GroupWiseAccount::receiveInviteDeclined( const ConferenceEvent & event ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; GroupWiseChatSession * sess = findChatSessionByGuid( event.guid ); if ( sess ) { GroupWiseContact * c = contactForDN( event.user ); if ( c ) sess->inviteDeclined( c ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl; } void GroupWiseAccount::receiveInviteNotify( const ConferenceEvent & event ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; GroupWiseChatSession * sess = findChatSessionByGuid( event.guid ); if ( sess ) { GroupWiseContact * c = contactForDN( event.user ); if ( !c ) c = createTemporaryContact( event.user ); sess->addInvitee( c ); Kopete::Message declined = Kopete::Message( myself(), sess->members(), i18n("%1 has been invited to join this conversation.").arg( c->metaContact()->displayName() ), Kopete::Message::Internal, Kopete::Message::PlainText ); sess->appendMessage( declined ); } else kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " couldn't find a GWCS for conference: " << event.guid << endl; } void GroupWiseAccount::slotLeavingConference( GroupWiseChatSession * sess ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "unregistering message manager:" << sess->guid()<< endl; if( isConnected () ) m_client->leaveConference( sess->guid() ); m_chatSessions.remove( sess ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "m_chatSessions now contains:" << m_chatSessions.count() << " managers" << endl; Kopete::ContactPtrList members = sess->members(); for ( Kopete::Contact * contact = members.first(); contact; contact = members.next() ) { static_cast< GroupWiseContact * >( contact )->setMessageReceivedOffline( false ); } } void GroupWiseAccount::slotSetAutoReply() { bool ok; TQRegExp rx( ".*" ); TQRegExpValidator validator( rx, this ); TQString newAutoReply = KInputDialog::getText( i18n( "Enter Auto-Reply Message" ), i18n( "Please enter an Auto-Reply message that will be shown to users who message you while Away or Busy" ), configGroup()->readEntry( "AutoReply" ), &ok, Kopete::UI::Global::mainWidget(), "autoreplymessagedlg", &validator ); if ( ok ) configGroup()->writeEntry( "AutoReply", newAutoReply ); } void GroupWiseAccount::slotTestRTFize() { /* bool ok; const TQString query = TQString::fromLatin1("Enter a string to rtfize:"); TQString testText = KLineEditDlg::getText( query, TQString(), &ok, Kopete::UI::Global::mainWidget() ); if ( ok ) kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Converted text is: '" << protocol()->rtfizeText( testText ) << "'" << endl;*/ // bool ok; // const TQString query = i18n("Enter a contactId:"); // TQString testText = KInputDialog::getText( query, i18n("This is a test dialog and will not be in the final product!" ), TQString(), &ok, Kopete::UI::Global::mainWidget() ); // if ( !ok ) // return; // kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "Trying to add contact: '" << protocol()->rtfizeText( testText ) << "'" << endl; // Kopete::MetaContact *metaContact = new Kopete::MetaContact (); // metaContact->setDisplayName( "Test Add MC" ); // metaContact->setTemporary (true); // createContact( testText, "Test Add Contact", metaContact ); } void GroupWiseAccount::slotPrivacy() { new GroupWisePrivacyDialog( this, Kopete::UI::Global::mainWidget(), "gwprivacydialog" ); } void GroupWiseAccount::slotJoinChatRoom() { new GroupWiseChatSearchDialog( this, Kopete::UI::Global::mainWidget(), "gwjoinchatdialog" ); } bool GroupWiseAccount::isContactBlocked( const TQString & dn ) { if ( isConnected() ) return client()->privacyManager()->isBlocked( dn ); else return false; } void GroupWiseAccount::dumpManagers() { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << " for: " << accountId() << " containing: " << m_chatSessions.count() << " managers " << endl; TQValueList::ConstIterator it; for ( it = m_chatSessions.begin() ; it != m_chatSessions.end(); ++it ) kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "guid: " << (*it)->guid() << endl; } bool GroupWiseAccount::dontSync() { return m_dontSync; } void GroupWiseAccount::syncContact( GroupWiseContact * contact ) { if ( dontSync() ) return; if ( contact != myself() ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl; if ( !isConnected() ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "not connected, can't sync display name or group membership" << endl; return; } // if this is a temporary contact, don't bother if ( contact->metaContact()->isTemporary() ) return; kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = CONTACT '" << contact->nickName() << "' IS IN " << contact->metaContact()->groups().count() << " MC GROUPS, AND HAS " << m_serverListModel->instancesWithDn( contact->dn() ).count() << " CONTACT LIST INSTANCES." << endl; kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR NOOP GROUP MEMBERSHIPS" << endl; // 1) Seek matches between CLIs and MCGs and remove from the lists without taking any action. match on objectid, parentid // 2) Each remaining unmatched pair is a move, initiate and remove - need to take care to always use greatest unused sequence number - if we have to set the sequence number to the following sequence number within the folder, we may have a problem where after the first move, we have to wait for the state of the CLIs to be updated pending the completion of the first move - this would be difficult to cope with, because our current lists would be out of date, or we'd have to restart the sync - assuming the first move created a new matched CLI-MCG pair, we could do that with little cost. // 3) Any remaining entries in MCG list are adds, carry out // 4) Any remaining entries in CLI list are removes, carry out // start by discovering the next free group sequence number in case we have to add any groups int nextFreeSequence = m_serverListModel->maxSequenceNumber() + 1; // 1) // make a list of all the groups the metacontact is in TQPtrList groupList = contact->metaContact()->groups(); // make a list of all the groups this contact is in, according to the server model GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() ); // seek corresponding pairs in both lists and remove // ( for each group ) TQPtrListIterator< Kopete::Group > grpIt( groupList ); while ( *grpIt ) { TQPtrListIterator< Kopete::Group > candidateGrp( groupList ); candidateGrp = grpIt; ++grpIt; GWContactInstanceList::Iterator instIt = instances.begin(); const GWContactInstanceList::Iterator instEnd = instances.end(); // ( see if a contactlist instance matches the group) while ( instIt != instEnd ) { GWContactInstanceList::Iterator candidateInst = instIt; ++instIt; GWFolder * folder = ::tqqt_cast( ( *candidateInst )->parent() ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - Looking for a match, MC grp '" << ( *candidateGrp )->displayName() << "', GWFolder '" << folder->displayName << "', objectId is " << folder->id << endl; if ( ( folder->id == 0 && ( ( *candidateGrp ) == Kopete::Group::topLevel() ) ) || ( ( *candidateGrp )->displayName() == folder->displayName ) ) { //this pair matches, we can remove its members from both lists ) kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - match! removing both entries" << endl; instances.remove( candidateInst ); groupList.remove( *candidateGrp ); break; } } } kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR UNMATCHED PAIRS => GROUP MOVES" << endl; grpIt.toFirst(); // ( take the first pair and carry out a move ) while ( *grpIt && !instances.isEmpty() ) { TQPtrListIterator< Kopete::Group > candidateGrp( groupList ); candidateGrp = grpIt; ++grpIt; GWContactInstanceList::Iterator instIt = instances.begin(); GWFolder * sourceFolder =::tqqt_cast( ( *instIt)->parent() ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - moving contact instance from group '" << sourceFolder->displayName << "' to group '" << ( *candidateGrp )->displayName() << "'" << endl; // create contactItem parameter ContactItem instance; instance.id = ( *instIt )->id; instance.parentId = sourceFolder->id; instance.sequence = ( *instIt )->sequence; instance.dn = ( *instIt )->dn; instance.displayName = contact->nickName(); // identify the destination folder GWFolder * destinationFolder = m_serverListModel->findFolderByName( ( ( *candidateGrp )->displayName() ) ); if ( destinationFolder ) // folder already exists on the server { MoveContactTask * mit = new MoveContactTask( client()->rootTask() ); mit->moveContact( instance, destinationFolder->id ); TQObject::connect( mit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); mit->go(); } else if ( *candidateGrp == Kopete::Group::topLevel() ) { MoveContactTask * mit = new MoveContactTask( client()->rootTask() ); mit->moveContact( instance, 0 ); TQObject::connect( mit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); mit->go(); } else { MoveContactTask * mit = new MoveContactTask( client()->rootTask() ); TQObject::connect( mit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); // discover the next free sequence number and add the group using that mit->moveContactToNewFolder( instance, nextFreeSequence++, ( *candidateGrp )->displayName() ); mit->go( true ); } groupList.remove( candidateGrp ); instances.remove( instIt ); } kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR ADDS" << endl; grpIt.toFirst(); while ( *grpIt ) { TQPtrListIterator< Kopete::Group > candidateGrp( groupList ); candidateGrp = grpIt; ++grpIt; GWFolder * destinationFolder = m_serverListModel->findFolderByName( ( ( *candidateGrp )->displayName() ) ); CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() ); contact->setNickName( contact->metaContact()->displayName() ); // does this group exist on the server? Create the contact appropriately if ( destinationFolder ) { int parentId = destinationFolder->id; ccit->contactFromUserId( contact->dn(), contact->metaContact()->displayName(), parentId ); } else { if ( ( *candidateGrp ) == Kopete::Group::topLevel() ) ccit->contactFromUserId( contact->dn(), contact->metaContact()->displayName(), m_serverListModel->rootFolder->id ); else // discover the next free sequence number and add the group using that ccit->contactFromUserIdAndFolder( contact->dn(), contact->metaContact()->displayName(), nextFreeSequence++, ( *candidateGrp )->displayName() ); } ccit->go( true ); groupList.remove( candidateGrp ); } kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " = LOOKING FOR REMOVES" << endl; GWContactInstanceList::Iterator instIt = instances.begin(); const GWContactInstanceList::Iterator instEnd = instances.end(); // ( remove each remaining contactlist instance, because it doesn't exist locally any more ) while ( instIt != instEnd ) { GWContactInstanceList::Iterator candidateInst = instIt; ++instIt; GWFolder * folder =::tqqt_cast( ( *candidateInst )->parent() ); kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " - remove contact instance '"<< ( *candidateInst )->id << "' in group '" << folder->displayName << "'" << endl; DeleteItemTask * dit = new DeleteItemTask( client()->rootTask() ); dit->item( folder->id, (*candidateInst)->id ); TQObject::connect( dit, TQT_SIGNAL( gotContactDeleted( const ContactItem & ) ), TQT_SLOT( receiveContactDeleted( const ContactItem & ) ) ); dit->go( true ); instances.remove( candidateInst ); } // start an UpdateItem if ( contact->metaContact()->displayName() != contact->nickName() ) { kdDebug( GROUPWISE_DEBUG_GLOBAL ) << " updating the contact's display name to the metacontact's: " << contact->metaContact()->displayName() << endl; // form a list of the contact's groups GWContactInstanceList instances = m_serverListModel->instancesWithDn( contact->dn() ); GWContactInstanceList::Iterator it = instances.begin(); const GWContactInstanceList::Iterator end = instances.end(); for ( ; it != end; ++it ) { TQValueList< ContactItem > instancesToChange; ContactItem instance; instance.id = (*it)->id; instance.parentId = ::tqqt_cast( (*it)->parent() )->id; instance.sequence = (*it)->sequence; instance.dn = contact->dn(); instance.displayName = contact->nickName(); instancesToChange.append( instance ); UpdateContactTask * uct = new UpdateContactTask( client()->rootTask() ); uct->renameContact( contact->metaContact()->displayName(), instancesToChange ); TQObject::connect ( uct, TQT_SIGNAL( finished() ), contact, TQT_SLOT( renamedOnServer() ) ); uct->go( true ); } } } } #include "gwaccount.moc"