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 // let application handle it
709 kDebug( OSCAR_RAW_DEBUG
) << "Emitting receivedMessage";
710 emit
messageReceived( msg
);
715 void Client::fileMessage( const Oscar::Message
& msg
)
717 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
721 kDebug( OSCAR_RAW_DEBUG
) << "internal ip: " << c
->localAddress().toString();
722 kDebug( OSCAR_RAW_DEBUG
) << "external ip: " << ourInfo().dcExternalIp().toString();
724 SendMessageTask
*sendMsgTask
= new SendMessageTask( c
->rootTask() );
725 // Set whether or not the message is an automated response
726 sendMsgTask
->setAutoResponse( false );
727 sendMsgTask
->setMessage( msg
);
728 sendMsgTask
->setIp( c
->localAddress().toIPv4Address() );
729 sendMsgTask
->go( Task::AutoDelete
);
732 void Client::requestAuth( const QString
& contactid
, const QString
& reason
)
734 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
737 d
->ssiAuthTask
->sendAuthRequest( contactid
, reason
);
740 void Client::sendAuth( const QString
& contactid
, const QString
& reason
, bool auth
)
742 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
745 d
->ssiAuthTask
->sendAuthReply( contactid
, reason
, auth
);
748 bool Client::isActive() const
753 bool Client::isIcq() const
758 void Client::setIsIcq( bool isIcq
)
763 void Client::debug( const QString
& str
)
766 // qDebug( "CLIENT: %s", str.toAscii() );
769 void Client::initializeStaticTasks()
771 //set up the extra tasks
772 Connection
* c
= d
->connections
.defaultConnection();
775 d
->errorTask
= new ErrorTask( c
->rootTask() );
776 d
->onlineNotifier
= new OnlineNotifierTask( c
->rootTask() );
777 d
->ownStatusTask
= new OwnUserInfoTask( c
->rootTask() );
778 d
->messageReceiverTask
= new MessageReceiverTask( c
->rootTask() );
779 d
->ssiAuthTask
= new SSIAuthTask( c
->rootTask() );
780 d
->icqInfoTask
= new ICQUserInfoRequestTask( c
->rootTask() );
781 d
->icqTlvInfoTask
= new ICQTlvInfoRequestTask( c
->rootTask() );
782 d
->userInfoTask
= new UserInfoTask( c
->rootTask() );
783 d
->typingNotifyTask
= new TypingNotifyTask( c
->rootTask() );
784 d
->ssiModifyTask
= new SSIModifyTask( c
->rootTask(), true );
786 connect( d
->onlineNotifier
, SIGNAL( userIsOnline( const QString
&, const UserDetails
& ) ),
787 this, SIGNAL( receivedUserInfo( const QString
&, const UserDetails
& ) ) );
788 connect( d
->onlineNotifier
, SIGNAL( userIsOffline( const QString
&, const UserDetails
& ) ),
789 this, SLOT( offlineUser( const QString
&, const UserDetails
& ) ) );
791 connect( d
->ownStatusTask
, SIGNAL( gotInfo() ), this, SLOT( haveOwnUserInfo() ) );
792 connect( d
->ownStatusTask
, SIGNAL( buddyIconUploadRequested() ), this,
793 SIGNAL( iconNeedsUploading() ) );
795 connect( d
->messageReceiverTask
, SIGNAL( receivedMessage( const Oscar::Message
& ) ),
796 this, SLOT( receivedMessage( const Oscar::Message
& ) ) );
797 connect( d
->messageReceiverTask
, SIGNAL( fileMessage( int, const QString
, const QByteArray
, Buffer
) ),
798 this, SLOT( gotFileMessage( int, const QString
, const QByteArray
, Buffer
) ) );
800 connect( d
->ssiAuthTask
, SIGNAL( authRequested( const QString
&, const QString
& ) ),
801 this, SIGNAL( authRequestReceived( const QString
&, const QString
& ) ) );
802 connect( d
->ssiAuthTask
, SIGNAL( authReplied( const QString
&, const QString
&, bool ) ),
803 this, SIGNAL( authReplyReceived( const QString
&, const QString
&, bool ) ) );
805 connect( d
->icqInfoTask
, SIGNAL( receivedInfoFor( const QString
&, unsigned int ) ),
806 this, SLOT( receivedIcqInfo( const QString
&, unsigned int ) ) );
807 connect( d
->icqTlvInfoTask
, SIGNAL(receivedInfoFor(const QString
&)),
808 this, SIGNAL(receivedIcqTlvInfo(const QString
&)) );
810 connect( d
->userInfoTask
, SIGNAL( receivedProfile( const QString
&, const QString
& ) ),
811 this, SIGNAL( receivedProfile( const QString
&, const QString
& ) ) );
812 connect( d
->userInfoTask
, SIGNAL( receivedAwayMessage( const QString
&, const QString
& ) ),
813 this, SIGNAL( receivedAwayMessage( const QString
&, const QString
& ) ) );
814 connect( d
->typingNotifyTask
, SIGNAL( typingStarted( const QString
& ) ),
815 this, SIGNAL( userStartedTyping( const QString
& ) ) );
816 connect( d
->typingNotifyTask
, SIGNAL( typingFinished( const QString
& ) ),
817 this, SIGNAL( userStoppedTyping( const QString
& ) ) );
820 void Client::removeGroup( const QString
& groupName
)
822 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
826 kDebug( OSCAR_RAW_DEBUG
) << "Removing group " << groupName
<< " from Contact";
827 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
828 if ( ssimt
->removeGroup( groupName
) )
829 ssimt
->go( Task::AutoDelete
);
834 void Client::addGroup( const QString
& groupName
)
836 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
840 kDebug( OSCAR_RAW_DEBUG
) << "Adding group " << groupName
<< " to Contact";
841 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
842 if ( ssimt
->addGroup( groupName
) )
843 ssimt
->go( Task::AutoDelete
);
848 void Client::addContact( const QString
& contactName
, const QString
& groupName
)
850 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
854 kDebug( OSCAR_RAW_DEBUG
) << "Adding contact " << contactName
<< " to ssi in group " << groupName
;
855 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
856 if ( ssimt
->addContact( contactName
, groupName
) )
857 ssimt
->go( Task::AutoDelete
);
862 void Client::removeContact( const QString
& contactName
)
864 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
868 kDebug( OSCAR_RAW_DEBUG
) << "Removing contact " << contactName
<< " from ssi";
869 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
870 if ( ssimt
->removeContact( contactName
) )
871 ssimt
->go( Task::AutoDelete
);
876 void Client::renameGroup( const QString
& oldGroupName
, const QString
& newGroupName
)
878 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
882 kDebug( OSCAR_RAW_DEBUG
) << "Renaming group " << oldGroupName
<< " to " << newGroupName
;
883 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
884 if ( ssimt
->renameGroup( oldGroupName
, newGroupName
) )
885 ssimt
->go( Task::AutoDelete
);
890 void Client::modifyContactItem( const OContact
& oldItem
, const OContact
& newItem
)
892 int action
= 0; //0 modify, 1 add, 2 remove TODO cleanup!
893 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
897 if ( !oldItem
&& newItem
)
899 if ( oldItem
&& !newItem
)
902 kDebug(OSCAR_RAW_DEBUG
) << "Add/Mod/Del item on server";
903 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
907 if ( ssimt
->modifyItem( oldItem
, newItem
) )
908 ssimt
->go( Task::AutoDelete
);
913 if ( ssimt
->addItem( newItem
) )
914 ssimt
->go( Task::AutoDelete
);
919 if ( ssimt
->removeItem( oldItem
) )
920 ssimt
->go( Task::AutoDelete
);
927 void Client::changeContactGroup( const QString
& contact
, const QString
& newGroupName
)
929 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
933 kDebug(OSCAR_RAW_DEBUG
) << "Changing " << contact
<< "'s group to "
934 << newGroupName
<< endl
;
935 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
936 if ( ssimt
->changeGroup( contact
, newGroupName
) )
937 ssimt
->go( Task::AutoDelete
);
942 void Client::changeContactAlias( const QString
& contact
, const QString
& alias
)
944 Connection
* c
= d
->connections
.connectionForFamily( 0x0013 );
948 OContact item
= ssiManager()->findContact( contact
);
951 OContact
oldItem(item
);
953 if ( alias
.isEmpty() )
955 QList
<TLV
> tList( item
.tlvList() );
956 TLV tlv
= Oscar::findTLV( tList
, 0x0131 );
960 tList
.removeAll( tlv
);
961 item
.setTLVList( tList
);
967 QByteArray data
= alias
.toUtf8();
968 tList
.append( TLV( 0x0131, data
.size(), data
) );
970 if ( !Oscar::updateTLVs( item
, tList
) )
974 kDebug( OSCAR_RAW_DEBUG
) << "Changing " << contact
<< "'s alias to " << alias
<< endl
;
975 SSIModifyTask
* ssimt
= new SSIModifyTask( c
->rootTask() );
976 if ( ssimt
->modifyContact( oldItem
, item
) )
977 ssimt
->go( Task::AutoDelete
);
983 void Client::requestShortTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
985 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
989 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
990 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
991 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Short
);
992 d
->icqTlvInfoTask
->go();
995 void Client::requestMediumTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
997 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1001 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
1002 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
1003 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Medium
);
1004 d
->icqTlvInfoTask
->go();
1007 void Client::requestLongTlvInfo( const QString
& contactId
, const QByteArray
&metaInfoId
)
1009 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1013 d
->icqTlvInfoTask
->setUser( Oscar::normalize( contactId
) );
1014 d
->icqTlvInfoTask
->setMetaInfoId( metaInfoId
);
1015 d
->icqTlvInfoTask
->setType( ICQTlvInfoRequestTask::Long
);
1016 d
->icqTlvInfoTask
->go();
1019 void Client::requestFullInfo( const QString
& contactId
)
1021 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1024 d
->icqInfoTask
->setUser( contactId
);
1025 d
->icqInfoTask
->setType( ICQUserInfoRequestTask::Long
);
1026 d
->icqInfoTask
->go();
1029 void Client::requestShortInfo( const QString
& contactId
)
1031 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1034 d
->icqInfoTask
->setUser( contactId
);
1035 d
->icqInfoTask
->setType( ICQUserInfoRequestTask::Short
);
1036 d
->icqInfoTask
->go();
1039 void Client::sendWarning( const QString
& contact
, bool anonymous
)
1041 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1044 WarningTask
* warnTask
= new WarningTask( c
->rootTask() );
1045 warnTask
->setContact( contact
);
1046 warnTask
->setAnonymous( anonymous
);
1047 QObject::connect( warnTask
, SIGNAL( userWarned( const QString
&, quint16
, quint16
) ),
1048 this, SIGNAL( userWarned( const QString
&, quint16
, quint16
) ) );
1049 warnTask
->go( Task::AutoDelete
);
1052 bool Client::changeICQPassword( const QString
& password
)
1054 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1058 ICQChangePasswordTask
* task
= new ICQChangePasswordTask( c
->rootTask() );
1059 QObject::connect( task
, SIGNAL(finished()), this, SLOT(changeICQPasswordFinished()) );
1060 task
->setPassword( password
);
1061 task
->go( Task::AutoDelete
);
1065 void Client::changeICQPasswordFinished()
1067 ICQChangePasswordTask
* task
= (ICQChangePasswordTask
*)sender();
1068 if ( task
->success() )
1069 d
->pass
= task
->password();
1071 emit
icqPasswordChanged( !task
->success() );
1074 ICQFullInfo
Client::getFullInfo( const QString
& contact
)
1076 return d
->icqTlvInfoTask
->fullInfoFor( contact
);
1079 ICQGeneralUserInfo
Client::getGeneralInfo( const QString
& contact
)
1081 return d
->icqInfoTask
->generalInfoFor( contact
);
1084 ICQWorkUserInfo
Client::getWorkInfo( const QString
& contact
)
1086 return d
->icqInfoTask
->workInfoFor( contact
);
1089 ICQEmailInfo
Client::getEmailInfo( const QString
& contact
)
1091 return d
->icqInfoTask
->emailInfoFor( contact
);
1094 ICQNotesInfo
Client::getNotesInfo( const QString
& contact
)
1096 return d
->icqInfoTask
->notesInfoFor( contact
);
1099 ICQMoreUserInfo
Client::getMoreInfo( const QString
& contact
)
1101 return d
->icqInfoTask
->moreInfoFor( contact
);
1104 ICQInterestInfo
Client::getInterestInfo( const QString
& contact
)
1106 return d
->icqInfoTask
->interestInfoFor( contact
);
1109 ICQOrgAffInfo
Client::getOrgAffInfo( const QString
& contact
)
1111 return d
->icqInfoTask
->orgAffInfoFor( contact
);
1114 ICQShortInfo
Client::getShortInfo( const QString
& contact
)
1116 return d
->icqInfoTask
->shortInfoFor( contact
);
1119 QList
<int> Client::chatExchangeList() const
1121 return d
->exchanges
;
1124 void Client::setChatExchangeList( const QList
<int>& exchanges
)
1126 d
->exchanges
= exchanges
;
1129 void Client::requestAIMProfile( const QString
& contact
)
1131 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::Profile
);
1134 void Client::requestAIMAwayMessage( const QString
& contact
)
1136 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::AwayMessage
);
1139 void Client::requestICQAwayMessage( const QString
& contact
, ICQStatus contactStatus
)
1141 kDebug(OSCAR_RAW_DEBUG
) << "requesting away message for " << contact
;
1143 msg
.setChannel( 2 );
1144 msg
.setReceiver( contact
);
1146 if ( (contactStatus
& ICQXStatus
) == ICQXStatus
)
1148 Xtraz::XtrazNotify xNotify
;
1149 xNotify
.setSenderUni( userId() );
1151 msg
.setMessageType( Oscar::MessageType::Plugin
); // plugin message
1152 msg
.setPlugin( xNotify
.statusRequest() );
1154 else if ( (contactStatus
& ICQPluginStatus
) == ICQPluginStatus
)
1156 Oscar::WORD subTypeId
= 0xFFFF;
1157 QByteArray subTypeText
;
1159 switch ( contactStatus
& ICQStatusMask
)
1162 case ICQFreeForChat
:
1165 subTypeText
= "Away Status Message";
1168 case ICQDoNotDisturb
:
1170 subTypeText
= "Busy Status Message";
1172 case ICQNotAvailable
:
1174 subTypeText
= "N/A Status Message";
1177 // may be a good way to deal with possible error and lack of online status message?
1178 emit
receivedAwayMessage( contact
, "Sorry, this protocol does not support this type of status message" );
1182 Oscar::MessagePlugin
*plugin
= new Oscar::MessagePlugin();
1183 plugin
->setType( Oscar::MessagePlugin::StatusMsgExt
);
1184 plugin
->setSubTypeId( subTypeId
);
1185 plugin
->setSubTypeText( subTypeText
);
1188 buffer
.addLEDWord( 0x00000000 );
1189 //TODO: Change this to text/x-aolrtf
1190 buffer
.addLEDBlock( "text/plain" );
1191 plugin
->setData( buffer
.buffer() );
1193 msg
.setMessageType( Oscar::MessageType::Plugin
); // plugin message
1194 msg
.setPlugin( plugin
);
1198 msg
.addProperty( Oscar::Message::StatusMessageRequest
);
1199 switch ( contactStatus
& ICQStatusMask
)
1202 msg
.setMessageType( Oscar::MessageType::AutoAway
); // away
1205 msg
.setMessageType( Oscar::MessageType::AutoBusy
); // occupied
1207 case ICQNotAvailable
:
1208 msg
.setMessageType( Oscar::MessageType::AutoNA
); // not awailable
1210 case ICQDoNotDisturb
:
1211 msg
.setMessageType( Oscar::MessageType::AutoDND
); // do not disturb
1213 case ICQFreeForChat
:
1214 msg
.setMessageType( Oscar::MessageType::AutoFFC
); // free for chat
1217 // may be a good way to deal with possible error and lack of online status message?
1218 emit
receivedAwayMessage( contact
, "Sorry, this protocol does not support this type of status message" );
1225 void Client::addICQAwayMessageRequest( const QString
& contact
, ICQStatus contactStatus
)
1227 kDebug(OSCAR_RAW_DEBUG
) << "adding away message request for "
1228 << contact
<< " to queue" << endl
;
1230 //remove old request if still exists
1231 removeICQAwayMessageRequest( contact
);
1233 ClientPrivate::AwayMsgRequest amr
= { contact
, contactStatus
};
1234 d
->awayMsgRequestQueue
.prepend( amr
);
1236 if ( !d
->awayMsgRequestTimer
->isActive() )
1237 d
->awayMsgRequestTimer
->start( 1000 );
1240 void Client::removeICQAwayMessageRequest( const QString
& contact
)
1242 kDebug(OSCAR_RAW_DEBUG
) << "removing away message request for "
1243 << contact
<< " from queue" << endl
;
1245 QList
<ClientPrivate::AwayMsgRequest
>::iterator it
= d
->awayMsgRequestQueue
.begin();
1246 while ( it
!= d
->awayMsgRequestQueue
.end() )
1248 if ( (*it
).contact
== contact
)
1249 it
= d
->awayMsgRequestQueue
.erase( it
);
1255 void Client::nextICQAwayMessageRequest()
1257 kDebug(OSCAR_RAW_DEBUG
) << "request queue count " << d
->awayMsgRequestQueue
.count();
1259 if ( d
->awayMsgRequestQueue
.empty() )
1261 d
->awayMsgRequestTimer
->stop();
1266 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1270 SNAC s
= { 0x0004, 0x0006, 0x0000, 0x00000000 };
1271 //get time needed to restore level to initial
1272 //for some reason when we are long under initial level
1273 //icq server will start to block our messages
1274 int time
= c
->rateManager()->timeToInitialLevel( s
);
1277 d
->awayMsgRequestTimer
->start( time
);
1282 d
->awayMsgRequestTimer
->start( 5000 );
1286 ClientPrivate::AwayMsgRequest amr
;
1288 amr
= d
->awayMsgRequestQueue
.back();
1289 d
->awayMsgRequestQueue
.pop_back();
1290 requestICQAwayMessage( amr
.contact
, amr
.contactStatus
);
1293 void Client::requestStatusInfo( const QString
& contact
)
1295 d
->userInfoTask
->requestInfoFor( contact
, UserInfoTask::General
);
1298 void Client::whitePagesSearch( const ICQWPSearchInfo
& info
)
1300 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1303 UserSearchTask
* ust
= new UserSearchTask( c
->rootTask() );
1304 connect( ust
, SIGNAL( foundUser( const ICQSearchResult
& ) ),
1305 this, SIGNAL( gotSearchResults( const ICQSearchResult
& ) ) );
1306 connect( ust
, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
1307 ust
->go( Task::AutoDelete
); //onGo does nothing in this task. This is just here so autodelete works
1308 ust
->searchWhitePages( info
);
1311 void Client::uinSearch( const QString
& uin
)
1313 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1316 UserSearchTask
* ust
= new UserSearchTask( c
->rootTask() );
1317 connect( ust
, SIGNAL( foundUser( const ICQSearchResult
& ) ),
1318 this, SIGNAL( gotSearchResults( const ICQSearchResult
& ) ) );
1319 connect( ust
, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
1320 ust
->go( Task::AutoDelete
); //onGo does nothing in this task. This is just here so autodelete works
1321 ust
->searchUserByUIN( uin
);
1324 void Client::updateProfile( const QString
& profile
)
1326 Connection
* c
= d
->connections
.connectionForFamily( 0x0002 );
1329 ProfileTask
* pt
= new ProfileTask( c
->rootTask() );
1330 pt
->setProfileText( profile
);
1331 pt
->go( Task::AutoDelete
);
1334 bool Client::updateProfile( const QList
<ICQInfoBase
*>& infoList
)
1336 Connection
* c
= d
->connections
.connectionForFamily( 0x0015 );
1340 ICQUserInfoUpdateTask
* ui
= new ICQUserInfoUpdateTask( c
->rootTask() );
1341 ui
->setInfo( infoList
);
1342 ui
->go( Task::AutoDelete
);
1346 void Client::sendTyping( const QString
& contact
, bool typing
)
1348 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1351 d
->typingNotifyTask
->setParams( contact
, ( typing
? TypingNotifyTask::Begin
: TypingNotifyTask::Finished
) );
1352 d
->typingNotifyTask
->go(); // don't delete the task after sending
1355 void Client::connectToIconServer()
1357 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1361 requestServerRedirect( 0x0010 );
1365 void Client::setIgnore( const QString
& user
, bool ignore
)
1367 OContact item
= ssiManager()->findItem( user
, ROSTER_IGNORE
);
1368 if ( item
&& !ignore
)
1370 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from ignore list";
1371 this->modifyContactItem( item
, OContact() );
1373 else if ( !item
&& ignore
)
1375 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to ignore list";
1376 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_IGNORE
, QList
<TLV
>() );
1377 this->modifyContactItem( OContact(), s
);
1381 void Client::setVisibleTo( const QString
& user
, bool visible
)
1383 OContact item
= ssiManager()->findItem( user
, ROSTER_VISIBLE
);
1384 if ( item
&& !visible
)
1386 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from visible list";
1387 this->modifyContactItem( item
, OContact() );
1389 else if ( !item
&& visible
)
1391 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to visible list";
1392 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE
, QList
<TLV
>() );
1393 this->modifyContactItem( OContact(), s
);
1397 void Client::setInvisibleTo( const QString
& user
, bool invisible
)
1399 OContact item
= ssiManager()->findItem( user
, ROSTER_INVISIBLE
);
1400 if ( item
&& !invisible
)
1402 kDebug(OSCAR_RAW_DEBUG
) << "Removing " << user
<< " from invisible list";
1403 this->modifyContactItem( item
, OContact() );
1405 else if ( !item
&& invisible
)
1407 kDebug(OSCAR_RAW_DEBUG
) << "Adding " << user
<< " to invisible list";
1408 OContact
s( user
, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE
, QList
<TLV
>() );
1409 this->modifyContactItem( OContact(), s
);
1413 void Client::requestBuddyIcon( const QString
& user
, const QByteArray
& hash
, Oscar::BYTE hashType
)
1415 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1419 BuddyIconTask
* bit
= new BuddyIconTask( c
->rootTask() );
1420 connect( bit
, SIGNAL( haveIcon( const QString
&, QByteArray
) ),
1421 this, SIGNAL( haveIconForContact( const QString
&, QByteArray
) ) );
1422 bit
->requestIconFor( user
);
1423 bit
->setHashType( hashType
);
1424 bit
->setHash( hash
);
1425 bit
->go( Task::AutoDelete
);
1428 void Client::requestServerRedirect( Oscar::WORD family
, Oscar::WORD exchange
,
1429 QByteArray cookie
, Oscar::WORD instance
,
1430 const QString
& room
)
1432 //making the assumption that family 2 will always be the BOS connection
1433 //use it instead since we can't query for family 1
1434 Connection
* c
= d
->connections
.connectionForFamily( family
);
1435 if ( c
&& family
!= 0x000E )
1436 return; //we already have the connection
1438 c
= d
->connections
.connectionForFamily( 0x0002 );
1442 if ( d
->redirectionServices
.indexOf( family
) == -1 )
1443 d
->redirectionServices
.append( family
); //don't add families twice
1445 if ( d
->currentRedirect
!= 0 )
1446 return; //we're already doing one redirection
1448 d
->currentRedirect
= family
;
1450 //FIXME. this won't work if we have to defer the connection because we're
1451 //already connecting to something
1452 ServerRedirectTask
* srt
= new ServerRedirectTask( c
->rootTask() );
1453 if ( family
== 0x000E )
1455 srt
->setChatParams( exchange
, cookie
, instance
);
1456 srt
->setChatRoom( room
);
1459 connect( srt
, SIGNAL( haveServer( const QString
&, const QByteArray
&, Oscar::WORD
) ),
1460 this, SLOT( haveServerForRedirect( const QString
&, const QByteArray
&, Oscar::WORD
) ) );
1461 srt
->setService( family
);
1462 srt
->go( Task::AutoDelete
);
1465 void Client::haveServerForRedirect( const QString
& host
, const QByteArray
& cookie
, Oscar::WORD
)
1467 //nasty sender() usage to get the task with chat room info
1468 QObject
* o
= const_cast<QObject
*>( sender() );
1469 ServerRedirectTask
* srt
= dynamic_cast<ServerRedirectTask
*>( o
);
1471 //create a new connection and set it up
1472 int colonPos
= host
.indexOf(':');
1473 QString realHost
, realPort
;
1474 if ( colonPos
!= -1 )
1476 realHost
= host
.left( colonPos
);
1477 realPort
= host
.right(4); //we only need 4 bytes
1482 realPort
= QString::fromLatin1("5190");
1485 Connection
* c
= createConnection();
1486 //create the new login task
1487 m_loginTaskTwo
= new StageTwoLoginTask( c
->rootTask() );
1488 m_loginTaskTwo
->setCookie( cookie
);
1489 QObject::connect( m_loginTaskTwo
, SIGNAL( finished() ), this, SLOT( serverRedirectFinished() ) );
1492 connectToServer( c
, realHost
, realPort
.toInt(), false );
1493 QObject::connect( c
, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
1496 d
->connections
.addChatInfoForConnection( c
, srt
->chatExchange(), srt
->chatRoomName() );
1499 void Client::serverRedirectFinished()
1501 if ( m_loginTaskTwo
&& m_loginTaskTwo
->statusCode() == 0 )
1502 { //stage two was successful
1503 Connection
* c
= d
->connections
.connectionForFamily( d
->currentRedirect
);
1506 ClientReadyTask
* crt
= new ClientReadyTask( c
->rootTask() );
1507 crt
->setFamilies( c
->supportedFamilies() );
1508 crt
->go( Task::AutoDelete
);
1511 kDebug(OSCAR_RAW_DEBUG
) << "redirection finished for service "
1512 << d
->currentRedirect
<< endl
;
1514 if ( d
->currentRedirect
== 0x0010 )
1515 emit
iconServerConnected();
1517 if ( d
->currentRedirect
== 0x000D )
1519 connect( this, SIGNAL( chatNavigationConnected() ),
1520 this, SLOT( requestChatNavLimits() ) );
1521 emit
chatNavigationConnected();
1524 if ( d
->currentRedirect
== 0x000E )
1526 //HACK! such abuse! think of a better way
1527 if ( !m_loginTaskTwo
)
1529 kWarning(OSCAR_RAW_DEBUG
) << "no login task to get connection from!";
1530 emit
redirectionFinished( d
->currentRedirect
);
1534 Connection
* c
= m_loginTaskTwo
->client();
1535 QString roomName
= d
->connections
.chatRoomForConnection( c
);
1536 Oscar::WORD exchange
= d
->connections
.exchangeForConnection( c
);
1539 kDebug(OSCAR_RAW_DEBUG
) << "setting up chat connection";
1540 ChatServiceTask
* cst
= new ChatServiceTask( c
->rootTask(), exchange
, roomName
);
1541 connect( cst
, SIGNAL( userJoinedChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ),
1542 this, SIGNAL( userJoinedChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ) );
1543 connect( cst
, SIGNAL( userLeftChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ),
1544 this, SIGNAL( userLeftChat( Oscar::Oscar::WORD
, const QString
&, const QString
& ) ) );
1545 connect( cst
, SIGNAL( newChatMessage( const Oscar::Message
& ) ),
1546 this, SIGNAL( messageReceived( const Oscar::Message
& ) ) );
1548 emit
chatRoomConnected( exchange
, roomName
);
1551 emit
redirectionFinished( d
->currentRedirect
);
1555 void Client::checkRedirectionQueue( Oscar::WORD family
)
1557 kDebug(OSCAR_RAW_DEBUG
) << "checking redirection queue";
1558 d
->redirectionServices
.removeAll( family
);
1559 d
->currentRedirect
= 0;
1560 if ( !d
->redirectionServices
.isEmpty() )
1562 kDebug(OSCAR_RAW_DEBUG
) << "scheduling new redirection";
1563 requestServerRedirect( d
->redirectionServices
.front() );
1568 void Client::requestChatNavLimits()
1570 Connection
* c
= d
->connections
.connectionForFamily( 0x000D );
1574 kDebug(OSCAR_RAW_DEBUG
) << "requesting chat nav service limits";
1575 ChatNavServiceTask
* cnst
= new ChatNavServiceTask( c
->rootTask() );
1576 cnst
->setRequestType( ChatNavServiceTask::Limits
);
1577 QObject::connect( cnst
, SIGNAL( haveChatExchanges( const QList
<int>& ) ),
1578 this, SLOT( setChatExchangeList( const QList
<int>& ) ) );
1579 cnst
->go( Task::AutoDelete
); //autodelete
1583 void Client::determineDisconnection( int code
, const QString
& string
)
1588 //yay for the sender() hack!
1589 QObject
* obj
= const_cast<QObject
*>( sender() );
1590 Connection
* c
= dynamic_cast<Connection
*>( obj
);
1594 if ( c
->isSupported( 0x0002 ) ||
1595 d
->stage
== ClientPrivate::StageOne
) //emit on login
1597 emit
socketError( code
, string
);
1600 //connection is deleted. deleteLater() is used
1601 d
->connections
.remove( c
);
1605 void Client::sendBuddyIcon( const QByteArray
& iconData
)
1607 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1611 kDebug(OSCAR_RAW_DEBUG
) << "icon length is " << iconData
.size();
1612 BuddyIconTask
* bit
= new BuddyIconTask( c
->rootTask() );
1613 bit
->uploadIcon( iconData
.size(), iconData
);
1614 bit
->go( Task::AutoDelete
);
1617 void Client::joinChatRoom( const QString
& roomName
, int exchange
)
1619 Connection
* c
= d
->connections
.connectionForFamily( 0x000D );
1623 kDebug(OSCAR_RAW_DEBUG
) << "joining the chat room '" << roomName
1624 << "' on exchange " << exchange
<< endl
;
1625 ChatNavServiceTask
* cnst
= new ChatNavServiceTask( c
->rootTask() );
1626 connect( cnst
, SIGNAL( connectChat( Oscar::WORD
, QByteArray
, Oscar::WORD
, const QString
& ) ),
1627 this, SLOT( setupChatConnection( Oscar::WORD
, QByteArray
, Oscar::WORD
, const QString
& ) ) );
1628 cnst
->createRoom( exchange
, roomName
);
1632 void Client::setupChatConnection( Oscar::WORD exchange
, QByteArray cookie
, Oscar::WORD instance
, const QString
& room
)
1634 kDebug(OSCAR_RAW_DEBUG
) << "cookie is:" << cookie
;
1635 QByteArray
realCookie( cookie
);
1636 kDebug(OSCAR_RAW_DEBUG
) << "connection to chat room";
1637 requestServerRedirect( 0x000E, exchange
, realCookie
, instance
, room
);
1640 void Client::disconnectChatRoom( Oscar::WORD exchange
, const QString
& room
)
1642 Connection
* c
= d
->connections
.connectionForChatRoom( exchange
, room
);
1646 d
->connections
.remove( c
);
1650 Connection
* Client::createConnection()
1652 ClientStream
* cs
= new ClientStream( new QTcpSocket(), 0 );
1653 cs
->setNoopTime( 60000 );
1654 Connection
* c
= new Connection( cs
, "BOS" );
1655 cs
->setConnection( c
);
1656 c
->setClient( this );
1660 void Client::deleteStaticTasks()
1662 delete d
->errorTask
;
1663 delete d
->onlineNotifier
;
1664 delete d
->ownStatusTask
;
1665 delete d
->messageReceiverTask
;
1666 delete d
->ssiAuthTask
;
1667 delete d
->icqInfoTask
;
1668 delete d
->icqTlvInfoTask
;
1669 delete d
->userInfoTask
;
1670 delete d
->typingNotifyTask
;
1671 delete d
->ssiModifyTask
;
1674 d
->onlineNotifier
= 0;
1675 d
->ownStatusTask
= 0;
1676 d
->messageReceiverTask
= 0;
1679 d
->icqTlvInfoTask
= 0;
1680 d
->userInfoTask
= 0;
1681 d
->typingNotifyTask
= 0;
1682 d
->ssiModifyTask
= 0;
1685 bool Client::hasIconConnection( ) const
1687 Connection
* c
= d
->connections
.connectionForFamily( 0x0010 );
1691 void Client::sendFiles( const QString
& contact
, const QStringList
& files
, Kopete::Transfer
*t
)
1693 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1697 FileTransferTask
*ft
= new FileTransferTask( c
->rootTask(), contact
, ourInfo().userId(), files
, t
);
1698 connect( ft
, SIGNAL( sendMessage( const Oscar::Message
& ) ),
1699 this, SLOT( fileMessage( const Oscar::Message
& ) ) );
1700 ft
->go( Task::AutoDelete
);
1703 void Client::gotFileMessage( int type
, const QString from
, const QByteArray cookie
, Buffer buf
)
1705 Connection
* c
= d
->connections
.connectionForFamily( 0x0004 );
1708 //pass the message to the matching task if we can
1709 const QList
<FileTransferTask
*> p
= c
->rootTask()->findChildren
<FileTransferTask
*>();
1710 foreach( FileTransferTask
*t
, p
)
1712 if ( t
->take( type
, cookie
, buf
) )
1717 //maybe it's a new request!
1720 kDebug(14151) << "new request :)";
1721 FileTransferTask
*ft
= new FileTransferTask( c
->rootTask(), from
, ourInfo().userId(), cookie
, buf
);
1722 connect( ft
, SIGNAL( getTransferManager( Kopete::TransferManager
** ) ),
1723 SIGNAL( getTransferManager( Kopete::TransferManager
** ) ) );
1724 connect( ft
, SIGNAL( askIncoming( QString
, QString
, Oscar::DWORD
, QString
, QString
) ),
1725 SIGNAL( askIncoming( QString
, QString
, Oscar::DWORD
, QString
, QString
) ) );
1726 connect( ft
, SIGNAL( sendMessage( const Oscar::Message
& ) ),
1727 this, SLOT( fileMessage( const Oscar::Message
& ) ) );
1728 ft
->go( Task::AutoDelete
);
1732 kDebug(14151) << "nobody wants it :(";
1735 #include "client.moc"
1736 //kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;