/* Kopete Groupwise Protocol client.cpp - The main interface for the Groupwise protocol Copyright (c) 2004 SUSE Linux AG http://www.suse.com (c) 2008 Novell, Inc. Based on Iris, Copyright (C) 2003 Justin Karneges Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #include #include #include "chatroommanager.h" #include "gwclientstream.h" #include "privacymanager.h" #include "requestfactory.h" #include "task.h" #include "tasks/conferencetask.h" #include "tasks/connectiontask.h" #include "tasks/createconferencetask.h" #include "tasks/getdetailstask.h" #include "tasks/getstatustask.h" #include "tasks/joinconferencetask.h" #include "tasks/keepalivetask.h" #include "tasks/leaveconferencetask.h" #include "tasks/logintask.h" #include "tasks/rejectinvitetask.h" #include "tasks/sendinvitetask.h" #include "tasks/sendmessagetask.h" #include "tasks/setstatustask.h" #include "tasks/statustask.h" #include "tasks/typingtask.h" #include "userdetailsmanager.h" #include "client.h" class Client::ClientPrivate { public: ClientPrivate() {} ClientStream *stream; int id_seed; Task *root; TQString host, user, userDN, pass; TQString osname, tzname, clientName, clientVersion; uint port; /* int tzoffset;*/ bool active; RequestFactory * requestFactory; ChatroomManager * chatroomMgr; UserDetailsManager * userDetailsMgr; PrivacyManager * privacyMgr; uint protocolVersion; TQValueList customStatuses; TQTimer * keepAliveTimer; }; Client::Client(TQObject *par, uint protocolVersion ) :TQObject(par, "groupwiseclient") { d = new ClientPrivate; /* d->tzoffset = 0;*/ d->active = false; d->osname = "N/A"; d->clientName = "N/A"; d->clientVersion = "0.0"; d->id_seed = 0xaaaa; d->root = new Task(this, true); d->chatroomMgr = 0; d->requestFactory = new RequestFactory; d->userDetailsMgr = new UserDetailsManager( this, "userdetailsmgr" ); d->privacyMgr = new PrivacyManager( this, "privacymgr" ); d->stream = 0; d->protocolVersion = protocolVersion; // Sends regular keepalives so the server knows we are still running d->keepAliveTimer = new TQTimer( this ); connect( d->keepAliveTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( sendKeepAlive() ) ); } Client::~Client() { delete d->root; delete d->requestFactory; delete d->userDetailsMgr; delete d; } void Client::connectToServer( ClientStream *s, const NovellDN &server, bool auth ) { d->stream = s; //connect(d->stream, TQ_SIGNAL(connected()), TQ_SLOT(streamConnected())); //connect(d->stream, TQ_SIGNAL(handshaken()), TQ_SLOT(streamHandshaken())); connect(d->stream, TQ_SIGNAL(error(int)), TQ_SLOT(streamError(int))); //connect(d->stream, TQ_SIGNAL(sslCertificateReady(const TQSSLCert &)), TQ_SLOT(streamSSLCertificateReady(const TQSSLCert &))); connect(d->stream, TQ_SIGNAL(readyRead()), TQ_SLOT(streamReadyRead())); //connect(d->stream, TQ_SIGNAL(closeFinished()), TQ_SLOT(streamCloseFinished())); d->stream->connectToServer(server, auth); } void Client::setOSName(const TQString &name) { d->osname = name; } void Client::setClientName(const TQString &s) { d->clientName = s; } void Client::setClientVersion(const TQString &s) { d->clientVersion = s; } void Client::start( const TQString &host, const uint port, const TQString &userId, const TQString &pass ) { d->host = host; d->port = port; d->user = userId; d->pass = pass; initialiseEventTasks(); LoginTask * login = new LoginTask( d->root ); connect( login, TQ_SIGNAL( gotMyself( const GroupWise::ContactDetails & ) ), this, TQ_SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails & ) ) ); connect( login, TQ_SIGNAL( gotFolder( const FolderItem & ) ), this, TQ_SIGNAL( folderReceived( const FolderItem & ) ) ); connect( login, TQ_SIGNAL( gotContact( const ContactItem & ) ), this, TQ_SIGNAL( contactReceived( const ContactItem & ) ) ); connect( login, TQ_SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ), this, TQ_SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) ) ; connect( login, TQ_SIGNAL( gotPrivacySettings( bool, bool, const TQStringList &, const TQStringList & ) ), privacyManager(), TQ_SLOT( slotGotPrivacySettings( bool, bool, const TQStringList &, const TQStringList & ) ) ); connect( login, TQ_SIGNAL( gotCustomStatus( const GroupWise::CustomStatus & ) ), TQ_SLOT( lt_gotCustomStatus( const GroupWise::CustomStatus & ) ) ); connect( login, TQ_SIGNAL( gotKeepalivePeriod( int ) ), TQ_SLOT( lt_gotKeepalivePeriod( int ) ) ); connect( login, TQ_SIGNAL( finished() ), this, TQ_SLOT( lt_loginFinished() ) ); login->initialise(); login->go( true ); d->active = true; } void Client::close() { debug( "Client::close()" ); d->keepAliveTimer->stop(); if(d->stream) { d->stream->disconnect(this); d->stream->close(); d->stream = 0; } } TQString Client::host() { return d->host; } int Client::port() { return d->port; } TQValueList Client::customStatuses() { return d->customStatuses; } void Client::initialiseEventTasks() { // The StatusTask handles incoming status changes StatusTask * st = new StatusTask( d->root ); // FIXME - add an additional EventRoot? connect( st, TQ_SIGNAL( gotStatus( const TQString &, TQ_UINT16, const TQString & ) ), TQ_SIGNAL( statusReceived( const TQString &, TQ_UINT16, const TQString & ) ) ); // The ConferenceTask handles incoming conference events, messages, joins, leaves, etc ConferenceTask * ct = new ConferenceTask( d->root ); connect( ct, TQ_SIGNAL( message( const ConferenceEvent & ) ), TQ_SLOT( ct_messageReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( typing( const ConferenceEvent & ) ), TQ_SIGNAL( contactTyping( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( notTyping( const ConferenceEvent & ) ), TQ_SIGNAL( contactNotTyping( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( joined( const ConferenceEvent & ) ), TQ_SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( left( const ConferenceEvent & ) ), TQ_SIGNAL( conferenceLeft( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( invited( const ConferenceEvent & ) ), TQ_SIGNAL( invitationReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( otherInvited( const ConferenceEvent & ) ), TQ_SIGNAL( inviteNotifyReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( invitationDeclined( const ConferenceEvent & ) ), TQ_SIGNAL( invitationDeclined( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( closed( const ConferenceEvent & ) ), TQ_SIGNAL( conferenceClosed( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( autoReply( const ConferenceEvent & ) ), TQ_SIGNAL( autoReplyReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( broadcast( const ConferenceEvent & ) ), TQ_SIGNAL( broadcastReceived( const ConferenceEvent & ) ) ); connect( ct, TQ_SIGNAL( systemBroadcast( const ConferenceEvent & ) ), TQ_SIGNAL( systemBroadcastReceived( const ConferenceEvent & ) ) ); // The ConnectionTask handles incoming connection events ConnectionTask* cont = new ConnectionTask( d->root ); connect( cont, TQ_SIGNAL( connectedElsewhere() ), TQ_SIGNAL( connectedElsewhere() ) ); } void Client::setStatus( GroupWise::Status status, const TQString & reason, const TQString & autoReply ) { debug( TQString("Setting status to %1").arg( status ) );; SetStatusTask * sst = new SetStatusTask( d->root ); sst->status( status, reason, autoReply ); connect( sst, TQ_SIGNAL( finished() ), this, TQ_SLOT( sst_statusChanged() ) ); sst->go( true ); // TODO: set status change in progress flag } void Client::requestStatus( const TQString & userDN ) { GetStatusTask * gst = new GetStatusTask( d->root ); gst->userDN( userDN ); connect( gst, TQ_SIGNAL( gotStatus( const TQString &, TQ_UINT16, const TQString & ) ), TQ_SIGNAL( statusReceived( const TQString &, TQ_UINT16, const TQString & ) ) ); gst->go( true ); } void Client::sendMessage( const TQStringList & addresseeDNs, const OutgoingMessage & message ) { SendMessageTask * smt = new SendMessageTask( d->root ); smt->message( addresseeDNs, message ); connect( smt, TQ_SIGNAL( finished() ), TQ_SLOT( smt_messageSent() ) ); smt->go( true ); } void Client::sendTyping( const GroupWise::ConferenceGuid & conferenceGuid, bool typing ) { TypingTask * tt = new TypingTask( d->root ); tt->typing( conferenceGuid, typing ); tt->go( true ); } void Client::createConference( const int clientId ) { TQStringList dummy; createConference( clientId, dummy ); } void Client::createConference( const int clientId, const TQStringList & participants ) { CreateConferenceTask * cct = new CreateConferenceTask( d->root ); cct->conference( clientId, participants ); connect( cct, TQ_SIGNAL( finished() ), TQ_SLOT( cct_conferenceCreated() ) ); cct->go( true ); } void Client::requestDetails( const TQStringList & userDNs ) { GetDetailsTask * gdt = new GetDetailsTask( d->root ); gdt->userDNs( userDNs ); connect( gdt, TQ_SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ), this, TQ_SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) ); gdt->go( true ); } void Client::joinConference( const GroupWise::ConferenceGuid & guid ) { JoinConferenceTask * jct = new JoinConferenceTask( d->root ); jct->join( guid ); connect( jct, TQ_SIGNAL( finished() ), TQ_SLOT( jct_joinConfCompleted() ) ); jct->go( true ); } void Client::rejectInvitation( const GroupWise::ConferenceGuid & guid ) { RejectInviteTask * rit = new RejectInviteTask ( d->root ); rit->reject( guid ); // we don't do anything with the results of this task rit->go( true ); } void Client::leaveConference( const GroupWise::ConferenceGuid & guid ) { LeaveConferenceTask * lct = new LeaveConferenceTask( d->root ); lct->leave( guid ); //connect( lct, TQ_SIGNAL( finished() ), TQ_SLOT( lct_leftConference() ) ); lct->go( true ); } void Client::sendInvitation( const GroupWise::ConferenceGuid & guid, const TQString & dn, const GroupWise::OutgoingMessage & message ) { SendInviteTask * sit = new SendInviteTask( d->root ); TQStringList invitees( dn ); sit->invite( guid, dn, message ); sit->go( true ); } // SLOTS // void Client::streamError( int error ) { debug( TQString( "CLIENT ERROR (Error %1)" ).arg( error ) ); } void Client::streamReadyRead() { debug( "CLIENT STREAM READY READ" ); // take the incoming transfer and distribute it to the task tree Transfer * transfer = d->stream->read(); distribute( transfer ); } void Client::lt_loginFinished() { debug( "Client::lt_loginFinished()" ); const LoginTask * lt = (LoginTask *)sender(); if ( lt->success() ) { debug( "Client::lt_loginFinished() LOGIN SUCCEEDED" ); // set our initial status SetStatusTask * sst = new SetStatusTask( d->root ); sst->status( GroupWise::Available, TQString(), TQString() ); sst->go( true ); emit loggedIn(); // fetch details for any privacy list items that aren't in our contact list. // There is a chicken-and-egg case regarding this: We need the privacy before reading the contact list so // blocked contacts are shown as blocked. But we need not fetch user details for the privacy lists // before reading the contact list, as many privacy items' details are already in the contact list privacyManager()->getDetailsForPrivacyLists(); } else { debug( "Client::lt_loginFinished() LOGIN FAILED" ); emit loginFailed(); } // otherwise client should disconnect and signal failure that way?? } void Client::sst_statusChanged() { const SetStatusTask * sst = (SetStatusTask *)sender(); if ( sst->success() ) { emit ourStatusChanged( sst->requestedStatus(), sst->awayMessage(), sst->autoReply() ); } } void Client::ct_messageReceived( const ConferenceEvent & messageEvent ) { debug( "parsing received message's RTF" ); ConferenceEvent transformedEvent = messageEvent; RTF2HTML parser; TQString rtf = messageEvent.message; if ( !rtf.isEmpty() ) transformedEvent.message = parser.Parse( rtf.latin1(), "" ); // fixes for RTF to HTML conversion problems // we can drop these once the server reenables the sending of unformatted text // redundant linebreak at the end of the message TQRegExp rx("
$"); transformedEvent.message.replace( rx, "" ); // missing linebreak after first line of an encrypted message TQRegExp ry("-----BEGIN PGP MESSAGE----- "); transformedEvent.message.replace( ry, "-----BEGIN PGP MESSAGE-----
" ); emit messageReceived( transformedEvent ); } void Client::cct_conferenceCreated() { const CreateConferenceTask * cct = ( CreateConferenceTask * )sender(); if ( cct->success() ) { emit conferenceCreated( cct->clientConfId(), cct->conferenceGUID() ); } else { emit conferenceCreationFailed( cct->clientConfId(), cct->statusCode() ); } } void Client::jct_joinConfCompleted() { const JoinConferenceTask * jct = ( JoinConferenceTask * )sender(); #ifdef LIBGW_DEBUG debug( TQString( "Joined conference %1, participants are: " ).arg( jct->guid() ) ); TQStringList parts = jct->participants(); for ( TQStringList::Iterator it = parts.begin(); it != parts.end(); ++it ) debug( TQString( " - %1" ).arg(*it) ); debug( "invitees are: " ); TQStringList invitees = jct->invitees(); for ( TQStringList::Iterator it = invitees.begin(); it != invitees.end(); ++it ) debug( TQString( " - %1" ).arg(*it) ); #endif emit conferenceJoined( jct->guid(), jct->participants(), jct->invitees() ); } void Client::lt_gotCustomStatus( const GroupWise::CustomStatus & custom ) { d->customStatuses.append( custom ); } // INTERNALS // TQString Client::userId() { return d->user; } void Client::setUserDN( const TQString & userDN ) { d->userDN = userDN; } TQString Client::userDN() { return d->userDN; } TQString Client::password() { return d->pass; } TQString Client::userAgent() { return TQString::fromLatin1( "%1/%2 (%3)" ).arg( d->clientName, d->clientVersion, d->osname ); } TQCString Client::ipAddress() { // TODO: remove hardcoding return "10.10.11.103"; } void Client::distribute( Transfer * transfer ) { if( !rootTask()->take( transfer ) ) debug( "CLIENT: root task refused transfer" ); // at this point the transfer is no longer needed delete transfer; } void Client::send( Request * request ) { debug( "CLIENT::send()" ); if( !d->stream ) { debug( "CLIENT - NO STREAM TO SEND ON!"); return; } // TQString out = request.toString(); // debug(TQString("Client: outgoing: [\n%1]\n").arg(out)); // xmlOutgoing(out); d->stream->write( request ); } void Client::debug( const TQString &str ) { #ifdef LIBGW_USE_KDEBUG kdDebug( GROUPWISE_DEBUG_LIBGW ) << "debug: " << str << endl; #else tqDebug( "CLIENT: %s\n", str.ascii() ); #endif } TQString Client::genUniqueId() { TQString s; s.sprintf("a%x", d->id_seed); d->id_seed += 0x10; return s; } PrivacyManager * Client::privacyManager() { return d->privacyMgr; } RequestFactory * Client::requestFactory() { return d->requestFactory; } UserDetailsManager * Client::userDetailsManager() { return d->userDetailsMgr; } Task * Client::rootTask() { return d->root; } uint Client::protocolVersion() const { return d->protocolVersion; } ChatroomManager * Client::chatroomManager() { if ( !d->chatroomMgr ) d->chatroomMgr = new ChatroomManager( this, "chatroommgr" ); return d->chatroomMgr; } void Client::lt_gotKeepalivePeriod( int period ) { d->keepAliveTimer->start( period * 60 * 1000 ); } void Client::sendKeepAlive() { KeepAliveTask * kat = new KeepAliveTask( d->root ); kat->setup(); kat->go( true ); } void Client::smt_messageSent() { const SendMessageTask * smt = ( SendMessageTask * )sender(); if ( smt->success() ) { debug( "message sent OK" ); } else { debug( "message sending failed!" ); emit messageSendingFailed(); } } #include "client.moc"