make webinterface translatable. there are around 20 short strings, all with context...
[kdenetwork.git] / kopete / protocols / groupwise / gwaccount.cpp
blob659eb3c1328a3dd758d38166a2e1f963bbbf005e
1 /*
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
7 Based on Testbed
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 *************************************************************************
13 * *
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. *
18 * *
19 *************************************************************************
22 #include <sys/utsname.h>
24 #include <qvalidator.h>
26 #include <kaboutdata.h>
27 #include <kconfig.h>
28 #include <kcomponentdata.h>
29 #include <kdebug.h>
30 #include <kinputdialog.h>
31 #include <klocale.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>
45 #include "client.h"
46 #include <QtCrypto>
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 )
71 Q_UNUSED( name );
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() ) );
94 m_connector = 0;
95 m_qcaInit = new QCA::Initializer;
96 m_QCATLS = 0;
97 m_tlsHandler = 0;
98 m_clientStream = 0;
99 m_client = 0;
100 m_dontSync = false;
101 m_serverListModel = 0;
104 GroupWiseAccount::~GroupWiseAccount()
106 cleanup();
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
139 return m_client;
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 );
156 if ( chatSession )
158 kDebug() << " found a message manager by GUID: " << guid;
159 break;
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() ) );
165 if ( chatSession )
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 );
174 break;
176 // we don't have an existing message manager for this chat, so create one if we may
177 if ( canCreate )
179 chatSession = new GroupWiseChatSession( myself(), others, protocol(), guid );
180 kDebug() <<
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 * ) ) );
187 break;
189 //kDebug() <<
190 // " no message manager available." << endl;
192 while ( 0 );
193 //dumpManagers();
194 return chatSession;
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 )
205 chatSession = *it;
206 break;
209 return chatSession;
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() )
218 i.next();
219 GroupWiseContact * candidate = static_cast<GroupWiseContact*>( i.value() );
220 if ( candidate && candidate->dn() == dn )
221 return candidate;
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 )
229 if ( away )
231 if ( Kopete::IdleTimer::self()->idleTime() > 10 ) // don't go AwayIdle unless the user has actually been idle this long
232 setOnlineStatus( protocol()->groupwiseAwayIdle );
233 else
234 setOnlineStatus( protocol()->groupwiseAway, reason );
236 else
237 setOnlineStatus( protocol()->groupwiseAvailable );
240 void GroupWiseAccount::connectWithPassword( const QString &password )
242 if ( password.isEmpty() )
244 disconnect();
245 return;
247 m_password = password;
248 // don't try and connect if we are already connected
249 if ( isConnected () )
250 return;
252 bool sslPossible = QCA::isSupported("tls");
254 if (!sslPossible)
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"));
260 return;
262 if ( m_client )
264 m_client->close();
265 cleanup();
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() ) );
312 // folder listed
313 QObject::connect( m_client, SIGNAL( folderReceived( const FolderItem & ) ), SLOT( receiveFolder( const FolderItem & ) ) );
314 // contact listed
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 & ) ) );
320 // incoming message
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 & ) ) );
326 // conference events
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 & ) ) );
339 // typing events
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 & ) ) );
344 // misc
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 ) ) );
350 // GW7
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;
355 uname (&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();
362 NovellDN dn;
363 dn.dn = "maeuschen";
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 )
379 kDebug () ;
380 if ( status == protocol()->groupwiseUnknown
381 || status == protocol()->groupwiseConnecting
382 || status == protocol()->groupwiseInvalid )
384 kDebug() << " called with invalid status \""
385 << status.description() << "\"" << endl;
387 // going offline
388 else if ( status == protocol()->groupwiseOffline )
390 kDebug() << " DISCONNECTING";
391 disconnect();
393 // changing status
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", "" ) );
401 else
402 m_client->setStatus( ( GroupWise::Status )status.internalStatus(), reason.message(), configGroup()->readEntry( "AutoReply", "" ) );
404 // going online
405 else
407 kDebug () << "Must be connected before changing status";
408 m_initialReason = reason.message();
409 connect( status );
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 )
426 kDebug() ;
428 if( isConnected () )
430 kDebug () << "Still connected, closing connection...";
431 foreach( GroupWiseChatSession * chatSession, m_chatSessions ) {
432 chatSession->setClosed();
435 /* Tell backend class to disconnect. */
436 m_client->close ();
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()
453 delete m_client;
454 delete m_clientStream;
455 delete m_QCATLS;
456 delete m_connector;
458 m_connector = 0;
459 m_QCATLS = 0;
460 m_clientStream = 0;
461 m_client = 0;
464 void GroupWiseAccount::createConference( const int clientId, const QStringList& invitees )
466 kDebug () ;
467 // TODO: remove this it prevents sending a list of participants with the createconf
468 if ( isConnected() )
469 m_client->createConference( clientId , invitees );
472 void GroupWiseAccount::sendInvitation( const GroupWise::ConferenceGuid & guid, const QString & dn, const QString & message )
474 kDebug () ;
475 if ( isConnected() )
477 GroupWise::OutgoingMessage msg;
478 msg.guid = guid;
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()
500 kDebug () ;
501 m_dontSync = true;
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
510 // for each group
511 // is there no CLI with the same id?
512 // if MC has no other contacts
513 // if MC's groups size is 1
514 // remove MC
515 // else
516 // remove from group
517 // else
518 // if MC's groups size is 1 and group is topLevel
519 // remove contact
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() )
527 it.next();
528 if ( it.value() == myself() )
529 continue;
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() << "'";
539 bool found = false;
540 QMutableListIterator<GWContactInstance*> instIt( instances );
541 while ( instIt.hasNext() )
543 instIt.next();
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
548 else
549 continue;
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() )
554 found = true;
555 instIt.remove();
556 break;
559 if ( !found )
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() );
567 break;
569 else
571 kDebug() << "contact instance " << c->dn() << " not found, removing metacontact " << c->metaContact()->displayName() << " from group " << grp->displayName();
572 c->metaContact()->removeFromGroup( grp );
575 else
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 ";
580 c->deleteLater();
581 break;
583 else
584 kDebug() << "metacontact " << c->metaContact()->displayName( ) << "has multiple children and group membership, and contact " << c->dn() << " was removed from one group on the server.";
585 conflicts = true;
587 } //
588 } //end while, now check the next group membership
589 } //end while, now check the next groupwise contact
590 if ( conflicts )
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() );
593 m_dontSync = false;
596 void GroupWiseAccount::slotLoginFailed()
598 kDebug () ;
599 password().setWrong();
600 disconnect();
601 connect();
604 void GroupWiseAccount::slotKopeteGroupRenamed( Kopete::Group * renamedGroup )
606 if ( isConnected() )
608 QString objectIdString = renamedGroup->pluginData( protocol(), accountId() + " objectId" );
609 // if this group exists on the server
610 if ( !objectIdString.isEmpty() )
612 kDebug () ;
614 GroupWise::FolderItem fi;
615 fi.id = objectIdString.toInt();
616 if ( fi.id != 0 )
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 );
623 uft->go( true );
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() );
630 //else
631 // errornotconnected
634 void GroupWiseAccount::slotKopeteGroupRemoved( Kopete::Group * group )
636 if ( isConnected() )
638 kDebug () ;
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();
646 if ( objectId == 0 )
648 kDebug() << "deleted folder " << group->displayName() << " has root folder objectId 0!";
649 return;
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
654 dit->go( true );
657 //else
658 // errornotconnected
661 void GroupWiseAccount::slotConnError()
663 kDebug () ;
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() ) );
667 disconnect();
670 void GroupWiseAccount::slotConnConnected()
672 kDebug () ;
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 );
684 client()->close();
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 ();
715 else
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 ();
723 else
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:
738 break;
739 case QCA::TLS::HostMismatch:
740 idString = i18n("The host name does not match the one in the certificate.");
741 idCode = "HostMismatch";
742 break;
743 case QCA::TLS::InvalidCertificate:
744 idString = i18n("The certificate is invalid.");
745 idCode = "InvalidCert";
746 break;
747 case QCA::TLS::NoCertificate:
748 idString = i18n("No certificate was presented.");
749 idCode = "NoCert";
750 break;
753 switch ( validityResult )
755 case QCA::ValidityGood:
756 break;
757 case QCA::ErrorRejected:
758 validityString = i18n("The Certificate Authority rejected the certificate.");
759 code = "Rejected";
760 break;
761 case QCA::ErrorUntrusted:
762 validityString = i18n("The certificate is not trusted.");
763 code = "Untrusted";
764 break;
765 case QCA::ErrorSignatureFailed:
766 validityString = i18n("The signature is invalid.");
767 code = "SignatureFailed";
768 break;
769 case QCA::ErrorInvalidCA:
770 validityString = i18n("The Certificate Authority is invalid.");
771 code = "InvalidCA";
772 break;
773 case QCA::ErrorInvalidPurpose:
774 validityString = i18n("Invalid certificate purpose.");
775 code = "InvalidPurpose";
776 break;
777 case QCA::ErrorSelfSigned:
778 validityString = i18n("The certificate is self-signed.");
779 code = "SelfSigned";
780 break;
781 case QCA::ErrorRevoked:
782 validityString = i18n("The certificate has been revoked.");
783 code = "Revoked";
784 break;
785 case QCA::ErrorPathLengthExceeded:
786 validityString = i18n("Maximum certificate chain length was exceeded.");
787 code = "PathLengthExceeded";
788 break;
789 case QCA::ErrorExpired:
790 validityString = i18n("The certificate has expired.");
791 code = "Expired";
792 break;
793 case QCA::ErrorExpiredCA:
794 validityString = i18n("The Certificate Authority has expired.");
795 code = "ExpiredCA";
796 break;
797 case QCA::ErrorValidityUnknown:
798 validityString = i18n("Validity is unknown.");
799 code = "ValidityUnknown";
800 break;
803 QString message;
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);
813 else
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);
819 } else {
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 (),
826 message,
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 );
837 kDebug() ;
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 );
856 if ( !sender )
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 );
900 } else {
901 newMessage->setHtmlBody( messageMunged );
903 Q_ASSERT( sess );
904 sess->appendMessage( *newMessage );
905 kDebug() << "message from KopeteMessage: plainbody: " << newMessage->plainBody() << " parsedbody: " << newMessage->parsedBody();
906 delete newMessage;
909 void GroupWiseAccount::receiveFolder( const FolderItem & folder )
911 kDebug()
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 << ')';
919 return;
922 GWFolder * fld = m_serverListModel->addFolder( folder.id, folder.sequence, folder.name );
923 Q_ASSERT( fld );
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 ) );
935 found = grp;
936 break;
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();
948 found = grp;
949 break;
953 if ( !found )
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 )
965 kDebug()
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 );
975 Q_ASSERT( gwInst );
977 GroupWiseContact * c = contactForDN( contact.dn );
978 // this contact is new to us, create him on the server
979 if ( !c )
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() );
989 else
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 & ) ) );
999 dit->go( true );
1000 return;
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
1004 if ( grp )
1006 kDebug() << " - making sure MC is in group " << grp->displayName();
1007 m_dontSync = true;
1008 c->metaContact()->addToGroup( grp ); //addToGroup() is safe to call if already a member
1009 m_dontSync = false;
1013 c->setNickName( contact.displayName );
1014 //m_serverListModel->dump();
1017 void GroupWiseAccount::receiveAccountDetails( const ContactDetails & details )
1019 kDebug()
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
1028 << endl;
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 );
1039 return;
1041 else
1043 kDebug() << " - passed someone else's details in contact list!";
1047 void GroupWiseAccount::receiveContactUserDetails( const ContactDetails & details )
1049 kDebug()
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
1058 << endl;
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 );
1065 if( detailsOwner )
1067 kDebug() << " - updating details for " << details.dn;
1068 detailsOwner->updateDetails( details );
1070 else
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 );
1100 else
1101 kDebug() << "Notified of existing temporary contact DN: " << details.dn;
1102 return c;
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 );
1109 if ( c )
1111 kDebug() << " - their KOS is: " << protocol()->gwStatusToKOS( status ).description();
1112 Kopete::OnlineStatus kos = protocol()->gwStatusToKOS( status );
1113 c->setOnlineStatus( kos );
1114 c->setStatusMessage( awayMessage );
1116 else
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 );
1124 else
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 )
1132 kDebug () ;
1133 // make an outgoing message
1134 if ( isConnected() )
1136 GroupWise::OutgoingMessage outMsg;
1137 outMsg.guid = guid;
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() );
1145 // send the message
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
1163 topLevel = true;
1164 continue;
1167 kDebug() << "looking up: " << group->displayName();
1168 GWFolder * fld = m_serverListModel->findFolderByName( group->displayName() );
1169 FolderItem fi;
1170 if ( fld )
1172 kDebug() << fld->displayName;
1173 //FIXME - get rid of FolderItem & co
1174 fi.parentId = qobject_cast<GWFolder*>( fld->parent() )->id;
1175 fi.id = fld->id;
1176 fi.name = fld->displayName;
1178 else
1180 kDebug() << "folder: " << group->displayName() <<
1181 "not found in server list model." << endl;
1182 fi.parentId = 0;
1183 fi.id = 0;
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 );
1202 QString displayAs;
1203 if ( dt.fullName.isEmpty() )
1204 displayAs = dt.givenName + ' ' + dt.surname;
1205 else
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";
1215 return false;
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() ) );
1222 cct->go( true );
1223 return true;
1226 void GroupWiseAccount::receiveContactCreated()
1228 kDebug() ;
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() );
1239 Q_ASSERT(c);
1240 c->setOnlineStatus( protocol()->gwStatusToKOS( dt.status ) );
1241 c->setNickName( dt.fullName );
1242 c->updateDetails( dt );
1244 else
1246 client()->requestDetails( QStringList( cct->dn() ) );
1247 client()->requestStatus( cct->dn() );
1250 else
1252 // delete the contact created optimistically using the supplied userid;
1253 Kopete::Contact * c = contacts()[ protocol()->dnToDotted( cct->userId() ) ];
1254 if ( c )
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() );
1261 else
1262 delete c;
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 )
1275 kDebug() ;
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 & ) ) );
1287 dit->go( true );
1292 void GroupWiseAccount::receiveContactDeleted( const ContactItem & instance )
1294 kDebug() ;
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() )
1306 c->deleteLater();
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() );
1314 disconnect();
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 );
1321 if ( !contactFrom )
1322 contactFrom = createTemporaryContact( event.user );
1323 if ( configGroup()->readEntry( "AlwaysAcceptInvitations",false ) == true )
1325 client()->joinConference( event.guid );
1327 else
1329 ReceiveInvitationDialog * dlg = new ReceiveInvitationDialog( this, event,
1330 Kopete::UI::Global::mainWidget(), "invitedialog" );
1331 dlg->show();
1336 void GroupWiseAccount::receiveConferenceJoin( const GroupWise::ConferenceGuid & guid, const QStringList & participants, const QStringList & invitees )
1338 // get a new GWMM
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 );
1348 if ( !c )
1349 c = createTemporaryContact( dn );
1350 sess->joined( c );
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 );
1359 if ( !c )
1360 c = createTemporaryContact( dn );
1361 sess->addInvitee( c );
1363 sess->view( true )->raise( false );
1366 void GroupWiseAccount::receiveConferenceJoinNotify( const ConferenceEvent & event )
1368 kDebug() ;
1369 GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
1370 if ( sess )
1372 GroupWiseContact * c = contactForDN( event.user );
1373 if ( !c )
1374 c = createTemporaryContact( event.user );
1375 sess->joined( c );
1377 else
1378 kDebug() << " couldn't find a GWCS for conference: " << event.guid;
1381 void GroupWiseAccount::receiveConferenceLeft( const ConferenceEvent & event )
1383 kDebug() ;
1384 GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
1385 if ( sess )
1387 GroupWiseContact * c = contactForDN( event.user );
1388 if ( c )
1390 sess->left( c );
1392 else
1393 kDebug() << " couldn't find a contact for DN: " << event.user;
1395 else
1396 kDebug() << " couldn't find a GWCS for conference: " << event.guid;
1400 void GroupWiseAccount::receiveInviteDeclined( const ConferenceEvent & event )
1402 kDebug() ;
1403 GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
1404 if ( sess )
1406 GroupWiseContact * c = contactForDN( event.user );
1407 if ( c )
1408 sess->inviteDeclined( c );
1410 else
1411 kDebug() << " couldn't find a GWCS for conference: " << event.guid;
1414 void GroupWiseAccount::receiveInviteNotify( const ConferenceEvent & event )
1416 kDebug() ;
1417 GroupWiseChatSession * sess = findChatSessionByGuid( event.guid );
1418 if ( sess )
1420 GroupWiseContact * c = contactForDN( event.user );
1421 if ( !c )
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 );
1429 else
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()
1449 bool ok;
1450 QRegExp rx( ".*" );
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 );
1455 if ( ok )
1456 configGroup()->writeEntry( "AutoReply", newAutoReply );
1459 void GroupWiseAccount::slotTestRTFize()
1461 /* bool ok;
1462 const QString query = QString::fromLatin1("Enter a string to rtfize:");
1463 QString testText = KLineEditDlg::getText( query, QString(), &ok, Kopete::UI::Global::mainWidget() );
1464 if ( ok )
1465 kDebug() << "Converted text is: '" << protocol()->rtfizeText( testText ) << "'";*/
1467 // bool ok;
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() );
1470 // if ( !ok )
1471 // return;
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 );
1493 else
1494 return false;
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()
1508 return m_dontSync;
1511 void GroupWiseAccount::syncContact( GroupWiseContact * contact )
1513 if ( dontSync() )
1514 return;
1516 if ( contact != myself() )
1518 kDebug() ;
1519 if ( !isConnected() )
1521 kDebug() << "not connected, can't sync display name or group membership";
1522 return;
1525 // if this is a temporary contact, don't bother
1526 if ( contact->metaContact()->isTemporary() )
1527 return;
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;
1539 // 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() )
1550 grpIt.next();
1552 QMutableListIterator< GWContactInstance *> instIt( instances );
1553 // ( see if a contact list instance matches the group)
1554 while ( instIt.hasNext() )
1556 instIt.next();
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";
1567 instIt.remove();
1568 grpIt.remove();
1569 break;
1574 kDebug() << " = LOOKING FOR UNMATCHED PAIRS => GROUP MOVES";
1575 grpIt.toFront();
1576 // ( take the first pair and carry out a move )
1577 while ( grpIt.hasNext() && !instances.isEmpty() )
1579 grpIt.next();
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 & ) ) );
1598 mit->go();
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 & ) ) );
1605 mit->go();
1607 else
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() );
1615 mit->go( true );
1617 grpIt.remove();
1620 kDebug() << " = LOOKING FOR ADDS";
1621 grpIt.toFront();
1622 while ( grpIt.hasNext() )
1624 grpIt.next();
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 );
1635 else
1637 if ( grpIt.value() == Kopete::Group::topLevel() )
1638 ccit->contactFromUserId( contact->dn(), contact->metaContact()->displayName(),
1639 m_serverListModel->rootFolder->id );
1640 else
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() );
1645 ccit->go( true );
1646 grpIt.remove();
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() )
1654 instIt.next();
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 & ) ) );
1661 dit->go( true );
1663 instIt.remove();
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;
1673 ContactItem item;
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() ) );
1684 uct->go( true );
1690 #include "gwaccount.moc"