2 gwaccount.cpp - Kopete GroupWise Protocol
4 Copyright (c) 2006,2007 Novell, Inc http://www.opensuse.org
5 Copyright (c) 2004 SUSE Linux AG http://www.suse.com
8 Copyright (c) 2003-2007 by Will Stephenson <wstephenson@kde.org>
10 Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
12 *************************************************************************
14 * This library is free software; you can redistribute it and/or *
15 * modify it under the terms of the GNU General Public *
16 * License as published by the Free Software Foundation; either *
17 * version 2 of the License, or (at your option) any later version. *
19 *************************************************************************
22 #include <sys/utsname.h>
24 #include <qvalidator.h>
26 #include <kaboutdata.h>
28 #include <kcomponentdata.h>
30 #include <kinputdialog.h>
32 #include <kmessagebox.h>
33 #include <kpassivepopup.h>
34 #include <kactionmenu.h>
36 #include <kopeteuiglobal.h>
37 #include <kopetecontactlist.h>
38 #include <kopetegroup.h>
39 #include <kopeteglobal.h>
40 #include <kopetemetacontact.h>
41 #include <kopetepassword.h>
42 #include <kopeteview.h>
43 #include <kopeteidletimer.h>
47 #include "gwcontact.h"
48 #include "gwcontactlist.h"
49 #include "gwprotocol.h"
50 #include "gwconnector.h"
51 #include "gwmessagemanager.h"
52 #include "privacymanager.h"
53 #include "qcatlshandler.h"
54 #include "userdetailsmanager.h"
55 #include "tasks/createcontacttask.h"
56 #include "tasks/createcontactinstancetask.h"
57 #include "tasks/deleteitemtask.h"
58 #include "tasks/movecontacttask.h"
59 #include "tasks/updatecontacttask.h"
60 #include "tasks/updatefoldertask.h"
61 #include "ui/gwchatsearchdialog.h"
62 #include "ui_gwprivacy.h"
63 #include "ui/gwprivacydialog.h"
64 #include "ui/gwreceiveinvitationdialog.h"
66 #include "gwaccount.h"
68 GroupWiseAccount::GroupWiseAccount( GroupWiseProtocol
*parent
, const QString
& accountID
, const char *name
)
69 : Kopete::PasswordedAccount ( parent
, accountID
)
72 // Init the myself contact
73 setMyself( new GroupWiseContact( this, accountId(), Kopete::ContactList::self()->myself(), 0, 0, 0 ) );
74 myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline
);
76 // Contact list management
77 QObject::connect( Kopete::ContactList::self(), SIGNAL( groupRenamed( Kopete::Group
*, const QString
& ) ),
78 SLOT( slotKopeteGroupRenamed( Kopete::Group
* ) ) );
79 QObject::connect( Kopete::ContactList::self(), SIGNAL( groupRemoved( Kopete::Group
* ) ),
80 SLOT( slotKopeteGroupRemoved( Kopete::Group
* ) ) );
82 // m_actionBlock = new KAction( KIcon( "msn_blocked" ), label, 0, "actionBlock" );
83 // QObject::connect( m_actionBlock, SIGNAL( triggered( bool ) ), SLOT( slotBlock() ) );
84 m_actionAutoReply
= new KAction ( i18n( "&Set Auto-Reply..." ), 0 );
85 QObject::connect( m_actionAutoReply
, SIGNAL( triggered( bool ) ),
86 SLOT( slotSetAutoReply() ) );
87 m_actionJoinChatRoom
= new KAction ( i18n( "&Join Channel..." ), 0 );
88 QObject::connect( m_actionJoinChatRoom
, SIGNAL( triggered( bool ) ),
89 SLOT( slotJoinChatRoom() ) );
90 m_actionManagePrivacy
= new KAction ( i18n( "&Manage Privacy..." ), 0 );
91 QObject::connect( m_actionManagePrivacy
, SIGNAL( triggered( bool ) ),
92 SLOT( slotPrivacy() ) );
95 m_qcaInit
= new QCA::Initializer
;
101 m_serverListModel
= 0;
104 GroupWiseAccount::~GroupWiseAccount()
109 void GroupWiseAccount::fillActionMenu( KActionMenu
*actionMenu
)
111 Kopete::Account::fillActionMenu( actionMenu
);
113 m_actionAutoReply
->setEnabled( isConnected() );
114 m_actionManagePrivacy
->setEnabled( isConnected() );
115 m_actionJoinChatRoom
->setEnabled( isConnected() );
116 actionMenu
->addAction( m_actionManagePrivacy
);
117 actionMenu
->addAction( m_actionAutoReply
);
118 actionMenu
->addAction( m_actionJoinChatRoom
);
119 /* Used for debugging */
121 theActionMenu->insert( new KAction ( "Test rtfize()", QString(), 0, this,
122 SLOT( slotTestRTFize() ), this,
123 "actionTestRTFize") );
127 int GroupWiseAccount::port() const
129 return configGroup()->readEntry( "Port", 0 );
132 const QString
GroupWiseAccount::server() const
134 return configGroup()->readEntry( "Server", "" );
137 Client
* GroupWiseAccount::client() const
142 GroupWiseProtocol
*GroupWiseAccount::protocol() const
144 return static_cast<GroupWiseProtocol
*>( Kopete::Account::protocol() );
147 GroupWiseChatSession
* GroupWiseAccount::chatSession( Kopete::ContactPtrList others
, const GroupWise::ConferenceGuid
& guid
, Kopete::Contact::CanCreateFlags canCreate
)
149 GroupWiseChatSession
* chatSession
= 0;
150 do // one iteration misuse of do...while to enable an easy drop-out once we locate a manager
152 // do we have a manager keyed by GUID?
153 if ( !guid
.isEmpty() )
155 chatSession
= findChatSessionByGuid( guid
);
158 kDebug() << " found a message manager by GUID: " << guid
;
162 // does the factory know about one, going on the chat members?
163 chatSession
= dynamic_cast<GroupWiseChatSession
*>(
164 Kopete::ChatSessionManager::self()->findChatSession( myself(), others
, protocol() ) );
167 kDebug() << " found a message manager by members with GUID: " << chatSession
->guid();
168 // re-add the returning contact(s) (very likely only one) to the chat
169 foreach ( Kopete::Contact
* returningContact
, others
)
170 chatSession
->joined( static_cast<GroupWiseContact
*>( returningContact
) );
172 if ( !guid
.isEmpty() )
173 chatSession
->setGuid( guid
);
176 // we don't have an existing message manager for this chat, so create one if we may
179 chatSession
= new GroupWiseChatSession( myself(), others
, protocol(), guid
);
181 " created a new message manager with GUID: " << chatSession
->guid() << endl
;
182 m_chatSessions
.append( chatSession
);
183 // listen for the message manager telling us that the user
184 //has left the conference so we remove it from our map
185 QObject::connect( chatSession
, SIGNAL( leavingConference( GroupWiseChatSession
* ) ),
186 SLOT( slotLeavingConference( GroupWiseChatSession
* ) ) );
190 // " no message manager available." << endl;
197 GroupWiseChatSession
* GroupWiseAccount::findChatSessionByGuid( const GroupWise::ConferenceGuid
& guid
)
199 GroupWiseChatSession
* chatSession
= 0;
200 Q3ValueList
<GroupWiseChatSession
*>::ConstIterator it
;
201 for ( it
= m_chatSessions
.begin(); it
!= m_chatSessions
.end(); ++it
)
203 if ( (*it
)->guid() == guid
)
212 GroupWiseContact
* GroupWiseAccount::contactForDN( const QString
& dn
)
214 QHashIterator
<QString
, Kopete::Contact
*> i( contacts() );
215 // check if we have a DN for them
216 while ( i
.hasNext() )
219 GroupWiseContact
* candidate
= static_cast<GroupWiseContact
*>( i
.value() );
220 if ( candidate
&& candidate
->dn() == dn
)
223 // we might have just added the contact with a user ID, try the first section of the dotted dn
224 return static_cast< GroupWiseContact
* >( contacts()[ protocol()->dnToDotted( dn
).section( '.', 0, 0 ) ] );
227 void GroupWiseAccount::setAway( bool away
, const QString
& reason
)
231 if ( Kopete::IdleTimer::self()->idleTime() > 10 ) // don't go AwayIdle unless the user has actually been idle this long
232 setOnlineStatus( protocol()->groupwiseAwayIdle
);
234 setOnlineStatus( protocol()->groupwiseAway
, reason
);
237 setOnlineStatus( protocol()->groupwiseAvailable
);
240 void GroupWiseAccount::connectWithPassword( const QString
&password
)
242 if ( password
.isEmpty() )
247 m_password
= password
;
248 // don't try and connect if we are already connected
249 if ( isConnected () )
252 bool sslPossible
= QCA::isSupported("tls");
256 KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget (), KMessageBox::Error
,
257 i18n ("SSL support could not be initialized for account %1. This is most likely because the QCA TLS plugin is not installed on your system.",
258 myself()->contactId()),
259 i18n ("GroupWise SSL Error"));
267 // set up network classes
268 m_connector
= new KNetworkConnector( 0 );
269 //myConnector->setOptHostPort( "localhost", 8300 );
270 m_connector
->setOptHostPort( server(), port() );
271 m_connector
->setOptSSL( true );
272 Q_ASSERT( QCA::isSupported("tls") );
273 m_QCATLS
= new QCA::TLS
;
274 m_tlsHandler
= new QCATLSHandler( m_QCATLS
);
275 if( QCA::haveSystemStore() )
276 m_QCATLS
->setTrustedCertificates( QCA::systemStore() );
277 m_clientStream
= new ClientStream( m_connector
, m_tlsHandler
);
279 QObject::connect( m_connector
, SIGNAL( error() ), this, SLOT( slotConnError() ) );
280 QObject::connect( m_connector
, SIGNAL( connected() ), this, SLOT( slotConnConnected() ) );
282 QObject::connect (m_clientStream
, SIGNAL (connectionClosed()),
283 this, SLOT (slotCSDisconnected()));
284 QObject::connect (m_clientStream
, SIGNAL (delayedCloseFinished()),
285 this, SLOT (slotCSDisconnected()));
286 // Notify us when the transport layer is connected
287 QObject::connect( m_clientStream
, SIGNAL( connected() ), SLOT( slotCSConnected() ) );
288 // it's necessary to catch this signal and tell the TLS handler to proceed
289 // even if we don't check cert validity
290 QObject::connect( m_tlsHandler
, SIGNAL(tlsHandshaken()), SLOT( slotTLSHandshaken()) );
291 // starts the client once the security layer is up, but see below
292 QObject::connect( m_clientStream
, SIGNAL( securityLayerActivated(int) ), SLOT( slotTLSReady(int) ) );
293 // we could handle login etc in start(), in which case we would emit this signal after that
294 //QObject::connect (jabberClientStream, SIGNAL (authenticated()),
295 // this, SLOT (slotCSAuthenticated ()));
296 // we could also get do the actual login in response to this..
297 //QObject::connect (m_clientStream, SIGNAL (needAuthParams(bool, bool, bool)),
298 // this, SLOT (slotCSNeedAuthParams (bool, bool, bool)));
300 // not implemented: warning
301 QObject::connect( m_clientStream
, SIGNAL( warning(int) ), SLOT( slotCSWarning(int) ) );
302 // not implemented: error
303 QObject::connect( m_clientStream
, SIGNAL( error(int) ), SLOT( slotCSError(int) ) );
305 m_client
= new Client( this, CMSGPRES_GW_6_5
);
307 // NB these are prefixed with QObject:: to avoid any chance of a clash with our connect() methods.
308 // we connected successfully
309 QObject::connect( m_client
, SIGNAL( loggedIn() ), SLOT( slotLoggedIn() ) );
310 // or connection failed
311 QObject::connect( m_client
, SIGNAL( loginFailed() ), SLOT( slotLoginFailed() ) );
313 QObject::connect( m_client
, SIGNAL( folderReceived( const FolderItem
& ) ), SLOT( receiveFolder( const FolderItem
& ) ) );
315 QObject::connect( m_client
, SIGNAL( contactReceived( const ContactItem
& ) ), SLOT( receiveContact( const ContactItem
& ) ) );
316 // contact details listed
317 QObject::connect( m_client
, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails
& ) ), SLOT( receiveContactUserDetails( const GroupWise::ContactDetails
& ) ) );
318 // contact status changed
319 QObject::connect( m_client
, SIGNAL( statusReceived( const QString
&, quint16
, const QString
& ) ), SLOT( receiveStatus( const QString
&, quint16
, const QString
& ) ) );
321 QObject::connect( m_client
, SIGNAL( messageReceived( const ConferenceEvent
& ) ), SLOT( handleIncomingMessage( const ConferenceEvent
& ) ) );
322 // auto reply to one of our messages because the recipient is away
323 QObject::connect( m_client
, SIGNAL( autoReplyReceived( const ConferenceEvent
& ) ), SLOT( handleIncomingMessage( const ConferenceEvent
& ) ) );
325 QObject::connect( m_client
, SIGNAL( ourStatusChanged( GroupWise::Status
, const QString
&, const QString
& ) ), SLOT( changeOurStatus( GroupWise::Status
, const QString
&, const QString
& ) ) );
327 QObject::connect( m_client
,
328 SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid
& ) ),
329 SIGNAL( conferenceCreated( const int, const GroupWise::ConferenceGuid
& ) ) );
330 QObject::connect( m_client
, SIGNAL( conferenceCreationFailed( const int, const int ) ), SIGNAL( conferenceCreationFailed( const int, const int ) ) );
331 QObject::connect( m_client
, SIGNAL( invitationReceived( const ConferenceEvent
& ) ), SLOT( receiveInvitation( const ConferenceEvent
& ) ) );
332 QObject::connect( m_client
, SIGNAL( conferenceLeft( const ConferenceEvent
& ) ), SLOT( receiveConferenceLeft( const ConferenceEvent
& ) ) );
333 QObject::connect( m_client
, SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent
& ) ), SLOT( receiveConferenceJoinNotify( const ConferenceEvent
& ) ) );
334 QObject::connect( m_client
, SIGNAL( inviteNotifyReceived( const ConferenceEvent
& ) ), SLOT( receiveInviteNotify( const ConferenceEvent
& ) ) );
335 QObject::connect( m_client
, SIGNAL( invitationDeclined( const ConferenceEvent
& ) ), SLOT( receiveInviteDeclined( const ConferenceEvent
& ) ) );
337 QObject::connect( m_client
, SIGNAL( conferenceJoined( const GroupWise::ConferenceGuid
&, const QStringList
&, const QStringList
& ) ), SLOT( receiveConferenceJoin( const GroupWise::ConferenceGuid
&, const QStringList
& , const QStringList
& ) ) );
340 QObject::connect( m_client
, SIGNAL( contactTyping( const ConferenceEvent
& ) ),
341 SIGNAL( contactTyping( const ConferenceEvent
& ) ) );
342 QObject::connect( m_client
, SIGNAL( contactNotTyping( const ConferenceEvent
& ) ),
343 SIGNAL( contactNotTyping( const ConferenceEvent
& ) ) );
345 QObject::connect( m_client
, SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails
&) ), SLOT( receiveAccountDetails( const GroupWise::ContactDetails
& ) ) );
346 QObject::connect( m_client
, SIGNAL( connectedElsewhere() ), SLOT( slotConnectedElsewhere() ) );
347 // privacy - contacts can't connect directly to this signal because myself() is initialised before m_client
348 QObject::connect( m_client
->privacyManager(), SIGNAL( privacyChanged( const QString
&, bool ) ), SIGNAL( privacyChanged( const QString
&, bool ) ) );
351 QObject::connect( m_client
, SIGNAL( broadcastReceived( const ConferenceEvent
& ) ), SLOT( handleIncomingMessage( const ConferenceEvent
& ) ) );
352 QObject::connect( m_client
, SIGNAL( systemBroadcastReceived( const ConferenceEvent
& ) ), SLOT( handleIncomingMessage( const ConferenceEvent
& ) ) );
354 struct utsname utsBuf
;
356 m_client
->setClientName ("Kopete");
357 m_client
->setClientVersion ( KGlobal::mainComponent().aboutData()->version () );
358 m_client
->setOSName (QString ("%1 %2").arg (utsBuf
.sysname
, 1).arg (utsBuf
.release
, 2));
360 kDebug () << "Connecting to GroupWise server " << server() << ':' << port();
364 dn
.server
= "reiser.suse.de";
365 m_serverListModel
= new GWContactList( this );
366 myself()->setOnlineStatus( protocol()->groupwiseConnecting
);
367 m_client
->connectToServer( m_clientStream
, dn
, true );
368 QObject::connect( m_client
, SIGNAL( messageSendingFailed() ), SLOT( slotMessageSendingFailed() ) );
371 void GroupWiseAccount::slotMessageSendingFailed()
373 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry
,
374 i18nc("Message Sending Failed using the named local account", "Kopete was not able to send the last message sent on account '%1'.\nIf possible, please send the console output from Kopete to <wstephenson@novell.com> for analysis.", accountId() ) , i18nc("message sending failed using the named local account", "Unable to Send Message on Account '%1'", accountId() ) );
377 void GroupWiseAccount::setOnlineStatus( const Kopete::OnlineStatus
& status
, const Kopete::StatusMessage
&reason
)
380 if ( status
== protocol()->groupwiseUnknown
381 || status
== protocol()->groupwiseConnecting
382 || status
== protocol()->groupwiseInvalid
)
384 kDebug() << " called with invalid status \""
385 << status
.description() << "\"" << endl
;
388 else if ( status
== protocol()->groupwiseOffline
)
390 kDebug() << " DISCONNECTING";
394 else if ( isConnected() )
396 kDebug () << "changing status to \"" << status
.description() << "\"";
397 // Appear Offline is achieved by explicitly setting the status to offline,
398 // rather than disconnecting as when really going offline.
399 if ( status
== protocol()->groupwiseAppearOffline
)
400 m_client
->setStatus( GroupWise::Offline
, reason
.message(), configGroup()->readEntry( "AutoReply", "" ) );
402 m_client
->setStatus( ( GroupWise::Status
)status
.internalStatus(), reason
.message(), configGroup()->readEntry( "AutoReply", "" ) );
407 kDebug () << "Must be connected before changing status";
408 m_initialReason
= reason
.message();
413 void GroupWiseAccount::setStatusMessage( const Kopete::StatusMessage
& statusMessage
)
415 int currentStatus
= myself()->onlineStatus().internalStatus();
416 m_client
->setStatus( ( GroupWise::Status
)currentStatus
, statusMessage
.message(), configGroup()->readEntry( "AutoReply", "" ) );
419 void GroupWiseAccount::disconnect ()
421 disconnect ( Manual
);
424 void GroupWiseAccount::disconnect( Kopete::Account::DisconnectReason reason
)
430 kDebug () << "Still connected, closing connection...";
431 foreach( GroupWiseChatSession
* chatSession
, m_chatSessions
) {
432 chatSession
->setClosed();
435 /* Tell backend class to disconnect. */
439 // 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()
440 delete m_serverListModel
;
441 m_serverListModel
= 0;
443 // make sure that the connection animation gets stopped if we're still
444 // in the process of connecting
445 myself()->setOnlineStatus( GroupWiseProtocol::protocol()->groupwiseOffline
);
447 disconnected( reason
);
448 kDebug() << "Disconnected.";
451 void GroupWiseAccount::cleanup()
454 delete m_clientStream
;
464 void GroupWiseAccount::createConference( const int clientId
, const QStringList
& invitees
)
467 // TODO: remove this it prevents sending a list of participants with the createconf
469 m_client
->createConference( clientId
, invitees
);
472 void GroupWiseAccount::sendInvitation( const GroupWise::ConferenceGuid
& guid
, const QString
& dn
, const QString
& message
)
477 GroupWise::OutgoingMessage msg
;
479 msg
.message
= message
;
480 m_client
->sendInvitation( guid
, dn
, msg
);
484 void GroupWiseAccount::slotLoggedIn()
486 reconcileOfflineChanges();
487 // set local status display
488 myself()->setOnlineStatus( protocol()->groupwiseAvailable
);
489 // set status on server
490 if ( initialStatus() != Kopete::OnlineStatus(Kopete::OnlineStatus::Online
) &&
491 ( ( GroupWise::Status
)initialStatus().internalStatus() != GroupWise::Unknown
) )
493 kDebug() << "Initial status is not online, setting status to " << initialStatus().internalStatus();
494 m_client
->setStatus( ( GroupWise::Status
)initialStatus().internalStatus(), m_initialReason
, configGroup()->readEntry( "AutoReply", "" ) );
498 void GroupWiseAccount::reconcileOfflineChanges()
502 //sanity check the server side model vs our contact list.
503 //Cont->acts might have been removed from some groups or entirely on the server.
504 //Any contact not present on the server should be deleted locally.
506 // for each metacontact group membership:
507 // for each GroupWiseContact
508 // get its contact list instances
509 // get its metacontact's groups
511 // is there no CLI with the same id?
512 // if MC has no other contacts
513 // if MC's groups size is 1
518 // if MC's groups size is 1 and group is topLevel
520 // else // Contact's group membership were changed elsewhere, but we can't change it here without
521 // // affecting other protocols' contacts
522 // set flag to warn user that incompatible changes were made on other client
523 bool conflicts
= false;
524 QHashIterator
<QString
, Kopete::Contact
*> it( contacts() );
525 while( it
.hasNext() )
528 if ( it
.value() == myself() )
531 GroupWiseContact
* c
= static_cast< GroupWiseContact
*>( it
.value() );
532 kDebug() << " reconciling changes for: '" << c
->contactId() << "'";
533 GWContactInstanceList instances
= m_serverListModel
->instancesWithDn( c
->dn() );
534 QListIterator
<Kopete::Group
*> grpIt( c
->metaContact()->groups() );
535 while ( grpIt
.hasNext() )
537 Kopete::Group
* grp
= grpIt
.next();
538 kDebug() << " looking at local group membership: '" << grp
->displayName() << "'";
540 QMutableListIterator
<GWContactInstance
*> instIt( instances
);
541 while ( instIt
.hasNext() )
544 QString groupId
= grp
->pluginData( protocol(), accountId() + " objectId" );
545 if ( groupId
.isEmpty() )
546 if ( grp
== Kopete::Group::topLevel() )
547 groupId
= '0'; // hack the top level's objectId to 0
550 GWFolder
* folder
= qobject_cast
<GWFolder
*>( instIt
.value()->parent() );
551 kDebug() << " local stored groupId #" << groupId
.toInt() << ", remote instance groupId #" << folder
->id
;
552 if ( folder
->id
== ( unsigned int )groupId
.toInt() )
561 if ( c
->metaContact()->contacts().count() == 1 )
563 if ( c
->metaContact()->groups().count() == 1 )
565 kDebug() << "local contact instance " << c
->dn() << " not found on server side list, no matches with contact instances' groups on server, deleting metacontact with only this contact, in one group" << c
->metaContact()->displayName();
566 Kopete::ContactList::self()->removeMetaContact( c
->metaContact() );
571 kDebug() << "contact instance " << c
->dn() << " not found, removing metacontact " << c
->metaContact()->displayName() << " from group " << grp
->displayName();
572 c
->metaContact()->removeFromGroup( grp
);
577 if ( c
->metaContact()->groups().count() == 1 )
579 kDebug() << "contact instance " << c
->dn() << " not found, removing contact " << c
->metaContact()->displayName() << " from metacontact with other contacts ";
584 kDebug() << "metacontact " << c
->metaContact()->displayName( ) << "has multiple children and group membership, and contact " << c
->dn() << " was removed from one group on the server.";
588 } //end while, now check the next group membership
589 } //end while, now check the next groupwise contact
591 // show queuedmessagebox
592 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() );
596 void GroupWiseAccount::slotLoginFailed()
599 password().setWrong();
604 void GroupWiseAccount::slotKopeteGroupRenamed( Kopete::Group
* renamedGroup
)
608 QString objectIdString
= renamedGroup
->pluginData( protocol(), accountId() + " objectId" );
609 // if this group exists on the server
610 if ( !objectIdString
.isEmpty() )
614 GroupWise::FolderItem fi
;
615 fi
.id
= objectIdString
.toInt();
618 fi
.sequence
= renamedGroup
->pluginData( protocol(), accountId() + " sequence" ).toInt();
619 fi
.name
= renamedGroup
->pluginData( protocol(), accountId() + " serverDisplayName" );
621 UpdateFolderTask
* uft
= new UpdateFolderTask( client()->rootTask() );
622 uft
->renameFolder( renamedGroup
->displayName(), fi
);
624 // would be safer to do this in a slot fired on uft's finished() signal
625 renamedGroup
->setPluginData( protocol(), accountId() + " serverDisplayName",
626 renamedGroup
->displayName() );
634 void GroupWiseAccount::slotKopeteGroupRemoved( Kopete::Group
* group
)
639 // the member contacts should be deleted separately, so just delete the folder here
640 // get the folder object id
641 QString objectIdString
= group
->pluginData( protocol(), accountId() + " objectId" );
642 if ( !objectIdString
.isEmpty() )
644 kDebug() << "deleting folder with objectId: " << objectIdString
;
645 int objectId
= objectIdString
.toInt();
648 kDebug() << "deleted folder " << group
->displayName() << " has root folder objectId 0!";
651 DeleteItemTask
* dit
= new DeleteItemTask( client()->rootTask() );
652 dit
->item( 0, objectId
);
653 // the group is deleted synchronously after this slot returns; so there is no point listening for signals
661 void GroupWiseAccount::slotConnError()
664 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry
,
665 i18nc( "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.", accountId() ) , i18n ("Unable to Connect '%1'", accountId() ) );
670 void GroupWiseAccount::slotConnConnected()
675 void GroupWiseAccount::slotCSDisconnected()
677 kDebug () << "Disconnected from Groupwise server.";
678 myself()->setOnlineStatus( protocol()->groupwiseOffline
);
679 setAllContactsStatus( protocol()->groupwiseOffline
);
680 foreach( GroupWiseChatSession
* chatSession
, m_chatSessions
) {
681 chatSession
->setClosed();
683 setAllContactsStatus( protocol()->groupwiseOffline
);
687 void GroupWiseAccount::slotCSConnected()
689 kDebug () << "Connected to Groupwise server.";
693 void GroupWiseAccount::slotCSError( int error
)
695 kDebug () << "Got error from ClientStream:" << error
;
698 void GroupWiseAccount::slotCSWarning( int warning
)
700 kDebug () << "Got warning from ClientStream:" << warning
;
703 void GroupWiseAccount::slotTLSHandshaken()
705 kDebug () << "TLS handshake complete";
706 QCA::TLS::IdentityResult identityResult
= m_QCATLS
->peerIdentityResult();
707 QCA::Validity validityResult
= m_QCATLS
->peerCertificateValidity();
709 if( identityResult
== QCA::TLS::Valid
&& validityResult
== QCA::ValidityGood
)
711 kDebug () << "Certificate is valid, continuing.";
712 // valid certificate, continue
713 m_tlsHandler
->continueAfterHandshake ();
717 kDebug () << "Certificate is not valid, continuing anyway";
718 // certificate is not valid, query the user
719 if ( handleTLSWarning ( identityResult
, validityResult
, server(), myself()->contactId ()) )
721 m_tlsHandler
->continueAfterHandshake ();
725 disconnect ( Kopete::Account::Manual
);
730 int GroupWiseAccount::handleTLSWarning ( QCA::TLS::IdentityResult identityResult
,
731 QCA::Validity validityResult
, QString server
, QString accountId
)
733 QString validityString
, idString
, code
, idCode
;
735 switch ( identityResult
)
737 case QCA::TLS::Valid
:
739 case QCA::TLS::HostMismatch
:
740 idString
= i18n("The host name does not match the one in the certificate.");
741 idCode
= "HostMismatch";
743 case QCA::TLS::InvalidCertificate
:
744 idString
= i18n("The certificate is invalid.");
745 idCode
= "InvalidCert";
747 case QCA::TLS::NoCertificate
:
748 idString
= i18n("No certificate was presented.");
753 switch ( validityResult
)
755 case QCA::ValidityGood
:
757 case QCA::ErrorRejected
:
758 validityString
= i18n("The Certificate Authority rejected the certificate.");
761 case QCA::ErrorUntrusted
:
762 validityString
= i18n("The certificate is not trusted.");
765 case QCA::ErrorSignatureFailed
:
766 validityString
= i18n("The signature is invalid.");
767 code
= "SignatureFailed";
769 case QCA::ErrorInvalidCA
:
770 validityString
= i18n("The Certificate Authority is invalid.");
773 case QCA::ErrorInvalidPurpose
:
774 validityString
= i18n("Invalid certificate purpose.");
775 code
= "InvalidPurpose";
777 case QCA::ErrorSelfSigned
:
778 validityString
= i18n("The certificate is self-signed.");
781 case QCA::ErrorRevoked
:
782 validityString
= i18n("The certificate has been revoked.");
785 case QCA::ErrorPathLengthExceeded
:
786 validityString
= i18n("Maximum certificate chain length was exceeded.");
787 code
= "PathLengthExceeded";
789 case QCA::ErrorExpired
:
790 validityString
= i18n("The certificate has expired.");
793 case QCA::ErrorExpiredCA
:
794 validityString
= i18n("The Certificate Authority has expired.");
797 case QCA::ErrorValidityUnknown
:
798 validityString
= i18n("Validity is unknown.");
799 code
= "ValidityUnknown";
805 if (!idString
.isEmpty())
807 if (!validityString
.isEmpty())
809 message
= i18n("<qt><p>The identity and the certificate of server %1 could not be "
810 "validated for account %2:</p><p>%3</p><p>%4</p><p>Do you want to continue?</p></qt>",
811 server
, accountId
, idString
, validityString
);
815 message
= i18n("<qt><p>The certificate of server %1 could not be validated for "
816 "account %2: %3</p><p>Do you want to continue?</p></qt>",
817 server
, accountId
, idString
);
820 message
= i18n("<qt><p>The certificate of server %1 could not be validated for "
821 "account %2: %3</p><p>Do you want to continue?</p></qt>",
822 server
, accountId
, validityString
);
825 return ( KMessageBox::warningContinueCancel ( Kopete::UI::Global::mainWidget (),
827 i18n("GroupWise Connection Certificate Problem"),
828 KStandardGuiItem::cont(),
829 KStandardGuiItem::cancel(),
830 QString("KopeteTLSWarning") + server
+ idCode
+ code
) == KMessageBox::Continue
);
833 void GroupWiseAccount::slotTLSReady( int secLayerCode
)
835 // i don't know what secLayerCode is for...
836 Q_UNUSED( secLayerCode
);
838 m_client
->start( server(), port(), accountId(), /*QLatin1String("bollax08")*/m_password
);
841 void GroupWiseAccount::handleIncomingMessage( const ConferenceEvent
& message
)
843 QString typeName
= "UNKNOWN";
844 if ( message
.type
== ReceiveMessage
)
845 typeName
= "message";
846 else if ( message
.type
== ReceiveAutoReply
)
847 typeName
= "autoreply";
848 else if ( message
.type
== ReceivedBroadcast
)
849 typeName
= "broadcast";
850 else if ( message
.type
== ReceivedSystemBroadcast
)
851 typeName
= "system broadcast";
853 kDebug() << " received a " << typeName
<< " from " << message
.user
<< ", to conference: " << message
.guid
<< ", message: " << message
.message
;
855 GroupWiseContact
* sender
= contactForDN( message
.user
);
857 sender
= createTemporaryContact( message
.user
);
859 // if we receive a message from an Offline contact, they are probably blocking us
860 // but we have to set their status to Unknown so that we can reply to them.
861 kDebug( GROUPWISE_DEBUG_GLOBAL
) << "sender is: " << sender
->onlineStatus().description() << endl
;
862 if ( sender
->onlineStatus() == protocol()->groupwiseOffline
) {
863 sender
->setMessageReceivedOffline( true );
866 Kopete::ContactPtrList contactList
;
867 contactList
.append( sender
);
868 // FIND A MESSAGE MANAGER FOR THIS CONTACT
869 GroupWiseChatSession
*sess
= chatSession( contactList
, message
.guid
, Kopete::Contact::CanCreate
);
871 // add an auto-reply indicator if needed
872 QString messageMunged
= message
.message
;
873 if ( message
.type
== ReceiveAutoReply
)
875 QString prefix
= i18nc("Prefix used for automatically generated auto-reply"
876 " messages when the contact is Away, contains contact's name",
877 "Auto reply from %1: ", sender
->metaContact()->displayName() );
878 messageMunged
= prefix
+ message
.message
;
880 if ( message
.type
== GroupWise::ReceivedBroadcast
)
882 QString prefix
= i18nc("Prefix used for broadcast messages",
883 "Broadcast message from %1: ", sender
->metaContact()->displayName() );
884 messageMunged
= prefix
+ message
.message
;
886 if ( message
.type
== GroupWise::ReceivedSystemBroadcast
)
888 QString prefix
= i18nc("Prefix used for system broadcast messages",
889 "System Broadcast message from %1: ", sender
->metaContact()->displayName() );
890 messageMunged
= prefix
+ message
.message
;
893 kDebug() << " message before KopeteMessage and appending: " << messageMunged
;
894 Kopete::Message
* newMessage
=
895 new Kopete::Message( sender
, contactList
);
896 newMessage
->setTimestamp( message
.timeStamp
);
897 newMessage
->setDirection( Kopete::Message::Inbound
);
898 if ( message
.type
== ReceiveAutoReply
) {
899 newMessage
->setPlainBody( messageMunged
);
901 newMessage
->setHtmlBody( messageMunged
);
904 sess
->appendMessage( *newMessage
);
905 kDebug() << "message from KopeteMessage: plainbody: " << newMessage
->plainBody() << " parsedbody: " << newMessage
->parsedBody();
909 void GroupWiseAccount::receiveFolder( const FolderItem
& folder
)
912 << " objectId: " << folder
.id
913 << " sequence: " << folder
.sequence
914 << " parentId: " << folder
.parentId
915 << " displayName: " << folder
.name
<< endl
;
916 if ( folder
.parentId
!= 0 )
918 kWarning() << " - received a nested folder. These were not supported in GroupWise or Kopete as of Sept 2004, aborting! (parentId = " << folder
.parentId
<< ')';
922 GWFolder
* fld
= m_serverListModel
->addFolder( folder
.id
, folder
.sequence
, folder
.name
);
925 // either find a local group and record these details there, or create a new group to suit
926 Kopete::Group
* found
= 0;
927 foreach( Kopete::Group
* grp
, Kopete::ContactList::self()->groups() )
929 // see if there is already a local group that matches this group
930 QString groupId
= grp
->pluginData( protocol(), accountId() + " objectId" );
931 if ( groupId
.isEmpty() )
932 if ( folder
.name
== grp
->displayName() ) // no match on id, match on display name instead
934 grp
->setPluginData( protocol(), accountId() + " objectId", QString::number( folder
.id
) );
938 if ( folder
.id
== (unsigned int)groupId
.toInt() )
940 // was it renamed locally while we were offline?
941 if ( grp
->displayName() != folder
.name
)
943 slotKopeteGroupRenamed( grp
);
944 grp
->setPluginData( protocol(), accountId() + " serverDisplayName", grp
->displayName() );
945 fld
->displayName
= grp
->displayName();
955 kDebug() << " - not found locally, creating Kopete::Group";
956 Kopete::Group
* grp
= new Kopete::Group( folder
.name
);
957 grp
->setPluginData( protocol(), accountId() + " serverDisplayName", folder
.name
);
958 grp
->setPluginData( protocol(), accountId() + " objectId", QString::number( folder
.id
) );
959 Kopete::ContactList::self()->addGroup( grp
);
963 void GroupWiseAccount::receiveContact( const ContactItem
& contact
)
966 << " objectId: " << contact
.id
967 << ", sequence: " << contact
.sequence
968 << ", parentId: " << contact
.parentId
969 << ", dn: " << contact
.dn
970 << ", displayName: " << contact
.displayName
<< endl
;
971 //kDebug() << "\n dotted notation is '" << protocol()->dnToDotted( contact.dn ) << "'\n";
973 // add to new style contact list
974 GWContactInstance
* gwInst
= m_serverListModel
->addContactInstance( contact
.id
, contact
.parentId
, contact
.sequence
, contact
.displayName
, contact
.dn
);
977 GroupWiseContact
* c
= contactForDN( contact
.dn
);
978 // this contact is new to us, create him on the server
981 Kopete::MetaContact
*metaContact
= new Kopete::MetaContact();
982 metaContact
->setDisplayName( contact
.displayName
);
983 c
= new GroupWiseContact( this, contact
.dn
, metaContact
, contact
.id
, contact
.parentId
, contact
.sequence
);
984 Kopete::ContactList::self()->addMetaContact( metaContact
);
986 // add the metacontact to the ContactItem's group, if not there aleady
987 if ( contact
.parentId
== 0 )
988 c
->metaContact()->addToGroup( Kopete::Group::topLevel() );
991 // check the metacontact is in the group this listing-of-the-contact is in...
992 GWFolder
* folder
= m_serverListModel
->findFolderById( contact
.parentId
);
993 if ( !folder
) // inconsistent
995 kDebug() << " - ERROR - contact's folder doesn't exist on server";
996 DeleteItemTask
* dit
= new DeleteItemTask( client()->rootTask() );
997 dit
->item( contact
.parentId
, contact
.id
);
998 // QObject::connect( dit, SIGNAL( gotContactDeleted( const ContactItem & ) ), SLOT( receiveContactDeleted( const ContactItem & ) ) );
1002 Kopete::Group
*grp
= Kopete::ContactList::self()->findGroup( folder
->displayName
);
1003 // grp should exist, because we receive the folders from the server before the contacts
1006 kDebug() << " - making sure MC is in group " << grp
->displayName();
1008 c
->metaContact()->addToGroup( grp
); //addToGroup() is safe to call if already a member
1013 c
->setNickName( contact
.displayName
);
1014 //m_serverListModel->dump();
1017 void GroupWiseAccount::receiveAccountDetails( const ContactDetails
& details
)
1020 << "Auth attribute: " << details
.authAttribute
1021 << ", Away message: " << details
.awayMessage
1022 << ", CN" << details
.cn
1023 << ", DN" << details
.dn
1024 << ", fullName" << details
.fullName
1025 << ", surname" << details
.surname
1026 << ", givenname" << details
.givenName
1027 << ", status" << details
.status
1029 if ( details
.cn
.toLower() == accountId().toLower().section('@', 0, 0) ) // incase user set account ID foo@novell.com
1031 kDebug() << " - got our details in contact list, updating them";
1032 GroupWiseContact
* detailsOwner
= static_cast<GroupWiseContact
*>( myself() );
1033 detailsOwner
->updateDetails( details
);
1034 //detailsOwner->setProperty( Kopete::Global::Properties::self()->nickName(), details.fullName );
1036 // Very important, without knowing our DN we can't do much else
1037 Q_ASSERT( !details
.dn
.isEmpty() );
1038 m_client
->setUserDN( details
.dn
);
1043 kDebug() << " - passed someone else's details in contact list!";
1047 void GroupWiseAccount::receiveContactUserDetails( const ContactDetails
& details
)
1050 << "Auth attribute: " << details
.authAttribute
1051 << ", Away message: " << details
.awayMessage
1052 << ", CN" << details
.cn
1053 << ", DN" << details
.dn
1054 << ", fullName" << details
.fullName
1055 << ", surname" << details
.surname
1056 << ", givenname" << details
.givenName
1057 << ", status" << details
.status
1059 // HACK: lowercased DN
1060 if ( !details
.dn
.isNull() )
1062 // are the details for someone in our contact list?
1063 GroupWiseContact
* detailsOwner
= contactForDN( details
.dn
);
1067 kDebug() << " - updating details for " << details
.dn
;
1068 detailsOwner
->updateDetails( details
);
1072 kDebug() << " - got details for " << details
.dn
<< ", but they aren't in our contact list!";
1077 GroupWiseContact
* GroupWiseAccount::createTemporaryContact( const QString
& dn
)
1079 ContactDetails details
= client()->userDetailsManager()->details( dn
);
1080 GroupWiseContact
* c
= static_cast<GroupWiseContact
*>( contacts()[ details
.dn
.toLower() ] );
1081 if ( !c
&& details
.dn
!= accountId() )
1083 kDebug() << "Got a temporary contact DN: " << details
.dn
;
1084 // the client is telling us about a temporary contact we need to know about so add them
1085 Kopete::MetaContact
*metaContact
= new Kopete::MetaContact ();
1086 metaContact
->setTemporary (true);
1087 QString displayName
= details
.fullName
;
1088 if ( displayName
.isEmpty() )
1089 displayName
= details
.givenName
+ ' ' + details
.surname
;
1091 metaContact
->setDisplayName( displayName
);
1092 c
= new GroupWiseContact( this, details
.dn
, metaContact
, 0, 0, 0 );
1093 c
->updateDetails( details
);
1094 c
->setProperty( Kopete::Global::Properties::self()->nickName(), protocol()->dnToDotted( details
.dn
) );
1095 Kopete::ContactList::self()->addMetaContact( metaContact
);
1096 // the contact details probably don't contain status - but we can ask for it
1097 if ( details
.status
== GroupWise::Invalid
&& isConnected() )
1098 m_client
->requestStatus( details
.dn
);
1101 kDebug() << "Notified of existing temporary contact DN: " << details
.dn
;
1105 void GroupWiseAccount::receiveStatus( const QString
& contactId
, quint16 status
, const QString
&awayMessage
)
1107 kDebug() << "got status for: " << contactId
<< ", status: " << status
<< ", away message: " << awayMessage
;
1108 GroupWiseContact
* c
= contactForDN( contactId
);
1111 kDebug() << " - their KOS is: " << protocol()->gwStatusToKOS( status
).description();
1112 Kopete::OnlineStatus kos
= protocol()->gwStatusToKOS( status
);
1113 c
->setOnlineStatus( kos
);
1114 c
->setStatusMessage( awayMessage
);
1117 kDebug() << " couldn't find " << contactId
;
1120 void GroupWiseAccount::changeOurStatus( GroupWise::Status status
, const QString
& awayMessage
, const QString
& autoReply
)
1122 if ( status
== GroupWise::Offline
)
1123 myself()->setOnlineStatus( protocol()->groupwiseAppearOffline
);
1125 myself()->setOnlineStatus( protocol()->gwStatusToKOS( status
) );
1126 myself()->setStatusMessage( awayMessage
);
1127 myself()->setProperty( protocol()->propAutoReply
, autoReply
);
1130 void GroupWiseAccount::sendMessage( const GroupWise::ConferenceGuid
&guid
, const Kopete::Message
& message
)
1133 // make an outgoing message
1134 if ( isConnected() )
1136 GroupWise::OutgoingMessage outMsg
;
1138 outMsg
.message
= message
.plainBody();
1139 outMsg
.rtfMessage
= protocol()->rtfizeText( message
.plainBody() );
1140 // make a list of DNs to send to
1141 QStringList addresseeDNs
;
1142 Kopete::ContactPtrList addressees
= message
.to();
1143 foreach( Kopete::Contact
* contact
, message
.to() )
1144 addresseeDNs
.append( static_cast< GroupWiseContact
* >( contact
)->dn() );
1146 m_client
->sendMessage( addresseeDNs
, outMsg
);
1150 bool GroupWiseAccount::createContact( const QString
& contactId
, Kopete::MetaContact
* parentContact
)
1152 kDebug () << "contactId: " << contactId
;
1154 // first find all the groups that this contact is a member of
1155 // record, in a folderitem, their display names and groupwise object id
1156 // Set object id to 0 if not found - they do not exist on the server
1157 bool topLevel
= false;
1158 Q3ValueList
< FolderItem
> folders
;
1159 foreach ( Kopete::Group
*group
, parentContact
->groups() )
1161 if ( group
->type() == Kopete::Group::TopLevel
) // no need to create it on the server
1167 kDebug() << "looking up: " << group
->displayName();
1168 GWFolder
* fld
= m_serverListModel
->findFolderByName( group
->displayName() );
1172 kDebug() << fld
->displayName
;
1173 //FIXME - get rid of FolderItem & co
1174 fi
.parentId
= qobject_cast
<GWFolder
*>( fld
->parent() )->id
;
1176 fi
.name
= fld
->displayName
;
1180 kDebug() << "folder: " << group
->displayName() <<
1181 "not found in server list model." << endl
;
1184 fi
.name
= group
->displayName();
1186 folders
.append( fi
);
1189 // find out the sequence number to use for any new folders
1190 int highestFreeSequence
= m_serverListModel
->maxSequenceNumber() + 1;
1192 // send this list along with the contact details to the server
1193 // CreateContactTask will create the missing folders on the server
1194 // and then add the contact to each one
1195 // finally it will signal finished(), and we can query it for the details
1196 // we gave it earlier and make sure the contact was successfully created.
1198 // Since ToMetaContact expects synchronous contact creation
1199 // we have to create the contact optimistically.
1200 GroupWiseContact
* gc
= new GroupWiseContact( this, contactId
, parentContact
, 0, 0, 0 );
1201 ContactDetails dt
= client()->userDetailsManager()->details( contactId
);
1203 if ( dt
.fullName
.isEmpty() )
1204 displayAs
= dt
.givenName
+ ' ' + dt
.surname
;
1206 displayAs
= dt
.fullName
;
1207 Q_ASSERT( !displayAs
.isEmpty() );
1208 gc
->setNickName( displayAs
);
1209 // If the CreateContactTask finishes with an error, we have to
1210 // delete the contact we just created, in receiveContactCreated :/
1212 if ( folders
.isEmpty() && !topLevel
)
1214 kDebug() << "aborting because we didn't find any groups to add them to";
1218 // get the contact's full name to use as the display name of the created contact
1219 CreateContactTask
* cct
= new CreateContactTask( client()->rootTask() );
1220 cct
->contactFromUserId( contactId
, displayAs
, highestFreeSequence
, folders
, topLevel
);
1221 QObject::connect( cct
, SIGNAL( finished() ), SLOT( receiveContactCreated() ) );
1226 void GroupWiseAccount::receiveContactCreated()
1229 m_serverListModel
->dump();
1231 CreateContactTask
* cct
= ( CreateContactTask
* )sender();
1232 if ( cct
->success() )
1234 if ( client()->userDetailsManager()->known( cct
->dn() ) )
1236 ContactDetails dt
= client()->userDetailsManager()->details( cct
->dn() );
1237 GroupWiseContact
* c
= contactForDN( cct
->dn() );
1240 c
->setOnlineStatus( protocol()->gwStatusToKOS( dt
.status
) );
1241 c
->setNickName( dt
.fullName
);
1242 c
->updateDetails( dt
);
1246 client()->requestDetails( QStringList( cct
->dn() ) );
1247 client()->requestStatus( cct
->dn() );
1252 // delete the contact created optimistically using the supplied userid;
1253 Kopete::Contact
* c
= contacts()[ protocol()->dnToDotted( cct
->userId() ) ];
1256 // if the contact creation failed because it already exists on the server, don't delete it
1257 if (cct
->statusCode() != NMERR_DUPLICATE_CONTACT
)
1259 if ( c
->metaContact()->contacts().count() == 1 )
1260 Kopete::ContactList::self()->removeMetaContact( c
->metaContact() );
1266 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget (), KMessageBox::Error
,
1267 i18n ("The contact %1 could not be added to the contact list, with error message: %2",
1268 cct
->userId(), cct
->statusString() ),
1269 i18n ("Error Adding Contact") );
1273 void GroupWiseAccount::deleteContact( GroupWiseContact
* contact
)
1276 contact
->setDeleting( true );
1277 if ( isConnected() )
1279 // remove all the instances of this contact from the server's contact list
1280 GWContactInstanceList instances
= m_serverListModel
->instancesWithDn( contact
->dn() );
1281 GWContactInstanceList::iterator it
= instances
.begin();
1282 for ( ; it
!= instances
.end(); ++it
)
1284 DeleteItemTask
* dit
= new DeleteItemTask( client()->rootTask() );
1285 dit
->item( qobject_cast
<GWFolder
*>( (*it
)->parent() )->id
, (*it
)->id
);
1286 QObject::connect( dit
, SIGNAL( gotContactDeleted( const ContactItem
& ) ), SLOT( receiveContactDeleted( const ContactItem
& ) ) );
1292 void GroupWiseAccount::receiveContactDeleted( const ContactItem
& instance
)
1295 // an instance of this contact was deleted on the server.
1296 // Remove it from the model of the server side list,
1297 // and if there are no other instances of this contact, delete the contact
1298 m_serverListModel
->removeInstanceById( instance
.id
);
1299 m_serverListModel
->dump();
1301 GWContactInstanceList instances
= m_serverListModel
->instancesWithDn( instance
.dn
);
1302 kDebug() << " - " << instance
.dn
<< " now has " << instances
.count() << " instances remaining.";
1303 GroupWiseContact
* c
= contactForDN( instance
.dn
);
1304 if ( c
&& instances
.count() == 0 && c
->deleting() )
1311 void GroupWiseAccount::slotConnectedElsewhere()
1313 KPassivePopup::message( i18n ( "Signed in as %1 Elsewhere", accountId() ), i18nc( "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", accountId() ) , Kopete::UI::Global::mainWidget() );
1317 void GroupWiseAccount::receiveInvitation( const ConferenceEvent
& event
)
1319 // ask the user if they want to accept the invitation or not
1320 GroupWiseContact
* contactFrom
= contactForDN( event
.user
);
1322 contactFrom
= createTemporaryContact( event
.user
);
1323 if ( configGroup()->readEntry( "AlwaysAcceptInvitations",false ) == true )
1325 client()->joinConference( event
.guid
);
1329 ReceiveInvitationDialog
* dlg
= new ReceiveInvitationDialog( this, event
,
1330 Kopete::UI::Global::mainWidget(), "invitedialog" );
1336 void GroupWiseAccount::receiveConferenceJoin( const GroupWise::ConferenceGuid
& guid
, const QStringList
& participants
, const QStringList
& invitees
)
1339 Kopete::ContactPtrList others
;
1340 GroupWiseChatSession
* sess
= chatSession( others
, guid
, Kopete::Contact::CanCreate
);
1341 // find each contact and add them to the GWMM, and tell them they are in the conference
1342 QStringListIterator
joinerIt( participants
);
1343 while ( joinerIt
.hasNext() )
1345 //kDebug() << " adding participant " << *it;
1346 QString dn
= joinerIt
.next();
1347 GroupWiseContact
* c
= contactForDN( dn
);
1349 c
= createTemporaryContact( dn
);
1352 // add each invitee too
1353 QStringListIterator
inviteeIt( invitees
);
1354 while ( inviteeIt
.hasNext() )
1356 //kDebug() << " adding invitee " << *it;
1357 QString dn
= inviteeIt
.next();
1358 GroupWiseContact
* c
= contactForDN( dn
);
1360 c
= createTemporaryContact( dn
);
1361 sess
->addInvitee( c
);
1363 sess
->view( true )->raise( false );
1366 void GroupWiseAccount::receiveConferenceJoinNotify( const ConferenceEvent
& event
)
1369 GroupWiseChatSession
* sess
= findChatSessionByGuid( event
.guid
);
1372 GroupWiseContact
* c
= contactForDN( event
.user
);
1374 c
= createTemporaryContact( event
.user
);
1378 kDebug() << " couldn't find a GWCS for conference: " << event
.guid
;
1381 void GroupWiseAccount::receiveConferenceLeft( const ConferenceEvent
& event
)
1384 GroupWiseChatSession
* sess
= findChatSessionByGuid( event
.guid
);
1387 GroupWiseContact
* c
= contactForDN( event
.user
);
1393 kDebug() << " couldn't find a contact for DN: " << event
.user
;
1396 kDebug() << " couldn't find a GWCS for conference: " << event
.guid
;
1400 void GroupWiseAccount::receiveInviteDeclined( const ConferenceEvent
& event
)
1403 GroupWiseChatSession
* sess
= findChatSessionByGuid( event
.guid
);
1406 GroupWiseContact
* c
= contactForDN( event
.user
);
1408 sess
->inviteDeclined( c
);
1411 kDebug() << " couldn't find a GWCS for conference: " << event
.guid
;
1414 void GroupWiseAccount::receiveInviteNotify( const ConferenceEvent
& event
)
1417 GroupWiseChatSession
* sess
= findChatSessionByGuid( event
.guid
);
1420 GroupWiseContact
* c
= contactForDN( event
.user
);
1422 c
= createTemporaryContact( event
.user
);
1424 sess
->addInvitee( c
);
1425 Kopete::Message
declined( myself(), sess
->members() );
1426 declined
.setPlainBody( i18n("%1 has been invited to join this conversation.", c
->metaContact()->displayName() ) );
1427 sess
->appendMessage( declined
);
1430 kDebug() << " couldn't find a GWCS for conference: " << event
.guid
;
1433 void GroupWiseAccount::slotLeavingConference( GroupWiseChatSession
* sess
)
1435 kDebug() << "unregistering message manager:" << sess
->guid();
1436 if( isConnected () )
1437 m_client
->leaveConference( sess
->guid() );
1438 m_chatSessions
.remove( sess
);
1439 kDebug() << "m_chatSessions now contains:" << m_chatSessions
.count() << " managers";
1440 Kopete::ContactPtrList members
= sess
->members();
1441 foreach( Kopete::Contact
* contact
, members
)
1443 static_cast< GroupWiseContact
* >( contact
)->setMessageReceivedOffline( false );
1447 void GroupWiseAccount::slotSetAutoReply()
1451 QRegExpValidator
validator( rx
, this );
1452 QString newAutoReply
= KInputDialog::getText( i18n( "Enter Auto-Reply Message" ),
1453 i18n( "Please enter an Auto-Reply message that will be shown to users who message you while Away or Busy" ), configGroup()->readEntry( "AutoReply", "" ),
1454 &ok
, Kopete::UI::Global::mainWidget(), &validator
);
1456 configGroup()->writeEntry( "AutoReply", newAutoReply
);
1459 void GroupWiseAccount::slotTestRTFize()
1462 const QString query = QString::fromLatin1("Enter a string to rtfize:");
1463 QString testText = KLineEditDlg::getText( query, QString(), &ok, Kopete::UI::Global::mainWidget() );
1465 kDebug() << "Converted text is: '" << protocol()->rtfizeText( testText ) << "'";*/
1468 // const QString query = i18n("Enter a contactId:");
1469 // QString testText = KInputDialog::getText( query, i18n("This is a test dialog and will not be in the final product!" ), QString(), &ok, Kopete::UI::Global::mainWidget() );
1472 // kDebug() << "Trying to add contact: '" << protocol()->rtfizeText( testText ) << "'";
1473 // Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
1474 // metaContact->setDisplayName( "Test Add MC" );
1475 // metaContact->setTemporary (true);
1476 // createContact( testText, "Test Add Contact", metaContact );
1479 void GroupWiseAccount::slotPrivacy()
1481 new GroupWisePrivacyDialog( this, Kopete::UI::Global::mainWidget(), "gwprivacydialog" );
1484 void GroupWiseAccount::slotJoinChatRoom()
1486 new GroupWiseChatSearchDialog( this, Kopete::UI::Global::mainWidget(), "gwjoinchatdialog" );
1489 bool GroupWiseAccount::isContactBlocked( const QString
& dn
)
1491 if ( isConnected() )
1492 return client()->privacyManager()->isBlocked( dn
);
1497 void GroupWiseAccount::dumpManagers()
1499 kDebug() << " for: " << accountId()
1500 << " containing: " << m_chatSessions
.count() << " managers " << endl
;
1501 Q3ValueList
<GroupWiseChatSession
*>::ConstIterator it
;
1502 for ( it
= m_chatSessions
.begin() ; it
!= m_chatSessions
.end(); ++it
)
1503 kDebug() << "guid: " << (*it
)->guid();
1506 bool GroupWiseAccount::dontSync()
1511 void GroupWiseAccount::syncContact( GroupWiseContact
* contact
)
1516 if ( contact
!= myself() )
1519 if ( !isConnected() )
1521 kDebug() << "not connected, can't sync display name or group membership";
1525 // if this is a temporary contact, don't bother
1526 if ( contact
->metaContact()->isTemporary() )
1529 kDebug() << " = CONTACT '" << contact
->nickName() << "' IS IN " << contact
->metaContact()->groups().count() << " MC GROUPS, AND HAS " << m_serverListModel
->instancesWithDn( contact
->dn() ).count() << " CONTACT LIST INSTANCES.";
1531 kDebug() << " = LOOKING FOR NOOP GROUP MEMBERSHIPS";
1532 // 1) Seek matches between CLIs and MCGs and remove from the lists without taking any action. match on objectid, parentid
1533 // 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.
1534 // 3) Any remaining entries in MCG list are adds, carry out
1535 // 4) Any remaining entries in CLI list are removes, carry out
1537 // start by discovering the next free group sequence number in case we have to add any groups
1538 int nextFreeSequence
= m_serverListModel
->maxSequenceNumber() + 1;
1540 // make a list of all the groups the metacontact is in
1541 QList
<Kopete::Group
*> groupList
= contact
->metaContact()->groups();
1542 // make a list of all the groups this contact is in, according to the server model
1543 GWContactInstanceList instances
= m_serverListModel
->instancesWithDn( contact
->dn() );
1545 // seek corresponding pairs in both lists and remove
1546 // ( for each group )
1547 QMutableListIterator
< Kopete::Group
*> grpIt( groupList
);
1548 while ( grpIt
.hasNext() )
1552 QMutableListIterator
< GWContactInstance
*> instIt( instances
);
1553 // ( see if a contact list instance matches the group)
1554 while ( instIt
.hasNext() )
1557 GWFolder
* folder
= qobject_cast
<GWFolder
*>( instIt
.value()->parent() );
1558 kDebug() << " - Looking for a match, MC grp '"
1559 << grpIt
.value()->displayName()
1560 << "', GWFolder '" << folder
->displayName
<< "', objectId is " << folder
->id
<< endl
;
1562 if ( ( folder
->id
== 0 && ( grpIt
.value() == Kopete::Group::topLevel() ) )
1563 || ( grpIt
.value()->displayName() == folder
->displayName
) )
1565 //this pair matches, we can remove its members from both lists )
1566 kDebug() << " - match! removing both entries";
1574 kDebug() << " = LOOKING FOR UNMATCHED PAIRS => GROUP MOVES";
1576 // ( take the first pair and carry out a move )
1577 while ( grpIt
.hasNext() && !instances
.isEmpty() )
1580 GWContactInstance
* cliInstance
= instances
.takeFirst();
1581 GWFolder
* sourceFolder
= qobject_cast
<GWFolder
*>( cliInstance
->parent() );
1582 kDebug() << " - moving contact instance from group '" << sourceFolder
->displayName
<< "' to group '" << grpIt
.value()->displayName() << "'";
1584 // create contactItem parameter
1585 ContactItem instance
;
1586 instance
.id
= cliInstance
->id
;
1587 instance
.parentId
= sourceFolder
->id
;
1588 instance
.sequence
= cliInstance
->sequence
;
1589 instance
.dn
= cliInstance
->dn
;
1590 instance
.displayName
= contact
->nickName();
1591 // identify the destination folder
1592 GWFolder
* destinationFolder
= m_serverListModel
->findFolderByName( grpIt
.value()->displayName() );
1593 if ( destinationFolder
) // folder already exists on the server
1595 MoveContactTask
* mit
= new MoveContactTask( client()->rootTask() );
1596 mit
->moveContact( instance
, destinationFolder
->id
);
1597 QObject::connect( mit
, SIGNAL( gotContactDeleted( const ContactItem
& ) ), SLOT( receiveContactDeleted( const ContactItem
& ) ) );
1600 else if ( grpIt
.value() == Kopete::Group::topLevel() )
1602 MoveContactTask
* mit
= new MoveContactTask( client()->rootTask() );
1603 mit
->moveContact( instance
, 0 );
1604 QObject::connect( mit
, SIGNAL( gotContactDeleted( const ContactItem
& ) ), SLOT( receiveContactDeleted( const ContactItem
& ) ) );
1609 MoveContactTask
* mit
= new MoveContactTask( client()->rootTask() );
1610 QObject::connect( mit
, SIGNAL( gotContactDeleted( const ContactItem
& ) ),
1611 SLOT( receiveContactDeleted( const ContactItem
& ) ) );
1612 // discover the next free sequence number and add the group using that
1613 mit
->moveContactToNewFolder( instance
, nextFreeSequence
++,
1614 grpIt
.value()->displayName() );
1620 kDebug() << " = LOOKING FOR ADDS";
1622 while ( grpIt
.hasNext() )
1625 GWFolder
* destinationFolder
= m_serverListModel
->findFolderByName( grpIt
.value()->displayName() );
1626 CreateContactInstanceTask
* ccit
= new CreateContactInstanceTask( client()->rootTask() );
1628 contact
->setNickName( contact
->metaContact()->displayName() );
1629 // does this group exist on the server? Create the contact appropriately
1630 if ( destinationFolder
)
1632 int parentId
= destinationFolder
->id
;
1633 ccit
->contactFromUserId( contact
->dn(), contact
->metaContact()->displayName(), parentId
);
1637 if ( grpIt
.value() == Kopete::Group::topLevel() )
1638 ccit
->contactFromUserId( contact
->dn(), contact
->metaContact()->displayName(),
1639 m_serverListModel
->rootFolder
->id
);
1641 // discover the next free sequence number and add the group using that
1642 ccit
->contactFromUserIdAndFolder( contact
->dn(), contact
->metaContact()->displayName(),
1643 nextFreeSequence
++, grpIt
.value()->displayName() );
1649 kDebug() << " = LOOKING FOR REMOVES";
1650 QMutableListIterator
<GWContactInstance
*> instIt( instances
);
1651 // ( remove each remaining contact list instance, because it doesn't exist locally any more )
1652 while ( instIt
.hasNext() )
1655 GWFolder
* folder
=qobject_cast
<GWFolder
*>( ( instIt
.value() )->parent() );
1656 kDebug() << " - remove contact instance '"<< ( instIt
.value() )->id
<< "' in group '" << folder
->displayName
<< "'";
1658 DeleteItemTask
* dit
= new DeleteItemTask( client()->rootTask() );
1659 dit
->item( folder
->id
, instIt
.value()->id
);
1660 QObject::connect( dit
, SIGNAL( gotContactDeleted( const ContactItem
& ) ), SLOT( receiveContactDeleted( const ContactItem
& ) ) );
1666 // start an UpdateItem
1667 if ( contact
->metaContact()->displayName() != contact
->nickName() )
1669 kDebug() << " updating the contact's display name to the metacontact's: " << contact
->metaContact()->displayName();
1670 foreach ( GWContactInstance
* instance
, m_serverListModel
->instancesWithDn( contact
->dn() ) )
1672 QList
< ContactItem
> instancesToChange
;
1674 item
.id
= instance
->id
;
1675 item
.parentId
= qobject_cast
<GWFolder
*>( instance
->parent() )->id
;
1676 item
.sequence
= instance
->sequence
;
1677 item
.dn
= contact
->dn();
1678 item
.displayName
= contact
->nickName();
1679 instancesToChange
.append( item
);
1681 UpdateContactTask
* uct
= new UpdateContactTask( client()->rootTask() );
1682 uct
->renameContact( contact
->metaContact()->displayName(), instancesToChange
);
1683 QObject::connect ( uct
, SIGNAL( finished() ), contact
, SLOT( renamedOnServer() ) );
1690 #include "gwaccount.moc"