2 client.cpp - Kopete Oscar Protocol
4 Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
5 Copyright (c) 2007 Roman Jarosz <kedgedev@centrum.cz>
7 Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
8 Based on Iris, Copyright (C) 2003 Justin Karneges
10 Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
12 *************************************************************************
14 * This library is free software; you can redistribute it and/or *
15 * modify it under the terms of the GNU 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 *************************************************************************
27 #include <qtextcodec.h>
28 #include <QtNetwork/QTcpSocket>
30 #include <kdebug.h> //for kDebug()
33 #include "filetransfertask.h"
34 #include "buddyicontask.h"
35 #include "clientreadytask.h"
36 #include "connectionhandler.h"
37 #include "changevisibilitytask.h"
38 #include "chatnavservicetask.h"
39 #include "errortask.h"
40 #include "icquserinfo.h"
41 #include "icquserinfotask.h"
42 #include "logintask.h"
43 #include "connection.h"
44 #include "messagereceivertask.h"
45 #include "onlinenotifiertask.h"
46 #include "oscarclientstream.h"
47 #include "oscarsettings.h"
48 #include "oscarutils.h"
49 #include "ownuserinfotask.h"
50 #include "profiletask.h"
51 #include "senddcinfotask.h"
52 #include "sendmessagetask.h"
53 #include "serverredirecttask.h"
54 #include "servicesetuptask.h"
55 #include "contactmanager.h"
56 #include "ssimodifytask.h"
57 #include "ssiauthtask.h"
58 #include "offlinemessagestask.h"
60 #include "typingnotifytask.h"
61 #include "userinfotask.h"
62 #include "usersearchtask.h"
63 #include "warningtask.h"
64 #include "chatservicetask.h"
65 #include "rateclassmanager.h"
66 #include "icquserinfoupdatetask.h"
67 #include "icqchangepasswordtask.h"
68 #include "oscarmessageplugin.h"
69 #include "xtrazxtraznotify.h"
70 #include "xtrazxawayservice.h"
71 #include "closeconnectiontask.h"
72 #include "icqtlvinforequesttask.h"
73 #include "icqtlvinfoupdatetask.h"
78 class DefaultCodecProvider
: public Client::CodecProvider
81 virtual QTextCodec
* codecForContact( const QString
& ) const
83 return QTextCodec::codecForMib( 4 );
85 virtual QTextCodec
* codecForAccount() const
87 return QTextCodec::codecForMib( 4 );
91 DefaultCodecProvider defaultCodecProvider
;
94 class Client::ClientPrivate
99 QString host
, user
, pass
;
104 enum { StageOne
, StageTwo
};
107 //Protocol specific data
109 bool redirectRequested
;
110 QList
<Oscar::WORD
> redirectionServices
;
111 Oscar::WORD currentRedirect
;
113 Oscar::Settings
* settings
;
116 ErrorTask
* errorTask
;
117 OnlineNotifierTask
* onlineNotifier
;
118 OwnUserInfoTask
* ownStatusTask
;
119 MessageReceiverTask
* messageReceiverTask
;
120 SSIAuthTask
* ssiAuthTask
;
121 ICQUserInfoRequestTask
* icqInfoTask
;
122 ICQTlvInfoRequestTask
* icqTlvInfoTask
;
123 UserInfoTask
* userInfoTask
;
124 TypingNotifyTask
* typingNotifyTask
;
125 SSIModifyTask
* ssiModifyTask
;
127 ContactManager
* ssiManager
;
128 ConnectionHandler connections
;
131 UserDetails ourDetails
;
134 QList
<int> exchanges
;
139 QString message
; // for away-,DND-message etc., and for Xtraz status
140 int xtraz
; // Xtraz status
141 QString description
; // Xtraz description
146 struct AwayMsgRequest
149 ICQStatus contactStatus
;
151 QList
<AwayMsgRequest
> awayMsgRequestQueue
;
152 QTimer
* awayMsgRequestTimer
;
153 CodecProvider
* codecProvider
;
155 const Oscar::ClientVersion
* version
;
159 Client::Client( QObject
* parent
)
162 setObjectName( "oscarclient" );
166 d
= new ClientPrivate
;
169 d
->isIcq
= false; //default to AIM
170 d
->redirectRequested
= false;
171 d
->currentRedirect
= 0;
172 d
->status
.status
= 0x0; // default to online
173 d
->status
.xtraz
= -1; // default to no Xtraz
174 d
->status
.sent
= false;
175 d
->ssiManager
= new ContactManager( this );
176 d
->settings
= new Oscar::Settings();
178 d
->onlineNotifier
= 0L;
179 d
->ownStatusTask
= 0L;
180 d
->messageReceiverTask
= 0L;
183 d
->icqTlvInfoTask
= 0L;
184 d
->userInfoTask
= 0L;
185 d
->stage
= ClientPrivate::StageOne
;
186 d
->typingNotifyTask
= 0L;
187 d
->ssiModifyTask
= 0L;
188 d
->awayMsgRequestTimer
= new QTimer();
189 d
->codecProvider
= &defaultCodecProvider
;
191 connect( this, SIGNAL( redirectionFinished( Oscar::WORD
) ),
192 this, SLOT( checkRedirectionQueue( Oscar::WORD
) ) );
193 connect( d
->awayMsgRequestTimer
, SIGNAL( timeout() ),
194 this, SLOT( nextICQAwayMessageRequest() ) );
200 //delete the connections differently than in deleteConnections()
201 //deleteLater() seems to cause destruction order issues
204 delete d
->ssiManager
;
205 delete d
->awayMsgRequestTimer
;
209 Oscar::Settings
* Client::clientSettings() const
214 void Client::connectToServer( Connection
*c
, const QString
& host
, quint16 port
, bool auth
)
216 d
->connections
.append( c
);
219 m_loginTask
= new StageOneLoginTask( c
->rootTask() );
220 connect( m_loginTask
, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
223 connect( c
, SIGNAL( socketError( int, const QString
& ) ), this, SLOT( determineDisconnection( int, const QString
& ) ) );
224 c
->connectToServer( host
, port
);
227 void Client::start( const QString
&host
, const uint port
, const QString
&userId
, const QString
&pass
)
233 d
->stage
= ClientPrivate::StageOne
;
239 QList
<Connection
*> cList
= d
->connections
.connections();
240 for ( int i
= 0; i
< cList
.size(); i
++ )
241 (new CloseConnectionTask( cList
.at(i
)->rootTask() ))->go( Task::AutoDelete
);
244 d
->awayMsgRequestTimer
->stop();
245 d
->awayMsgRequestQueue
.clear();
246 d
->connections
.clear();
249 //don't clear the stored status between stage one and two
250 if ( d
->stage
== ClientPrivate::StageTwo
)
252 d
->status
.status
= 0x0;
253 d
->status
.xtraz
= -1;
254 d
->status
.sent
= false;
255 d
->status
.message
.clear();
256 d
->status
.description
.clear();
259 d
->exchanges
.clear();
260 d
->redirectRequested
= false;
261 d
->currentRedirect
= 0;
262 d
->redirectionServices
.clear();
263 d
->ssiManager
->clear();
266 void Client::setStatus( Oscar::DWORD status
, const QString
&message
, int xtraz
, const QString
&description
)
268 kDebug(OSCAR_RAW_DEBUG
) << "Setting status message to "<< message
;
270 // remember the values to reply with, when requested
271 bool xtrazChanged
= (xtraz
> -1 || d
->status
.xtraz
!= xtraz
);
272 bool statusInfoChanged
= ( !d
->status
.sent
|| message
!= d
->status
.message
|| description
!= d
->status
.description
);
273 d
->status
.status
= status
;
274 d
->status
.message
= message
;
275 d
->status
.xtraz
= xtraz
;
276 d
->status
.description
= description
;
277 d
->status
.sent
= false;
283 //the first connection is always the BOS connection
284 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
286 return; //TODO trigger an error of some sort?
288 ChangeVisibilityTask
* cvt
= new ChangeVisibilityTask( c
->rootTask() );
289 if ( ( status
& 0x0100 ) == 0x0100 )
291 kDebug(OSCAR_RAW_DEBUG
) << "Setting invisible";
292 cvt
->setVisible( false );
296 kDebug(OSCAR_RAW_DEBUG
) << "Setting visible";
297 cvt
->setVisible( true );
299 cvt
->go( Task::AutoDelete
);
302 Connection
* c
= d
->connections
.connectionForFamily( 0x0002 );
306 SendDCInfoTask
* sdcit
= new SendDCInfoTask( c
->rootTask(), status
);
307 sdcit
->go( Task::AutoDelete
); //autodelete
310 // AIM: you're away exactly when your away message isn't empty.
311 // can't use QString() as a message either; ProfileTask
312 // interprets null as "don't change".
313 if ( (status
& 0xFF) == 0x00 ) //is status online?
315 msg
= QString::fromAscii("");
319 if ( message
.isEmpty() )
320 msg
= QString::fromAscii(" ");
325 ProfileTask
* pt
= new ProfileTask( c
->rootTask() );
326 pt
->setAwayMessage( msg
);
328 if ( d
->isIcq
&& xtrazChanged
)
329 pt
->setXtrazStatus( xtraz
);
331 pt
->go( Task::AutoDelete
);
333 if ( d
->isIcq
&& statusInfoChanged
)
335 ICQFullInfo
info( false );
336 info
.statusDescription
.set( description
.toUtf8() );
338 ICQTlvInfoUpdateTask
* infoUpdateTask
= new ICQTlvInfoUpdateTask( c
->rootTask() );
339 infoUpdateTask
->setInfo( info
);
340 infoUpdateTask
->go( Task::AutoDelete
);
342 d
->status
.sent
= true;
346 UserDetails
Client::ourInfo() const
348 return d
->ourDetails
;
351 QString
Client::host()
361 ContactManager
* Client::ssiManager() const
363 return d
->ssiManager
;
366 const Oscar::ClientVersion
* Client::version() const
371 Guid
Client::versionCap() const
373 return d
->versionCap
;
378 void Client::streamConnected()
380 kDebug(OSCAR_RAW_DEBUG
) ;
381 d
->stage
= ClientPrivate::StageTwo
;
382 if ( m_loginTaskTwo
)
383 m_loginTaskTwo
->go();
386 void Client::lt_loginFinished()
388 /* Check for stage two login first, since we create the stage two
389 * task when we finish stage one
391 if ( d
->stage
== ClientPrivate::StageTwo
)
393 //we've finished logging in. start the services setup
394 kDebug(OSCAR_RAW_DEBUG
) << "stage two done. setting up services";
395 initializeStaticTasks();
396 ServiceSetupTask
* ssTask
= new ServiceSetupTask( d
->connections
.defaultConnection()->rootTask() );
397 connect( ssTask
, SIGNAL( finished() ), this, SLOT( serviceSetupFinished() ) );
398 ssTask
->go( Task::AutoDelete
); //fire and forget
399 m_loginTaskTwo
->deleteLater();
402 else if ( d
->stage
== ClientPrivate::StageOne
)
404 kDebug(OSCAR_RAW_DEBUG
) << "stage one login done";
405 disconnect( m_loginTask
, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
407 if ( m_loginTask
->statusCode() == 0 ) //we can start stage two
409 kDebug(OSCAR_RAW_DEBUG
) << "no errors from stage one. moving to stage two";
411 //cache these values since they'll be deleted when we close the connections (which deletes the tasks)
412 d
->host
= m_loginTask
->bosServer();
413 d
->port
= m_loginTask
->bosPort().toUInt();
414 d
->cookie
= m_loginTask
->loginCookie();
416 QTimer::singleShot( 100, this, SLOT(startStageTwo() ) );
420 kDebug(OSCAR_RAW_DEBUG
) << "errors reported. not moving to stage two";
421 close(); //deletes the connections for us
424 m_loginTask
->deleteLater();
430 void Client::startStageTwo()
432 //create a new connection and set it up
433 Connection
* c
= createConnection();
434 new CloseConnectionTask( c
->rootTask() );
436 //create the new login task
437 m_loginTaskTwo
= new StageTwoLoginTask( c
->rootTask() );
438 m_loginTaskTwo
->setCookie( d
->cookie
);
439 QObject::connect( m_loginTaskTwo
, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
443 QObject::connect( c
, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
444 connectToServer( c
, d
->host
, d
->port
, false ) ;
448 void Client::serviceSetupFinished()
452 setStatus( d
->status
.status
, d
->status
.message
, d
->status
.xtraz
, d
->status
.description
);
453 d
->ownStatusTask
->go();
457 //retrieve offline messages
458 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
462 OfflineMessagesTask
*offlineMsgTask
= new OfflineMessagesTask( c
->rootTask() );
463 connect( offlineMsgTask
, SIGNAL( receivedOfflineMessage(const Oscar::Message
& ) ),
464 this, SIGNAL( messageReceived(const Oscar::Message
& ) ) );
465 offlineMsgTask
->go( Task::AutoDelete
);
468 emit
haveContactList();
472 void Client::receivedIcqInfo( const QString
& contact
, unsigned int type
)
474 kDebug(OSCAR_RAW_DEBUG
) << "received icq info for " << contact
475 << " of type " << type
<< endl
;
477 if ( type
== ICQUserInfoRequestTask::Short
)
478 emit
receivedIcqShortInfo( contact
);
480 emit
receivedIcqLongInfo( contact
);
483 void Client::receivedInfo( quint16 sequence
)
485 UserDetails details
= d
->userInfoTask
->getInfoFor( sequence
);
486 emit
receivedUserInfo( details
.userId(), details
);
489 void Client::offlineUser( const QString
& user
, const UserDetails
& )
491 emit
userIsOffline( user
);
494 void Client::haveOwnUserInfo()
496 kDebug( OSCAR_RAW_DEBUG
) ;
497 UserDetails ud
= d
->ownStatusTask
->getInfo();
502 void Client::setCodecProvider( Client::CodecProvider
* codecProvider
)
504 d
->codecProvider
= codecProvider
;
507 void Client::setVersion( const Oscar::ClientVersion
* version
)
509 d
->version
= version
;
512 void Client::setVersionCap( const QByteArray
&cap
)
514 d
->versionCap
= Guid( cap
);
519 QString
Client::userId() const
524 QString
Client::password() const
529 int Client::statusXtraz() const
531 return d
->status
.xtraz
;
534 QString
Client::statusDescription() const
536 return d
->status
.description
;
539 QString
Client::statusMessage() const
541 return d
->status
.message
;
544 void Client::setStatusMessage( const QString
&message
)
546 d
->status
.message
= message
;
549 QByteArray
Client::ipAddress() const
551 //!TODO determine ip address
555 void Client::notifyTaskError( const Oscar::SNAC
& s
, int errCode
, bool fatal
)
557 emit
taskError( s
, errCode
, fatal
);
560 void Client::notifySocketError( int errCode
, const QString
& msg
)
562 emit
socketError( errCode
, msg
);
565 void Client::sendMessage( const Oscar::Message
& msg
, bool isAuto
)
568 if ( msg
.channel() == 0x0003 )
570 c
= d
->connections
.connectionForChatRoom( msg
.exchange(), msg
.chatRoom() );
574 kDebug(OSCAR_RAW_DEBUG
) << "sending message to chat room";
575 ChatServiceTask
* cst
= new ChatServiceTask( c
->rootTask(), msg
.exchange(), msg
.chatRoom() );
576 cst
->setMessage( msg
);
577 cst
->setEncoding( d
->codecProvider
->codecForAccount()->name() );
578 cst
->go( Task::AutoDelete
);
582 c
= d
->connections
.connectionForFamily( 0x0004 );
585 SendMessageTask
*sendMsgTask
= new SendMessageTask( c
->rootTask() );
586 // Set whether or not the message is an automated response
587 sendMsgTask
->setAutoResponse( isAuto
);
588 sendMsgTask
->setMessage( msg
);
589 sendMsgTask
->go( Task::AutoDelete
);
593 void Client::receivedMessage( const Oscar::Message
& msg
)
595 if ( msg
.channel() == 2 && !msg
.hasProperty( Oscar::Message::AutoResponse
) )
597 // channel 2 message needs an autoresponse, regardless of type
598 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
602 Oscar::Message
response ( msg
);
603 if ( msg
.hasProperty( Oscar::Message::StatusMessageRequest
) )
605 QTextCodec
* codec
= d
->codecProvider
->codecForContact( msg
.sender() );
606 response
.setText( Oscar::Message::UserDefined
, statusMessage(), codec
);
607 emit
userReadsStatusMessage( msg
.sender() );
609 else if ( msg
.messageType() == Oscar::MessageType::Plugin
)
611 Oscar::MessagePlugin::Types type
= msg
.plugin()->type();
612 Oscar::WORD subType
= msg
.plugin()->subTypeId();
613 if ( type
== Oscar::MessagePlugin::XtrazScript
)
615 if ( subType
== Oscar::MessagePlugin::SubScriptNotify
)
617 using namespace Xtraz
;
619 xNotify
.handle( msg
.plugin() );
620 if ( xNotify
.type() == XtrazNotify::Request
&& xNotify
.pluginId() == "srvMng" )
622 if ( xNotify
.findService( "cAwaySrv" ) )
624 XtrazNotify xNotifyResponse
;
625 xNotifyResponse
.setSenderUni( userId() );
626 response
.setPlugin( xNotifyResponse
.statusResponse( statusXtraz(), statusDescription(), statusMessage() ) );
627 emit
userReadsStatusMessage( msg
.sender() );
632 else if ( type
== Oscar::MessagePlugin::StatusMsgExt
)
636 QTextCodec
* codec
= d
->codecProvider
->codecForContact( msg
.sender() );
637 buffer
.addLEDBlock( codec
->fromUnicode( statusMessage() ) );
638 //TODO: Change this to text/x-aolrtf
639 buffer
.addLEDBlock( "text/plain" );
641 msg
.plugin()->setData( buffer
.buffer() );
642 emit
userReadsStatusMessage( msg
.sender() );
647 response
.setEncoding( Oscar::Message::UserDefined
);
648 response
.setTextArray( QByteArray() );
650 response
.setReceiver( msg
.sender() );
651 response
.addProperty( Oscar::Message::AutoResponse
);
652 SendMessageTask
*sendMsgTask
= new SendMessageTask( c
->rootTask() );
653 sendMsgTask
->setMessage( response
);
654 sendMsgTask
->go( Task::AutoDelete
);
657 if ( msg
.hasProperty( Oscar::Message::AutoResponse
) )
659 if ( msg
.hasProperty( Oscar::Message::StatusMessageRequest
) )
661 // we got a response to a status message request.
662 QString
awayMessage( msg
.text( d
->codecProvider
->codecForContact( msg
.sender() ) ) );
663 kDebug( OSCAR_RAW_DEBUG
) << "Received an away message: " << awayMessage
;
664 emit
receivedAwayMessage( msg
.sender(), awayMessage
);
666 else if ( msg
.messageType() == Oscar::MessageType::Plugin
)
668 kDebug( OSCAR_RAW_DEBUG
) << "Received an plugin message response.";
670 Oscar::MessagePlugin::Types type
= msg
.plugin()->type();
671 Oscar::WORD subType
= msg
.plugin()->subTypeId();
672 if ( type
== Oscar::MessagePlugin::XtrazScript
)
674 if ( subType
== Oscar::MessagePlugin::SubScriptNotify
)
676 using namespace Xtraz
;
678 xNotify
.handle( msg
.plugin() );
679 if ( xNotify
.type() == XtrazNotify::Response
)
681 const Xtraz::XAwayService
* service
= dynamic_cast<const XAwayService
*>(xNotify
.findService( "cAwaySrv" ));
683 emit
receivedXStatusMessage( service
->senderId(), service
->iconIndex(),
684 service
->description(), service
->message() );
688 else if ( type
== Oscar::MessagePlugin::StatusMsgExt
)
690 // we got a response to a status message request.
691 Buffer
buffer( msg
.plugin()->data() );
693 QTextCodec
* codec
= d
->codecProvider
->codecForContact( msg
.sender() );
694 QString awayMessage
= codec
->toUnicode( buffer
.getLEDBlock() );
695 kDebug( OSCAR_RAW_DEBUG
) << "Received an away message: " << awayMessage
;
696 emit
receivedAwayMessage( msg
.sender(), awayMessage
);
702 if ( msg
.messageType() == Oscar::MessageType::Plugin
)
704 kDebug( OSCAR_RAW_DEBUG
) << "Received a plugin message.";
706 else if ( !msg
.hasProperty( Oscar::Message::StatusMessageRequest
) )
708 // Filter out miranda's invisible check
709 if ( msg
.messageType() == 0x0004 && msg
.textArray().isEmpty() )
712 // let application handle it
713 kDebug( OSCAR_RAW_DEBUG
) << "Emitting receivedMessage";
714 emit
messageReceived( msg
);
719 void Client::fileMessage( const Oscar::Message
& msg
)
721 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
725 kDebug( OSCAR_RAW_DEBUG
) << "internal ip: " << c
->localAddress().toString();
726 kDebug( OSCAR_RAW_DEBUG
) << "external ip: " << ourInfo().dcExternalIp().toString();
728 SendMessageTask
*sendMsgTask
= new SendMessageTask( c
->rootTask() );
729 // Set whether or not the message is an automated response
730 sendMsgTask
->setAutoResponse( false );
731 sendMsgTask
->setMessage( msg
);
732 sendMsgTask
->setIp( c
->localAddress().toIPv4Address() );
733 sendMsgTask
->go( Task::AutoDelete
);
736 void Client::requestAuth( const QString
& contactid
, const QString
& reason
)
738 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
741 d
->ssiAuthTask
->sendAuthRequest( contactid
, reason
);
744 void Client::sendAuth( const QString
& contactid
, const QString
& reason
, bool auth
)
746 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
749 d
->ssiAuthTask
->sendAuthReply( contactid
, reason
, auth
);
752 bool Client::isActive() const
757 bool Client::isIcq() const
762 void Client::setIsIcq( bool isIcq
)
767 void Client::debug( const QString
& str
)
770 // qDebug( "CLIENT: %s", str.toAscii() );
773 void Client::initializeStaticTasks()
775 //set up the extra tasks
776 Connection
* c
= d
->connections
.defaultConnection();
779 d
->errorTask
= new ErrorTask( c
->rootTask() );
780 d
->onlineNotifier
= new OnlineNotifierTask( c
->rootTask() );
781 d
->ownStatusTask
= new OwnUserInfoTask( c
->rootTask() );
782 d
->messageReceiverTask
= new MessageReceiverTask( c
->rootTask() );
783 d
->ssiAuthTask
= new SSIAuthTask( c
->rootTask() );
784 d
->icqInfoTask
= new ICQUserInfoRequestTask( c
->rootTask() );
785 d
->icqTlvInfoTask
= new ICQTlvInfoRequestTask( c
->rootTask() );
786 d
->userInfoTask
= new UserInfoTask( c
->rootTask() );
787 d
->typingNotifyTask
= new TypingNotifyTask( c
->rootTask() );
788 d
->ssiModifyTask
= new SSIModifyTask( c
->rootTask(), true );
790 connect( d
->onlineNotifier
, SIGNAL( userIsOnline( const QString
&, const UserDetails
& ) ),
791 this, SIGNAL( receivedUserInfo( const QString
&, const UserDetails
& ) ) );
792 connect( d
->onlineNotifier
, SIGNAL( userIsOffline( const QString
&, const UserDetails
& ) ),
793 this, SLOT( offlineUser( const QString
&, const UserDetails
& ) ) );
795 connect( d
->ownStatusTask
, SIGNAL( gotInfo() ), this, SLOT( haveOwnUserInfo() ) );
796 connect( d
->ownStatusTask
, SIGNAL( buddyIconUploadRequested() ), this,
797 SIGNAL( iconNeedsUploading() ) );
799 connect( d
->messageReceiverTask
, SIGNAL( receivedMessage( const Oscar::Message
& ) ),
800 this, SLOT( receivedMessage( const Oscar::Message
& ) ) );
801 connect( d
->messageReceiverTask
, SIGNAL( fileMessage( int, const QString
, const QByteArray
, Buffer
) ),
802 this, SLOT( gotFileMessage( int, const QString
, const QByteArray
, Buffer
) ) );
804 connect( d
->ssiAuthTask
, SIGNAL( authRequested( const QString
&, const QString
& ) ),
805 this, SIGNAL( authRequestReceived( const QString
&, const QString
& ) ) );
806 connect( d
->ssiAuthTask
, SIGNAL( authReplied( const QString
&, const QString
&, bool ) ),
807 this, SIGNAL( authReplyReceived( const QString
&, const QString
&, bool ) ) );
809 connect( d
->icqInfoTask
, SIGNAL( receivedInfoFor( const QString
&, unsigned int ) ),
810 this, SLOT( receivedIcqInfo( const QString
&, unsigned int ) ) );
811 connect( d
->icqTlvInfoTask
, SIGNAL(receivedInfoFor(const QString
&)),
812 this, SIGNAL(receivedIcqTlvInfo(const QString
&)) );
814 connect( d
->userInfoTask
, SIGNAL( receivedProfile( const QString
&, const QString
& ) ),
815 this, SIGNAL( receivedProfile( const QString
&, const QString
& ) ) );
816 connect( d
->userInfoTask
, SIGNAL( receivedAwayMessage( const QString
&, const QString
& ) ),
817 this, SIGNAL( receivedAwayMessage( const QString
&, const QString
& ) ) );
818 connect( d
->typingNotifyTask
, SIGNAL( typingStarted( const QString
& ) ),
819 this, SIGNAL( userStartedTyping( const QString
& ) ) );
820 connect( d
->typingNotifyTask
, SIGNAL( typingFinished( const QString
& ) ),
821 this, SIGNAL( userStoppedTyping( const QString
& ) ) );
824 void Client::removeGroup( const QString
& groupName
)
826 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
830 kDebug( OSCAR_RAW_DEBUG
) << "Removing group " << groupName
<< " from Contact";
831 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
832 if ( ssimt
->removeGroup( groupName
) )
833 ssimt
->go( Task::AutoDelete
);
838 void Client::addGroup( const QString
& groupName
)
840 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
844 kDebug( OSCAR_RAW_DEBUG
) << "Adding group " << groupName
<< " to Contact";
845 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
846 if ( ssimt
->addGroup( groupName
) )
847 ssimt
->go( Task::AutoDelete
);
852 void Client::addContact( const QString
& contactName
, const QString
& groupName
)
854 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
858 kDebug( OSCAR_RAW_DEBUG
) << "Adding contact " << contactName
<< " to ssi in group " << groupName
;
859 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
860 if ( ssimt
->addContact( contactName
, groupName
) )
861 ssimt
->go( Task::AutoDelete
);
866 void Client::removeContact( const QString
& contactName
)
868 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
872 kDebug( OSCAR_RAW_DEBUG
) << "Removing contact " << contactName
<< " from ssi";
873 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
874 if ( ssimt
->removeContact( contactName
) )
875 ssimt
->go( Task::AutoDelete
);
880 void Client::renameGroup( const QString
& oldGroupName
, const QString
& newGroupName
)
882 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
886 kDebug( OSCAR_RAW_DEBUG
) << "Renaming group " << oldGroupName
<< " to " << newGroupName
;
887 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
888 if ( ssimt
->renameGroup( oldGroupName
, newGroupName
) )
889 ssimt
->go( Task::AutoDelete
);
894 void Client::modifyContactItem( const OContact
& oldItem
, const OContact
& newItem
)
896 int action
= 0; //0 modify, 1 add, 2 remove TODO cleanup!
897 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
901 if ( !oldItem
&& newItem
)
903 if ( oldItem
&& !newItem
)
906 kDebug(OSCAR_RAW_DEBUG
) << "Add/Mod/Del item on server";
907 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
911 if ( ssimt
->modifyItem( oldItem
, newItem
) )
912 ssimt
->go( Task::AutoDelete
);
917 if ( ssimt
->addItem( newItem
) )
918 ssimt
->go( Task::AutoDelete
);
923 if ( ssimt
->removeItem( oldItem
) )
924 ssimt
->go( Task::AutoDelete
);
931 void Client::changeContactGroup( const QString
& contact
, const QString
& newGroupName
)
933 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
937 kDebug(OSCAR_RAW_DEBUG
) << "Changing " << contact
<< "'s group to "
938 << newGroupName
<< endl
;
939 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
940 if ( ssimt
->changeGroup( contact
, newGroupName
) )
941 ssimt
->go( Task::AutoDelete
);
946 void Client::changeContactAlias( const QString
& contact
, const QString
& alias
)
948 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
952 OContact item
= ssiManager()->findContact( contact
);
955 OContact
oldItem(item
);
957 if ( alias
.isEmpty() )
959 QList
<TLV
> tList( item
.tlvList() );
960 TLV tlv
= Oscar::findTLV( tList
, 0x0131 );
964 tList
.removeAll( tlv
);
965 item
.setTLVList( tList
);
971 QByteArray data
= alias
.toUtf8();
972 tList
.append( TLV( 0x0131, data
.size(), data
) );
974 if ( !Oscar::updateTLVs( item
, tList
) )
978 kDebug( OSCAR_RAW_DEBUG
) << "Changing " << contact
<< "'s alias to " << alias
;
979 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
980 if ( ssimt
->modifyContact( oldItem
, item
) )
981 ssimt
->go( Task::AutoDelete
);
987 void Client::setPrivacyTLVs( Oscar::BYTE privacy
, Oscar::DWORD userClasses
)
989 OContact item
= ssiManager()->findItem( QString(), ROSTER_VISIBILITY
);
991 QList
<Oscar::TLV
> tList
;
992 tList
.append( TLV( 0x00CA, 1, (char *)&privacy
) );
993 tList
.append( TLV( 0x00CB, sizeof(userClasses
), (char *)&userClasses
) );
997 kDebug( OSCAR_RAW_DEBUG
) << "Adding new privacy TLV item";
999 OContact
s( empty
, 0, ssiManager()->nextContactId(), ROSTER_VISIBILITY
, tList
);
1000 modifyContactItem( item
, s
);
1006 if ( Oscar::updateTLVs( s
, tList
) == true )
1008 kDebug( OSCAR_RAW_DEBUG
) << "Updating privacy TLV item";
1009 modifyContactItem( item
, s
);
1014 void Client::requestShortTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
1016 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1020 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
1021 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
1022 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Short
);
1023 d
->icqTlvInfoTask
->go();
1026 void Client::requestMediumTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
1028 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1032 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
1033 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
1034 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Medium
);
1035 d
->icqTlvInfoTask
->go();
1038 void Client::requestLongTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
1040 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1044 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
1045 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
1046 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Long
);
1047 d
->icqTlvInfoTask
->go();
1050 void Client::requestFullInfo( const QString
& contactId
)
1052 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1055 d
->icqInfoTask
->setUser( contactId
);
1056 d
->icqInfoTask
->setType( ICQUserInfoRequestTask::Long
);
1057 d
->icqInfoTask
->go();
1060 void Client::requestShortInfo( const QString
& contactId
)
1062 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1065 d
->icqInfoTask
->setUser( contactId
);
1066 d
->icqInfoTask
->setType( ICQUserInfoRequestTask::Short
);
1067 d
->icqInfoTask
->go();
1070 void Client::sendWarning( const QString
& contact
, bool anonymous
)
1072 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1075 WarningTask
* warnTask
= new WarningTask( c
->rootTask() );
1076 warnTask
->setContact( contact
);
1077 warnTask
->setAnonymous( anonymous
);
1078 QObject::connect( warnTask
, SIGNAL( userWarned( const QString
&, quint16
, quint16
) ),
1079 this, SIGNAL( userWarned( const QString
&, quint16
, quint16
) ) );
1080 warnTask
->go( Task::AutoDelete
);
1083 bool Client::changeICQPassword( const QString
& password
)
1085 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1089 ICQChangePasswordTask
* task
= new ICQChangePasswordTask( c
->rootTask() );
1090 QObject::connect( task
, SIGNAL(finished()), this, SLOT(changeICQPasswordFinished()) );
1091 task
->setPassword( password
);
1092 task
->go( Task::AutoDelete
);
1096 void Client::changeICQPasswordFinished()
1098 ICQChangePasswordTask
* task
= (ICQChangePasswordTask
*)sender();
1099 if ( task
->success() )
1100 d
->pass
= task
->password();
1102 emit
icqPasswordChanged( !task
->success() );
1105 ICQFullInfo
Client::getFullInfo( const QString
& contact
)
1107 return d
->icqTlvInfoTask
->fullInfoFor( contact
);
1110 ICQGeneralUserInfo
Client::getGeneralInfo( const QString
& contact
)
1112 return d
->icqInfoTask
->generalInfoFor( contact
);
1115 ICQWorkUserInfo
Client::getWorkInfo( const QString
& contact
)
1117 return d
->icqInfoTask
->workInfoFor( contact
);
1120 ICQEmailInfo
Client::getEmailInfo( const QString
& contact
)
1122 return d
->icqInfoTask
->emailInfoFor( contact
);
1125 ICQNotesInfo
Client::getNotesInfo( const QString
& contact
)
1127 return d
->icqInfoTask
->notesInfoFor( contact
);
1130 ICQMoreUserInfo
Client::getMoreInfo( const QString
& contact
)
1132 return d
->icqInfoTask
->moreInfoFor( contact
);
1135 ICQInterestInfo
Client::getInterestInfo( const QString
& contact
)
1137 return d
->icqInfoTask
->interestInfoFor( contact
);
1140 ICQOrgAffInfo
Client::getOrgAffInfo( const QString
& contact
)
1142 return d
->icqInfoTask
->orgAffInfoFor( contact
);
1145 ICQShortInfo
Client::getShortInfo( const QString
& contact
)
1147 return d
->icqInfoTask
->shortInfoFor( contact
);
1150 QList
<int> Client::chatExchangeList() const
1152 return d
->exchanges
;
1155 void Client::setChatExchangeList( const QList
<int>& exchanges
)
1157 d
->exchanges
= exchanges
;
1160 void Client::requestAIMProfile( const QString
& contact
)
1162 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::Profile
);
1165 void Client::requestAIMAwayMessage( const QString
& contact
)
1167 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::AwayMessage
);
1170 void Client::requestICQAwayMessage( const QString
& contact
, ICQStatus contactStatus
)
1172 kDebug(OSCAR_RAW_DEBUG
) << "requesting away message for " << contact
;
1174 msg
.setChannel( 2 );
1175 msg
.setReceiver( contact
);
1177 if ( (contactStatus
& ICQXStatus
) == ICQXStatus
)
1179 Xtraz::XtrazNotify xNotify
;
1180 xNotify
.setSenderUni( userId() );
1182 msg
.setMessageType( Oscar::MessageType::Plugin
); // plugin message
1183 msg
.setPlugin( xNotify
.statusRequest() );
1185 else if ( (contactStatus
& ICQPluginStatus
) == ICQPluginStatus
)
1187 Oscar::WORD subTypeId
= 0xFFFF;
1188 QByteArray subTypeText
;
1190 switch ( contactStatus
& ICQStatusMask
)
1193 case ICQFreeForChat
:
1196 subTypeText
= "Away Status Message";
1199 case ICQDoNotDisturb
:
1201 subTypeText
= "Busy Status Message";
1203 case ICQNotAvailable
:
1205 subTypeText
= "N/A Status Message";
1208 // may be a good way to deal with possible error and lack of online status message?
1209 emit
receivedAwayMessage( contact
, "Sorry, this protocol does not support this type of status message" );
1213 Oscar::MessagePlugin
*plugin
= new Oscar::MessagePlugin();
1214 plugin
->setType( Oscar::MessagePlugin::StatusMsgExt
);
1215 plugin
->setSubTypeId( subTypeId
);
1216 plugin
->setSubTypeText( subTypeText
);
1219 buffer
.addLEDWord( 0x00000000 );
1220 //TODO: Change this to text/x-aolrtf
1221 buffer
.addLEDBlock( "text/plain" );
1222 plugin
->setData( buffer
.buffer() );
1224 msg
.setMessageType( Oscar::MessageType::Plugin
); // plugin message
1225 msg
.setPlugin( plugin
);
1229 msg
.addProperty( Oscar::Message::StatusMessageRequest
);
1230 switch ( contactStatus
& ICQStatusMask
)
1233 msg
.setMessageType( Oscar::MessageType::AutoAway
); // away
1236 msg
.setMessageType( Oscar::MessageType::AutoBusy
); // occupied
1238 case ICQNotAvailable
:
1239 msg
.setMessageType( Oscar::MessageType::AutoNA
); // not awailable
1241 case ICQDoNotDisturb
:
1242 msg
.setMessageType( Oscar::MessageType::AutoDND
); // do not disturb
1244 case ICQFreeForChat
:
1245 msg
.setMessageType( Oscar::MessageType::AutoFFC
); // free for chat
1248 // may be a good way to deal with possible error and lack of online status message?
1249 emit
receivedAwayMessage( contact
, "Sorry, this protocol does not support this type of status message" );
1256 void Client::addICQAwayMessageRequest( const QString
& contact
, ICQStatus contactStatus
)
1258 kDebug(OSCAR_RAW_DEBUG
) << "adding away message request for "
1259 << contact
<< " to queue" << endl
;
1261 //remove old request if still exists
1262 removeICQAwayMessageRequest( contact
);
1264 ClientPrivate::AwayMsgRequest amr
= { contact
, contactStatus
};
1265 d
->awayMsgRequestQueue
.prepend( amr
);
1267 if ( !d
->awayMsgRequestTimer
->isActive() )
1268 d
->awayMsgRequestTimer
->start( 1000 );
1271 void Client::removeICQAwayMessageRequest( const QString
& contact
)
1273 kDebug(OSCAR_RAW_DEBUG
) << "removing away message request for "
1274 << contact
<< " from queue" << endl
;
1276 QList
<ClientPrivate::AwayMsgRequest
>::iterator it
= d
->awayMsgRequestQueue
.begin();
1277 while ( it
!= d
->awayMsgRequestQueue
.end() )
1279 if ( (*it
).contact
== contact
)
1280 it
= d
->awayMsgRequestQueue
.erase( it
);
1286 void Client::nextICQAwayMessageRequest()
1288 kDebug(OSCAR_RAW_DEBUG
) << "request queue count " << d
->awayMsgRequestQueue
.count();
1290 if ( d
->awayMsgRequestQueue
.empty() )
1292 d
->awayMsgRequestTimer
->stop();
1297 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1301 SNAC s
= { 0x0004, 0x0006, 0x0000, 0x00000000 };
1302 //get time needed to restore level to initial
1303 //for some reason when we are long under initial level
1304 //icq server will start to block our messages
1305 int time
= c
->rateManager()->timeToInitialLevel( s
);
1308 d
->awayMsgRequestTimer
->start( time
);
1313 d
->awayMsgRequestTimer
->start( 5000 );
1317 ClientPrivate::AwayMsgRequest amr
;
1319 amr
= d
->awayMsgRequestQueue
.back();
1320 d
->awayMsgRequestQueue
.pop_back();
1321 requestICQAwayMessage( amr
.contact
, amr
.contactStatus
);
1324 void Client::requestStatusInfo( const QString
& contact
)
1326 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::General
);
1329 void Client::whitePagesSearch( const ICQWPSearchInfo
& info
)
1331 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1334 UserSearchTask
* ust
= new UserSearchTask( c
->rootTask() );
1335 connect( ust
, SIGNAL( foundUser( const ICQSearchResult
& ) ),
1336 this, SIGNAL( gotSearchResults( const ICQSearchResult
& ) ) );
1337 connect( ust
, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
1338 ust
->go( Task::AutoDelete
); //onGo does nothing in this task. This is just here so autodelete works
1339 ust
->searchWhitePages( info
);
1342 void Client::uinSearch( const QString
& uin
)
1344 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1347 UserSearchTask
* ust
= new UserSearchTask( c
->rootTask() );
1348 connect( ust
, SIGNAL( foundUser( const ICQSearchResult
& ) ),
1349 this, SIGNAL( gotSearchResults( const ICQSearchResult
& ) ) );
1350 connect( ust
, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
1351 ust
->go( Task::AutoDelete
); //onGo does nothing in this task. This is just here so autodelete works
1352 ust
->searchUserByUIN( uin
);
1355 void Client::updateProfile( const QString
& profile
)
1357 Connection
* c
= d
->connections
.connectionForFamily( 0x0002 );
1360 ProfileTask
* pt
= new ProfileTask( c
->rootTask() );
1361 pt
->setProfileText( profile
);
1362 pt
->go( Task::AutoDelete
);
1365 bool Client::updateProfile( const QList
<ICQInfoBase
*>& infoList
)
1367 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1371 ICQUserInfoUpdateTask
* ui
= new ICQUserInfoUpdateTask( c
->rootTask() );
1372 ui
->setInfo( infoList
);
1373 ui
->go( Task::AutoDelete
);
1377 void Client::sendTyping( const QString
& contact
, bool typing
)
1379 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1382 d
->typingNotifyTask
->setParams( contact
, ( typing
? TypingNotifyTask::Begin
: TypingNotifyTask::Finished
) );
1383 d
->typingNotifyTask
->go(); // don't delete the task after sending
1386 void Client::connectToIconServer()
1388 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1392 requestServerRedirect( 0x0010 );
1396 void Client::setIgnore( const QString
& user
, bool ignore
)
1398 OContact item
= ssiManager()->findItem( user
, ROSTER_IGNORE
);
1399 if ( item
&& !ignore
)
1401 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from ignore list";
1402 this->modifyContactItem( item
, OContact() );
1404 else if ( !item
&& ignore
)
1406 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to ignore list";
1407 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_IGNORE
, QList
<TLV
>() );
1408 this->modifyContactItem( OContact(), s
);
1412 void Client::setVisibleTo( const QString
& user
, bool visible
)
1414 OContact item
= ssiManager()->findItem( user
, ROSTER_VISIBLE
);
1415 if ( item
&& !visible
)
1417 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from visible list";
1418 this->modifyContactItem( item
, OContact() );
1420 else if ( !item
&& visible
)
1422 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to visible list";
1423 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE
, QList
<TLV
>() );
1424 this->modifyContactItem( OContact(), s
);
1428 void Client::setInvisibleTo( const QString
& user
, bool invisible
)
1430 OContact item
= ssiManager()->findItem( user
, ROSTER_INVISIBLE
);
1431 if ( item
&& !invisible
)
1433 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from invisible list";
1434 this->modifyContactItem( item
, OContact() );
1436 else if ( !item
&& invisible
)
1438 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to invisible list";
1439 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE
, QList
<TLV
>() );
1440 this->modifyContactItem( OContact(), s
);
1444 void Client::requestBuddyIcon( const QString
& user
, const QByteArray
& hash
, Oscar::WORD iconType
, Oscar::BYTE hashType
)
1446 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1450 BuddyIconTask
* bit
= new BuddyIconTask( c
->rootTask() );
1451 connect( bit
, SIGNAL( haveIcon( const QString
&, QByteArray
) ),
1452 this, SIGNAL( haveIconForContact( const QString
&, QByteArray
) ) );
1453 bit
->requestIconFor( user
);
1454 bit
->setIconType( iconType
);
1455 bit
->setHashType( hashType
);
1456 bit
->setHash( hash
);
1457 bit
->go( Task::AutoDelete
);
1460 void Client::requestServerRedirect( Oscar::WORD family
, Oscar::WORD exchange
,
1461 QByteArray cookie
, Oscar::WORD instance
,
1462 const QString
& room
)
1464 //making the assumption that family 2 will always be the BOS connection
1465 //use it instead since we can't query for family 1
1466 Connection
* c
= d
->connections
.connectionForFamily( family
);
1467 if ( c
&& family
!= 0x000E )
1468 return; //we already have the connection
1470 c
= d
->connections
.connectionForFamily( 0x0002 );
1474 if ( d
->redirectionServices
.indexOf( family
) == -1 )
1475 d
->redirectionServices
.append( family
); //don't add families twice
1477 if ( d
->currentRedirect
!= 0 )
1478 return; //we're already doing one redirection
1480 d
->currentRedirect
= family
;
1482 //FIXME. this won't work if we have to defer the connection because we're
1483 //already connecting to something
1484 ServerRedirectTask
* srt
= new ServerRedirectTask( c
->rootTask() );
1485 if ( family
== 0x000E )
1487 srt
->setChatParams( exchange
, cookie
, instance
);
1488 srt
->setChatRoom( room
);
1491 connect( srt
, SIGNAL( haveServer( const QString
&, const QByteArray
&, Oscar::WORD
) ),
1492 this, SLOT( haveServerForRedirect( const QString
&, const QByteArray
&, Oscar::WORD
) ) );
1493 srt
->setService( family
);
1494 srt
->go( Task::AutoDelete
);
1497 void Client::haveServerForRedirect( const QString
& host
, const QByteArray
& cookie
, Oscar::WORD
)
1499 //nasty sender() usage to get the task with chat room info
1500 QObject
* o
= const_cast<QObject
*>( sender() );
1501 ServerRedirectTask
* srt
= dynamic_cast<ServerRedirectTask
*>( o
);
1503 //create a new connection and set it up
1504 int colonPos
= host
.indexOf(':');
1505 QString realHost
, realPort
;
1506 if ( colonPos
!= -1 )
1508 realHost
= host
.left( colonPos
);
1509 realPort
= host
.right(4); //we only need 4 bytes
1514 realPort
= QString::fromLatin1("5190");
1517 Connection
* c
= createConnection();
1518 //create the new login task
1519 m_loginTaskTwo
= new StageTwoLoginTask( c
->rootTask() );
1520 m_loginTaskTwo
->setCookie( cookie
);
1521 QObject::connect( m_loginTaskTwo
, SIGNAL( finished() ), this, SLOT( serverRedirectFinished() ) );
1524 connectToServer( c
, realHost
, realPort
.toInt(), false );
1525 QObject::connect( c
, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
1528 d
->connections
.addChatInfoForConnection( c
, srt
->chatExchange(), srt
->chatRoomName() );
1531 void Client::serverRedirectFinished()
1533 if ( m_loginTaskTwo
&& m_loginTaskTwo
->statusCode() == 0 )
1534 { //stage two was successful
1535 Connection
* c
= d
->connections
.connectionForFamily( d
->currentRedirect
);
1538 ClientReadyTask
* crt
= new ClientReadyTask( c
->rootTask() );
1539 crt
->setFamilies( c
->supportedFamilies() );
1540 crt
->go( Task::AutoDelete
);
1543 kDebug(OSCAR_RAW_DEBUG
) << "redirection finished for service "
1544 << d
->currentRedirect
<< endl
;
1546 if ( d
->currentRedirect
== 0x0010 )
1547 emit
iconServerConnected();
1549 if ( d
->currentRedirect
== 0x000D )
1551 connect( this, SIGNAL( chatNavigationConnected() ),
1552 this, SLOT( requestChatNavLimits() ) );
1553 emit
chatNavigationConnected();
1556 if ( d
->currentRedirect
== 0x000E )
1558 //HACK! such abuse! think of a better way
1559 if ( !m_loginTaskTwo
)
1561 kWarning(OSCAR_RAW_DEBUG
) << "no login task to get connection from!";
1562 emit
redirectionFinished( d
->currentRedirect
);
1566 Connection
* c
= m_loginTaskTwo
->client();
1567 QString roomName
= d
->connections
.chatRoomForConnection( c
);
1568 Oscar::WORD exchange
= d
->connections
.exchangeForConnection( c
);
1571 kDebug(OSCAR_RAW_DEBUG
) << "setting up chat connection";
1572 ChatServiceTask
* cst
= new ChatServiceTask( c
->rootTask(), exchange
, roomName
);
1573 connect( cst
, SIGNAL( userJoinedChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ),
1574 this, SIGNAL( userJoinedChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ) );
1575 connect( cst
, SIGNAL( userLeftChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ),
1576 this, SIGNAL( userLeftChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ) );
1577 connect( cst
, SIGNAL( newChatMessage( const Oscar::Message
& ) ),
1578 this, SIGNAL( messageReceived( const Oscar::Message
& ) ) );
1580 emit
chatRoomConnected( exchange
, roomName
);
1583 emit
redirectionFinished( d
->currentRedirect
);
1587 void Client::checkRedirectionQueue( Oscar::WORD family
)
1589 kDebug(OSCAR_RAW_DEBUG
) << "checking redirection queue";
1590 d
->redirectionServices
.removeAll( family
);
1591 d
->currentRedirect
= 0;
1592 if ( !d
->redirectionServices
.isEmpty() )
1594 kDebug(OSCAR_RAW_DEBUG
) << "scheduling new redirection";
1595 requestServerRedirect( d
->redirectionServices
.front() );
1600 void Client::requestChatNavLimits()
1602 Connection
* c
= d
->connections
.connectionForFamily( 0x000D );
1606 kDebug(OSCAR_RAW_DEBUG
) << "requesting chat nav service limits";
1607 ChatNavServiceTask
* cnst
= new ChatNavServiceTask( c
->rootTask() );
1608 cnst
->setRequestType( ChatNavServiceTask::Limits
);
1609 QObject::connect( cnst
, SIGNAL( haveChatExchanges( const QList
<int>& ) ),
1610 this, SLOT( setChatExchangeList( const QList
<int>& ) ) );
1611 cnst
->go( Task::AutoDelete
); //autodelete
1615 void Client::determineDisconnection( int code
, const QString
& string
)
1620 //yay for the sender() hack!
1621 QObject
* obj
= const_cast<QObject
*>( sender() );
1622 Connection
* c
= dynamic_cast<Connection
*>( obj
);
1626 if ( c
->isSupported( 0x0002 ) ||
1627 d
->stage
== ClientPrivate::StageOne
) //emit on login
1629 emit
socketError( code
, string
);
1632 //connection is deleted. deleteLater() is used
1633 d
->connections
.remove( c
);
1637 void Client::sendBuddyIcon( const QByteArray
& iconData
)
1639 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1643 kDebug(OSCAR_RAW_DEBUG
) << "icon length is " << iconData
.size();
1644 BuddyIconTask
* bit
= new BuddyIconTask( c
->rootTask() );
1645 bit
->uploadIcon( iconData
.size(), iconData
);
1646 bit
->go( Task::AutoDelete
);
1649 void Client::joinChatRoom( const QString
& roomName
, int exchange
)
1651 Connection
* c
= d
->connections
.connectionForFamily( 0x000D );
1655 kDebug(OSCAR_RAW_DEBUG
) << "joining the chat room '" << roomName
1656 << "' on exchange " << exchange
<< endl
;
1657 ChatNavServiceTask
* cnst
= new ChatNavServiceTask( c
->rootTask() );
1658 connect( cnst
, SIGNAL( connectChat( Oscar::WORD
, QByteArray
, Oscar::WORD
, const QString
& ) ),
1659 this, SLOT( setupChatConnection( Oscar::WORD
, QByteArray
, Oscar::WORD
, const QString
& ) ) );
1660 cnst
->createRoom( exchange
, roomName
);
1664 void Client::setupChatConnection( Oscar::WORD exchange
, QByteArray cookie
, Oscar::WORD instance
, const QString
& room
)
1666 kDebug(OSCAR_RAW_DEBUG
) << "cookie is:" << cookie
;
1667 QByteArray
realCookie( cookie
);
1668 kDebug(OSCAR_RAW_DEBUG
) << "connection to chat room";
1669 requestServerRedirect( 0x000E, exchange
, realCookie
, instance
, room
);
1672 void Client::disconnectChatRoom( Oscar::WORD exchange
, const QString
& room
)
1674 Connection
* c
= d
->connections
.connectionForChatRoom( exchange
, room
);
1678 d
->connections
.remove( c
);
1682 Connection
* Client::createConnection()
1684 ClientStream
* cs
= new ClientStream( new QTcpSocket(), 0 );
1685 cs
->setNoopTime( 60000 );
1686 Connection
* c
= new Connection( cs
, "BOS" );
1687 cs
->setConnection( c
);
1688 c
->setClient( this );
1692 void Client::deleteStaticTasks()
1694 delete d
->errorTask
;
1695 delete d
->onlineNotifier
;
1696 delete d
->ownStatusTask
;
1697 delete d
->messageReceiverTask
;
1698 delete d
->ssiAuthTask
;
1699 delete d
->icqInfoTask
;
1700 delete d
->icqTlvInfoTask
;
1701 delete d
->userInfoTask
;
1702 delete d
->typingNotifyTask
;
1703 delete d
->ssiModifyTask
;
1706 d
->onlineNotifier
= 0;
1707 d
->ownStatusTask
= 0;
1708 d
->messageReceiverTask
= 0;
1711 d
->icqTlvInfoTask
= 0;
1712 d
->userInfoTask
= 0;
1713 d
->typingNotifyTask
= 0;
1714 d
->ssiModifyTask
= 0;
1717 bool Client::hasIconConnection( ) const
1719 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1723 void Client::sendFiles( const QString
& contact
, const QStringList
& files
, Kopete::Transfer
*t
)
1725 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1729 FileTransferTask
*ft
= new FileTransferTask( c
->rootTask(), contact
, ourInfo().userId(), files
, t
);
1730 connect( ft
, SIGNAL( sendMessage( const Oscar::Message
& ) ),
1731 this, SLOT( fileMessage( const Oscar::Message
& ) ) );
1732 ft
->go( Task::AutoDelete
);
1735 void Client::gotFileMessage( int type
, const QString from
, const QByteArray cookie
, Buffer buf
)
1737 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1740 //pass the message to the matching task if we can
1741 const QList
<FileTransferTask
*> p
= c
->rootTask()->findChildren
<FileTransferTask
*>();
1742 foreach( FileTransferTask
*t
, p
)
1744 if ( t
->take( type
, cookie
, buf
) )
1749 //maybe it's a new request!
1752 kDebug(14151) << "new request :)";
1753 FileTransferTask
*ft
= new FileTransferTask( c
->rootTask(), from
, ourInfo().userId(), cookie
, buf
);
1754 connect( ft
, SIGNAL( getTransferManager( Kopete::TransferManager
** ) ),
1755 SIGNAL( getTransferManager( Kopete::TransferManager
** ) ) );
1756 connect( ft
, SIGNAL( askIncoming( QString
, QString
, Oscar::DWORD
, QString
, QString
) ),
1757 SIGNAL( askIncoming( QString
, QString
, Oscar::DWORD
, QString
, QString
) ) );
1758 connect( ft
, SIGNAL( sendMessage( const Oscar::Message
& ) ),
1759 this, SLOT( fileMessage( const Oscar::Message
& ) ) );
1760 ft
->go( Task::AutoDelete
);
1764 kDebug(14151) << "nobody wants it :(";
1767 #include "client.moc"
1768 //kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;