compile with old gcc
[kdenetwork.git] / kopete / protocols / oscar / liboscar / client.cpp
blob47a7da4a5fba910fe8d3fd23c1f648a04820e05f
1 /*
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 *************************************************************************
13 * *
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. *
18 * *
19 *************************************************************************
22 #include "client.h"
24 #include <qtimer.h>
25 #include <QList>
26 #include <QByteArray>
27 #include <qtextcodec.h>
28 #include <QtNetwork/QTcpSocket>
30 #include <kdebug.h> //for kDebug()
31 #include <klocale.h>
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"
59 #include "task.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"
76 namespace
78 class DefaultCodecProvider : public Client::CodecProvider
80 public:
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
96 public:
97 ClientPrivate() {}
99 QString host, user, pass;
100 uint port;
101 int tzoffset;
102 bool active;
104 enum { StageOne, StageTwo };
105 int stage;
107 //Protocol specific data
108 bool isIcq;
109 bool redirectRequested;
110 QList<Oscar::WORD> redirectionServices;
111 Oscar::WORD currentRedirect;
112 QByteArray cookie;
113 Oscar::Settings* settings;
115 //Tasks
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;
126 //Managers
127 ContactManager* ssiManager;
128 ConnectionHandler connections;
130 //Our Userinfo
131 UserDetails ourDetails;
133 //Infos
134 QList<int> exchanges;
136 struct Status
138 Oscar::DWORD status;
139 QString message; // for away-,DND-message etc., and for Xtraz status
140 int xtraz; // Xtraz status
141 QString description; // Xtraz description
142 bool sent;
143 } status;
145 //away messages
146 struct AwayMsgRequest
148 QString contact;
149 ICQStatus contactStatus;
151 QList<AwayMsgRequest> awayMsgRequestQueue;
152 QTimer* awayMsgRequestTimer;
153 CodecProvider* codecProvider;
155 const Oscar::ClientVersion* version;
156 Guid versionCap;
159 Client::Client( QObject* parent )
160 :QObject( parent )
162 setObjectName( "oscarclient" );
163 m_loginTask = 0L;
164 m_loginTaskTwo = 0L;
166 d = new ClientPrivate;
167 d->tzoffset = 0;
168 d->active = false;
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();
177 d->errorTask = 0L;
178 d->onlineNotifier = 0L;
179 d->ownStatusTask = 0L;
180 d->messageReceiverTask = 0L;
181 d->ssiAuthTask = 0L;
182 d->icqInfoTask = 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() ) );
197 Client::~Client()
200 //delete the connections differently than in deleteConnections()
201 //deleteLater() seems to cause destruction order issues
202 deleteStaticTasks();
203 delete d->settings;
204 delete d->ssiManager;
205 delete d->awayMsgRequestTimer;
206 delete d;
209 Oscar::Settings* Client::clientSettings() const
211 return d->settings;
214 void Client::connectToServer( Connection *c, const QString& host, quint16 port, bool auth )
216 d->connections.append( c );
217 if ( auth == true )
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 )
229 Q_UNUSED( host );
230 Q_UNUSED( port );
231 d->user = userId;
232 d->pass = pass;
233 d->stage = ClientPrivate::StageOne;
234 d->active = false;
237 void Client::close()
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 );
243 d->active = false;
244 d->awayMsgRequestTimer->stop();
245 d->awayMsgRequestQueue.clear();
246 d->connections.clear();
247 deleteStaticTasks();
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;
279 if ( d->active )
281 if ( d->isIcq )
283 //the first connection is always the BOS connection
284 Connection* c = d->connections.connectionForFamily( 0x0013 );
285 if ( !c )
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 );
294 else
296 kDebug(OSCAR_RAW_DEBUG) << "Setting visible";
297 cvt->setVisible( true );
299 cvt->go( Task::AutoDelete );
302 Connection* c = d->connections.connectionForFamily( 0x0002 );
303 if ( !c )
304 return;
306 SendDCInfoTask* sdcit = new SendDCInfoTask( c->rootTask(), status );
307 sdcit->go( Task::AutoDelete ); //autodelete
309 QString msg;
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("");
317 else
319 if ( message.isEmpty() )
320 msg = QString::fromAscii(" ");
321 else
322 msg = message;
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()
353 return d->host;
356 int Client::port()
358 return d->port;
361 ContactManager* Client::ssiManager() const
363 return d->ssiManager;
366 const Oscar::ClientVersion* Client::version() const
368 return d->version;
371 Guid Client::versionCap() const
373 return d->versionCap;
376 // SLOTS //
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();
400 m_loginTaskTwo = 0;
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();
415 close();
416 QTimer::singleShot( 100, this, SLOT(startStageTwo() ) );
418 else
420 kDebug(OSCAR_RAW_DEBUG) << "errors reported. not moving to stage two";
421 close(); //deletes the connections for us
424 m_loginTask->deleteLater();
425 m_loginTask = 0;
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() ) );
442 //connect
443 QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
444 connectToServer( c, d->host, d->port, false ) ;
448 void Client::serviceSetupFinished()
450 d->active = true;
452 setStatus( d->status.status, d->status.message, d->status.xtraz, d->status.description );
453 d->ownStatusTask->go();
455 if ( isIcq() )
457 //retrieve offline messages
458 Connection* c = d->connections.connectionForFamily( 0x0015 );
459 if ( !c )
460 return;
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();
469 emit loggedIn();
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 );
479 else
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();
498 d->ourDetails = ud;
499 emit haveOwnInfo();
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 );
517 // INTERNALS //
519 QString Client::userId() const
521 return d->user;
524 QString Client::password() const
526 return d->pass;
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
552 return "127.0.0.1";
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)
567 Connection* c = 0L;
568 if ( msg.channel() == 0x0003 )
570 c = d->connections.connectionForChatRoom( msg.exchange(), msg.chatRoom() );
571 if ( !c )
572 return;
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 );
580 else
582 c = d->connections.connectionForFamily( 0x0004 );
583 if ( !c )
584 return;
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 );
599 if ( !c )
600 return;
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;
618 XtrazNotify xNotify;
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 )
634 Buffer buffer;
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() );
645 else
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;
677 XtrazNotify xNotify;
678 xNotify.handle( msg.plugin() );
679 if ( xNotify.type() == XtrazNotify::Response )
681 const Xtraz::XAwayService* service = dynamic_cast<const XAwayService*>(xNotify.findService( "cAwaySrv" ));
682 if ( service )
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 );
700 else
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() )
710 return;
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 );
722 if ( !c )
723 return;
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 );
739 if ( !c )
740 return;
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 );
747 if ( !c )
748 return;
749 d->ssiAuthTask->sendAuthReply( contactid, reason, auth );
752 bool Client::isActive() const
754 return d->active;
757 bool Client::isIcq() const
759 return d->isIcq;
762 void Client::setIsIcq( bool isIcq )
764 d->isIcq = isIcq;
767 void Client::debug( const QString& str )
769 Q_UNUSED(str);
770 // qDebug( "CLIENT: %s", str.toAscii() );
773 void Client::initializeStaticTasks()
775 //set up the extra tasks
776 Connection* c = d->connections.defaultConnection();
777 if ( !c )
778 return;
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 );
827 if ( !c )
828 return;
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 );
834 else
835 delete ssimt;
838 void Client::addGroup( const QString& groupName )
840 Connection* c = d->connections.connectionForFamily( 0x0013 );
841 if ( !c )
842 return;
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 );
848 else
849 delete ssimt;
852 void Client::addContact( const QString& contactName, const QString& groupName )
854 Connection* c = d->connections.connectionForFamily( 0x0013 );
855 if ( !c )
856 return;
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 );
862 else
863 delete ssimt;
866 void Client::removeContact( const QString& contactName )
868 Connection* c = d->connections.connectionForFamily( 0x0013 );
869 if ( !c )
870 return;
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 );
876 else
877 delete ssimt;
880 void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName )
882 Connection* c = d->connections.connectionForFamily( 0x0013 );
883 if ( !c )
884 return;
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 );
890 else
891 delete ssimt;
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 );
898 if ( !c )
899 return;
901 if ( !oldItem && newItem )
902 action = 1;
903 if ( oldItem && !newItem )
904 action = 2;
906 kDebug(OSCAR_RAW_DEBUG) << "Add/Mod/Del item on server";
907 SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
908 switch ( action )
910 case 0:
911 if ( ssimt->modifyItem( oldItem, newItem ) )
912 ssimt->go( Task::AutoDelete );
913 else
914 delete ssimt;
915 break;
916 case 1:
917 if ( ssimt->addItem( newItem ) )
918 ssimt->go( Task::AutoDelete );
919 else
920 delete ssimt;
921 break;
922 case 2:
923 if ( ssimt->removeItem( oldItem ) )
924 ssimt->go( Task::AutoDelete );
925 else
926 delete ssimt;
927 break;
931 void Client::changeContactGroup( const QString& contact, const QString& newGroupName )
933 Connection* c = d->connections.connectionForFamily( 0x0013 );
934 if ( !c )
935 return;
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 );
942 else
943 delete ssimt;
946 void Client::changeContactAlias( const QString& contact, const QString& alias )
948 Connection* c = d->connections.connectionForFamily( 0x0013 );
949 if ( !c )
950 return;
952 OContact item = ssiManager()->findContact( contact );
953 if ( item )
955 OContact oldItem(item);
957 if ( alias.isEmpty() )
959 QList<TLV> tList( item.tlvList() );
960 TLV tlv = Oscar::findTLV( tList, 0x0131 );
961 if ( !tlv )
962 return;
964 tList.removeAll( tlv );
965 item.setTLVList( tList );
967 else
969 QList<TLV> tList;
971 QByteArray data = alias.toUtf8();
972 tList.append( TLV( 0x0131, data.size(), data ) );
974 if ( !Oscar::updateTLVs( item, tList ) )
975 return;
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 );
982 else
983 delete ssimt;
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 ) );
995 if ( !item )
997 kDebug( OSCAR_RAW_DEBUG ) << "Adding new privacy TLV item";
998 QString empty;
999 OContact s( empty, 0, ssiManager()->nextContactId(), ROSTER_VISIBILITY, tList );
1000 modifyContactItem( item, s );
1002 else
1003 { //found an item
1004 OContact s(item);
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 );
1017 if ( !c )
1018 return;
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 );
1029 if ( !c )
1030 return;
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 );
1041 if ( !c )
1042 return;
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 );
1053 if ( !c )
1054 return;
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 );
1063 if ( !c )
1064 return;
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 );
1073 if ( !c )
1074 return;
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 );
1086 if ( !c )
1087 return false;
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 );
1093 return true;
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;
1173 Oscar::Message msg;
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 )
1192 case ICQOnline:
1193 case ICQFreeForChat:
1194 case ICQAway:
1195 subTypeId = 1;
1196 subTypeText = "Away Status Message";
1197 break;
1198 case ICQOccupied:
1199 case ICQDoNotDisturb:
1200 subTypeId = 2;
1201 subTypeText = "Busy Status Message";
1202 break;
1203 case ICQNotAvailable:
1204 subTypeId = 3;
1205 subTypeText = "N/A Status Message";
1206 break;
1207 default:
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" );
1210 return;
1213 Oscar::MessagePlugin *plugin = new Oscar::MessagePlugin();
1214 plugin->setType( Oscar::MessagePlugin::StatusMsgExt );
1215 plugin->setSubTypeId( subTypeId );
1216 plugin->setSubTypeText( subTypeText );
1218 Buffer buffer;
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 );
1227 else
1229 msg.addProperty( Oscar::Message::StatusMessageRequest );
1230 switch ( contactStatus & ICQStatusMask )
1232 case ICQAway:
1233 msg.setMessageType( Oscar::MessageType::AutoAway ); // away
1234 break;
1235 case ICQOccupied:
1236 msg.setMessageType( Oscar::MessageType::AutoBusy ); // occupied
1237 break;
1238 case ICQNotAvailable:
1239 msg.setMessageType( Oscar::MessageType::AutoNA ); // not awailable
1240 break;
1241 case ICQDoNotDisturb:
1242 msg.setMessageType( Oscar::MessageType::AutoDND ); // do not disturb
1243 break;
1244 case ICQFreeForChat:
1245 msg.setMessageType( Oscar::MessageType::AutoFFC ); // free for chat
1246 break;
1247 default:
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" );
1250 return;
1253 sendMessage( msg );
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 );
1281 else
1282 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();
1293 return;
1295 else
1297 Connection* c = d->connections.connectionForFamily( 0x0004 );
1298 if ( !c )
1299 return;
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 );
1306 if ( time > 0 )
1308 d->awayMsgRequestTimer->start( time );
1309 return;
1311 else
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 );
1332 if ( !c )
1333 return;
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 );
1345 if ( !c )
1346 return;
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 );
1358 if ( !c )
1359 return;
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 );
1368 if ( !c )
1369 return false;
1371 ICQUserInfoUpdateTask* ui = new ICQUserInfoUpdateTask( c->rootTask() );
1372 ui->setInfo( infoList );
1373 ui->go( Task::AutoDelete );
1374 return true;
1377 void Client::sendTyping( const QString & contact, bool typing )
1379 Connection* c = d->connections.connectionForFamily( 0x0004 );
1380 if ( !c )
1381 return;
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 );
1389 if ( c )
1390 return;
1392 requestServerRedirect( 0x0010 );
1393 return;
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 );
1447 if ( !c )
1448 return;
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 );
1471 if ( !c )
1472 return;
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
1511 else
1513 realHost = host;
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() ) );
1523 //connect
1524 connectToServer( c, realHost, realPort.toInt(), false );
1525 QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
1527 if ( srt )
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 );
1536 if ( !c )
1537 return;
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 );
1563 return;
1566 Connection* c = m_loginTaskTwo->client();
1567 QString roomName = d->connections.chatRoomForConnection( c );
1568 Oscar::WORD exchange = d->connections.exchangeForConnection( c );
1569 if ( 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 );
1603 if ( !c )
1604 return;
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 )
1617 if ( !sender() )
1618 return;
1620 //yay for the sender() hack!
1621 QObject* obj = const_cast<QObject*>( sender() );
1622 Connection* c = dynamic_cast<Connection*>( obj );
1623 if ( !c )
1624 return;
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 );
1634 c = 0;
1637 void Client::sendBuddyIcon( const QByteArray& iconData )
1639 Connection* c = d->connections.connectionForFamily( 0x0010 );
1640 if ( !c )
1641 return;
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 );
1652 if ( !c )
1653 return;
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 );
1675 if ( !c )
1676 return;
1678 d->connections.remove( c );
1679 c = 0;
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 );
1689 return c;
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;
1705 d->errorTask = 0;
1706 d->onlineNotifier = 0;
1707 d->ownStatusTask = 0;
1708 d->messageReceiverTask = 0;
1709 d->ssiAuthTask = 0;
1710 d->icqInfoTask = 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 );
1720 return c;
1723 void Client::sendFiles( const QString& contact, const QStringList& files, Kopete::Transfer *t )
1725 Connection* c = d->connections.connectionForFamily( 0x0004 );
1726 if ( !c )
1727 return;
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 );
1738 if ( !c )
1739 return;
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 ) )
1746 return;
1749 //maybe it's a new request!
1750 if ( type == 0 )
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 );
1761 return;
1764 kDebug(14151) << "nobody wants it :(";
1767 #include "client.moc"
1768 //kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;