2 Kopete Groupwise Protocol
3 client.cpp - The main interface for the Groupwise protocol
5 Copyright (c) 2004 SUSE Linux AG http://www.suse.com
8 Based on Iris, Copyright (C) 2003 Justin Karneges <justin@affinix.com>
10 Kopete (c) 2002-2004 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 Lesser 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 <QApplication>
27 #include "chatroommanager.h"
28 #include "gwclientstream.h"
29 #include "privacymanager.h"
30 #include "requestfactory.h"
32 #include "tasks/conferencetask.h"
33 #include "tasks/connectiontask.h"
34 #include "tasks/createconferencetask.h"
35 #include "tasks/getdetailstask.h"
36 #include "tasks/getstatustask.h"
37 #include "tasks/joinconferencetask.h"
38 #include "tasks/keepalivetask.h"
39 #include "tasks/leaveconferencetask.h"
40 #include "tasks/logintask.h"
41 #include "tasks/rejectinvitetask.h"
42 #include "tasks/sendinvitetask.h"
43 #include "tasks/sendmessagetask.h"
44 #include "tasks/setstatustask.h"
45 #include "tasks/statustask.h"
46 #include "tasks/typingtask.h"
47 #include "userdetailsmanager.h"
50 class Client::ClientPrivate
58 QString host
, user
, userDN
, pass
;
59 QString osname
, tzname
, clientName
, clientVersion
;
63 RequestFactory
* requestFactory
;
64 ChatroomManager
* chatroomMgr
;
65 UserDetailsManager
* userDetailsMgr
;
66 PrivacyManager
* privacyMgr
;
68 QList
<GroupWise::CustomStatus
> customStatuses
;
69 QTimer
* keepAliveTimer
;
72 Client::Client(QObject
*par
, uint protocolVersion
)
75 setObjectName("groupwiseclient");
76 d
= new ClientPrivate
;
80 d
->clientName
= "N/A";
81 d
->clientVersion
= "0.0";
83 d
->root
= new Task(this, true);
85 d
->requestFactory
= new RequestFactory
;
86 d
->userDetailsMgr
= new UserDetailsManager( this );
87 d
->userDetailsMgr
->setObjectName( "userdetailsmgr" );
88 d
->privacyMgr
= new PrivacyManager( this );
89 d
->privacyMgr
->setObjectName( "privacymgr" );
91 d
->protocolVersion
= protocolVersion
;
92 // Sends regular keepalives so the server knows we are still running
93 d
->keepAliveTimer
= new QTimer( this );
94 connect( d
->keepAliveTimer
, SIGNAL( timeout() ), SLOT( sendKeepAlive() ) );
100 delete d
->requestFactory
;
104 void Client::connectToServer( ClientStream
*s
, const NovellDN
&server
, bool auth
)
107 //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected()));
108 //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken()));
109 connect(d
->stream
, SIGNAL(error(int)), SLOT(streamError(int)));
110 //connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &)));
111 connect(d
->stream
, SIGNAL(readyRead()), SLOT(streamReadyRead()));
112 //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished()));
114 d
->stream
->connectToServer(server
, auth
);
117 void Client::setOSName(const QString
&name
)
122 void Client::setClientName(const QString
&s
)
127 void Client::setClientVersion(const QString
&s
)
129 d
->clientVersion
= s
;
132 void Client::start( const QString
&host
, const uint port
, const QString
&userId
, const QString
&pass
)
139 initialiseEventTasks();
141 LoginTask
* login
= new LoginTask( d
->root
);
143 connect( login
, SIGNAL( gotMyself( const GroupWise::ContactDetails
& ) ),
144 this, SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails
& ) ) );
146 connect( login
, SIGNAL( gotFolder( const FolderItem
& ) ),
147 this, SIGNAL( folderReceived( const FolderItem
& ) ) );
149 connect( login
, SIGNAL( gotContact( const ContactItem
& ) ),
150 this, SIGNAL( contactReceived( const ContactItem
& ) ) );
152 connect( login
, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails
& ) ),
153 this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails
& ) ) ) ;
155 connect( login
, SIGNAL( gotPrivacySettings( bool, bool, const QStringList
&, const QStringList
& ) ),
156 privacyManager(), SLOT( slotGotPrivacySettings( bool, bool, const QStringList
&, const QStringList
& ) ) );
158 connect( login
, SIGNAL( gotCustomStatus( const GroupWise::CustomStatus
& ) ),
159 SLOT( lt_gotCustomStatus( const GroupWise::CustomStatus
& ) ) );
161 connect( login
, SIGNAL( gotKeepalivePeriod( int ) ), SLOT( lt_gotKeepalivePeriod( int ) ) );
163 connect( login
, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
173 debug( "Client::close()" );
174 d
->keepAliveTimer
->stop();
176 d
->stream
->disconnect(this);
182 QString
Client::host()
192 QList
<GroupWise::CustomStatus
> Client::customStatuses()
194 return d
->customStatuses
;
197 void Client::initialiseEventTasks()
199 // The StatusTask handles incoming status changes
200 StatusTask
* st
= new StatusTask( d
->root
); // FIXME - add an additional EventRoot?
201 connect( st
, SIGNAL( gotStatus( const QString
&, quint16
, const QString
& ) ), SIGNAL( statusReceived( const QString
&, quint16
, const QString
& ) ) );
202 // The ConferenceTask handles incoming conference events, messages, joins, leaves, etc
203 ConferenceTask
* ct
= new ConferenceTask( d
->root
);
204 connect( ct
, SIGNAL( message( const ConferenceEvent
& ) ), SLOT( ct_messageReceived( const ConferenceEvent
& ) ) );
205 connect( ct
, SIGNAL( typing( const ConferenceEvent
& ) ), SIGNAL( contactTyping( const ConferenceEvent
& ) ) );
206 connect( ct
, SIGNAL( notTyping( const ConferenceEvent
& ) ), SIGNAL( contactNotTyping( const ConferenceEvent
& ) ) );
207 connect( ct
, SIGNAL( joined( const ConferenceEvent
& ) ), SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent
& ) ) );
208 connect( ct
, SIGNAL( left( const ConferenceEvent
& ) ), SIGNAL( conferenceLeft( const ConferenceEvent
& ) ) );
209 connect( ct
, SIGNAL( invited( const ConferenceEvent
& ) ), SIGNAL( invitationReceived( const ConferenceEvent
& ) ) );
210 connect( ct
, SIGNAL( otherInvited( const ConferenceEvent
& ) ), SIGNAL( inviteNotifyReceived( const ConferenceEvent
& ) ) );
211 connect( ct
, SIGNAL( invitationDeclined( const ConferenceEvent
& ) ), SIGNAL( invitationDeclined( const ConferenceEvent
& ) ) );
212 connect( ct
, SIGNAL( closed( const ConferenceEvent
& ) ), SIGNAL( conferenceClosed( const ConferenceEvent
& ) ) );
213 connect( ct
, SIGNAL( autoReply( const ConferenceEvent
& ) ), SIGNAL( autoReplyReceived( const ConferenceEvent
& ) ) );
214 connect( ct
, SIGNAL( broadcast( const ConferenceEvent
& ) ), SIGNAL( broadcastReceived( const ConferenceEvent
& ) ) );
215 connect( ct
, SIGNAL( systemBroadcast( const ConferenceEvent
& ) ), SIGNAL( systemBroadcastReceived( const ConferenceEvent
& ) ) );
218 // The ConnectionTask handles incoming connection events
219 ConnectionTask
* cont
= new ConnectionTask( d
->root
);
220 connect( cont
, SIGNAL( connectedElsewhere() ), SIGNAL( connectedElsewhere() ) );
223 void Client::setStatus( GroupWise::Status status
, const QString
& reason
, const QString
& autoReply
)
225 debug( QString("Setting status to %1").arg( status
) );;
226 SetStatusTask
* sst
= new SetStatusTask( d
->root
);
227 sst
->status( status
, reason
, autoReply
);
228 connect( sst
, SIGNAL( finished() ), this, SLOT( sst_statusChanged() ) );
230 // TODO: set status change in progress flag
233 void Client::requestStatus( const QString
& userDN
)
235 GetStatusTask
* gst
= new GetStatusTask( d
->root
);
236 gst
->userDN( userDN
);
237 connect( gst
, SIGNAL( gotStatus( const QString
&, quint16
, const QString
& ) ), SIGNAL( statusReceived( const QString
&, quint16
, const QString
& ) ) );
241 void Client::sendMessage( const QStringList
& addresseeDNs
, const OutgoingMessage
& message
)
243 SendMessageTask
* smt
= new SendMessageTask( d
->root
);
244 smt
->message( addresseeDNs
, message
);
245 connect( smt
, SIGNAL( finished() ), SLOT( smt_messageSent() ) );
249 void Client::sendTyping( const GroupWise::ConferenceGuid
& conferenceGuid
, bool typing
)
251 TypingTask
* tt
= new TypingTask( d
->root
);
252 tt
->typing( conferenceGuid
, typing
);
256 void Client::createConference( const int clientId
)
259 createConference( clientId
, dummy
);
262 void Client::createConference( const int clientId
, const QStringList
& participants
)
264 CreateConferenceTask
* cct
= new CreateConferenceTask( d
->root
);
265 cct
->conference( clientId
, participants
);
266 connect( cct
, SIGNAL( finished() ), SLOT( cct_conferenceCreated() ) );
269 void Client::requestDetails( const QStringList
& userDNs
)
271 GetDetailsTask
* gdt
= new GetDetailsTask( d
->root
);
272 gdt
->userDNs( userDNs
);
273 connect( gdt
, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails
& ) ),
274 this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails
& ) ) );
278 void Client::joinConference( const GroupWise::ConferenceGuid
& guid
)
280 JoinConferenceTask
* jct
= new JoinConferenceTask( d
->root
);
282 connect( jct
, SIGNAL( finished() ), SLOT( jct_joinConfCompleted() ) );
286 void Client::rejectInvitation( const GroupWise::ConferenceGuid
& guid
)
288 RejectInviteTask
* rit
= new RejectInviteTask ( d
->root
);
290 // we don't do anything with the results of this task
294 void Client::leaveConference( const GroupWise::ConferenceGuid
& guid
)
296 LeaveConferenceTask
* lct
= new LeaveConferenceTask( d
->root
);
298 //connect( lct, SIGNAL( finished() ), SLOT( lct_leftConference() ) );
302 void Client::sendInvitation( const GroupWise::ConferenceGuid
& guid
, const QString
& dn
, const GroupWise::OutgoingMessage
& message
)
304 SendInviteTask
* sit
= new SendInviteTask( d
->root
);
305 QStringList
invitees( dn
);
306 sit
->invite( guid
, invitees
, message
);
311 void Client::streamError( int error
)
313 debug( QString( "CLIENT ERROR (Error %1)" ).arg( error
) );
316 void Client::streamReadyRead()
318 debug( "CLIENT STREAM READY READ" );
319 // take the incoming transfer and distribute it to the task tree
320 Transfer
* transfer
= d
->stream
->read();
321 distribute( transfer
);
324 void Client::lt_loginFinished()
326 debug( "Client::lt_loginFinished()" );
327 const LoginTask
* lt
= (LoginTask
*)sender();
330 debug( "Client::lt_loginFinished() LOGIN SUCCEEDED" );
331 // set our initial status
332 SetStatusTask
* sst
= new SetStatusTask( d
->root
);
333 sst
->status( GroupWise::Available
, QString(), QString() );
336 // fetch details for any privacy list items that aren't in our contact list.
337 // There is a chicken-and-egg case regarding this: We need the privacy before reading the contact list so
338 // blocked contacts are shown as blocked. But we need not fetch user details for the privacy lists
339 // before reading the contact list, as many privacy items' details are already in the contact list
340 privacyManager()->getDetailsForPrivacyLists();
344 debug( "Client::lt_loginFinished() LOGIN FAILED" );
347 // otherwise client should disconnect and signal failure that way??
350 void Client::sst_statusChanged()
352 const SetStatusTask
* sst
= (SetStatusTask
*)sender();
353 if ( sst
->success() )
355 emit
ourStatusChanged( sst
->requestedStatus(), sst
->awayMessage(), sst
->autoReply() );
359 void Client::ct_messageReceived( const ConferenceEvent
& messageEvent
)
361 debug( "parsing received message's RTF" );
362 ConferenceEvent transformedEvent
= messageEvent
;
364 QString rtf
= messageEvent
.message
;
365 if ( !rtf
.isEmpty() )
366 transformedEvent
.message
= parser
.Parse( rtf
.toLatin1(), "" );
368 // fixes for RTF to HTML conversion problems
369 // we can drop these once the server reenables the sending of unformatted text
370 // redundant linebreak at the end of the message
371 QRegExp
rx(" </span> </span> </span><br>$");
372 transformedEvent
.message
.replace( rx
, "</span></span></span>" );
373 // missing linebreak after first line of an encrypted message
374 QRegExp
ry("-----BEGIN PGP MESSAGE----- </span> </span> </span>");
375 transformedEvent
.message
.replace( ry
, "-----BEGIN PGP MESSAGE-----</span></span></span><br/>" );
377 emit
messageReceived( transformedEvent
);
380 void Client::cct_conferenceCreated()
382 const CreateConferenceTask
* cct
= ( CreateConferenceTask
* )sender();
383 if ( cct
->success() )
385 emit
conferenceCreated( cct
->clientConfId(), cct
->conferenceGUID() );
389 emit
conferenceCreationFailed( cct
->clientConfId(), cct
->statusCode() );
393 void Client::jct_joinConfCompleted()
395 const JoinConferenceTask
* jct
= ( JoinConferenceTask
* )sender();
397 debug( QString( "Joined conference %1, participants are: " ).arg( jct
->guid() ) );
398 QStringList parts
= jct
->participants();
399 for ( QStringList::Iterator it
= parts
.begin(); it
!= parts
.end(); ++it
)
400 debug( QString( " - %1" ).arg(*it
) );
401 debug( "invitees are: " );
402 QStringList invitees
= jct
->invitees();
403 for ( QStringList::Iterator it
= invitees
.begin(); it
!= invitees
.end(); ++it
)
404 debug( QString( " - %1" ).arg(*it
) );
406 emit
conferenceJoined( jct
->guid(), jct
->participants(), jct
->invitees() );
409 void Client::lt_gotCustomStatus( const GroupWise::CustomStatus
& custom
)
411 d
->customStatuses
.append( custom
);
416 QString
Client::userId()
421 void Client::setUserDN( const QString
& userDN
)
426 QString
Client::userDN()
431 QString
Client::password()
436 QString
Client::userAgent()
438 return QString::fromLatin1( "%1/%2 (%3)" ).arg( d
->clientName
, d
->clientVersion
, d
->osname
);
441 QByteArray
Client::ipAddress()
443 // TODO: remove hardcoding
444 return "10.10.11.103";
447 void Client::distribute( Transfer
* transfer
)
449 if( !rootTask()->take( transfer
) )
450 debug( "CLIENT: root task refused transfer" );
451 // at this point the transfer is no longer needed
455 void Client::send( Request
* request
)
457 debug( "CLIENT::send()" );
460 debug( "CLIENT - NO STREAM TO SEND ON!");
463 // QString out = request.toString();
464 // debug(QString("Client: outgoing: [\n%1]\n").arg(out));
467 d
->stream
->write( request
);
470 void Client::debug( const QString
&str
)
472 #ifdef LIBGW_USE_KDEBUG
475 qDebug() << "CLIENT: " << str
.toAscii();
479 QString
Client::genUniqueId()
482 s
.sprintf("a%x", d
->id_seed
);
487 PrivacyManager
* Client::privacyManager()
489 return d
->privacyMgr
;
492 RequestFactory
* Client::requestFactory()
494 return d
->requestFactory
;
497 UserDetailsManager
* Client::userDetailsManager()
499 return d
->userDetailsMgr
;
502 Task
* Client::rootTask()
507 uint
Client::protocolVersion() const
509 return d
->protocolVersion
;
512 ChatroomManager
* Client::chatroomManager()
514 if ( !d
->chatroomMgr
)
516 d
->chatroomMgr
= new ChatroomManager( this );
517 d
->chatroomMgr
->setObjectName( "chatroommgr" );
519 return d
->chatroomMgr
;
522 void Client::lt_gotKeepalivePeriod( int period
)
524 d
->keepAliveTimer
->start( period
* 60 * 1000 );
527 void Client::sendKeepAlive()
529 KeepAliveTask
* kat
= new KeepAliveTask( d
->root
);
534 void Client::smt_messageSent()
536 const SendMessageTask
* smt
= ( SendMessageTask
* )sender();
537 if ( smt
->success() )
539 debug( "message sent OK" );
543 debug( "message sending failed!" );
544 emit
messageSendingFailed();
548 #include "client.moc"