Allow user to change server side aliases.
[kdenetwork.git] / kopete / protocols / oscar / liboscar / client.cpp
blob2e93f5f588a7afa3968ff081d722c83c7df884ae
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 // 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 );
718 if ( !c )
719 return;
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 );
735 if ( !c )
736 return;
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 );
743 if ( !c )
744 return;
745 d->ssiAuthTask->sendAuthReply( contactid, reason, auth );
748 bool Client::isActive() const
750 return d->active;
753 bool Client::isIcq() const
755 return d->isIcq;
758 void Client::setIsIcq( bool isIcq )
760 d->isIcq = isIcq;
763 void Client::debug( const QString& str )
765 Q_UNUSED(str);
766 // qDebug( "CLIENT: %s", str.toAscii() );
769 void Client::initializeStaticTasks()
771 //set up the extra tasks
772 Connection* c = d->connections.defaultConnection();
773 if ( !c )
774 return;
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 );
823 if ( !c )
824 return;
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 );
830 else
831 delete ssimt;
834 void Client::addGroup( const QString& groupName )
836 Connection* c = d->connections.connectionForFamily( 0x0013 );
837 if ( !c )
838 return;
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 );
844 else
845 delete ssimt;
848 void Client::addContact( const QString& contactName, const QString& groupName )
850 Connection* c = d->connections.connectionForFamily( 0x0013 );
851 if ( !c )
852 return;
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 );
858 else
859 delete ssimt;
862 void Client::removeContact( const QString& contactName )
864 Connection* c = d->connections.connectionForFamily( 0x0013 );
865 if ( !c )
866 return;
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 );
872 else
873 delete ssimt;
876 void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName )
878 Connection* c = d->connections.connectionForFamily( 0x0013 );
879 if ( !c )
880 return;
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 );
886 else
887 delete ssimt;
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 );
894 if ( !c )
895 return;
897 if ( !oldItem && newItem )
898 action = 1;
899 if ( oldItem && !newItem )
900 action = 2;
902 kDebug(OSCAR_RAW_DEBUG) << "Add/Mod/Del item on server";
903 SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
904 switch ( action )
906 case 0:
907 if ( ssimt->modifyItem( oldItem, newItem ) )
908 ssimt->go( Task::AutoDelete );
909 else
910 delete ssimt;
911 break;
912 case 1:
913 if ( ssimt->addItem( newItem ) )
914 ssimt->go( Task::AutoDelete );
915 else
916 delete ssimt;
917 break;
918 case 2:
919 if ( ssimt->removeItem( oldItem ) )
920 ssimt->go( Task::AutoDelete );
921 else
922 delete ssimt;
923 break;
927 void Client::changeContactGroup( const QString& contact, const QString& newGroupName )
929 Connection* c = d->connections.connectionForFamily( 0x0013 );
930 if ( !c )
931 return;
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 );
938 else
939 delete ssimt;
942 void Client::changeContactAlias( const QString& contact, const QString& alias )
944 Connection* c = d->connections.connectionForFamily( 0x0013 );
945 if ( !c )
946 return;
948 OContact item = ssiManager()->findContact( contact );
949 if ( item )
951 OContact oldItem(item);
953 if ( alias.isEmpty() )
955 QList<TLV> tList( item.tlvList() );
956 TLV tlv = Oscar::findTLV( tList, 0x0131 );
957 if ( !tlv )
958 return;
960 tList.removeAll( tlv );
961 item.setTLVList( tList );
963 else
965 QList<TLV> tList;
967 QByteArray data = alias.toUtf8();
968 tList.append( TLV( 0x0131, data.size(), data ) );
970 if ( !Oscar::updateTLVs( item, tList ) )
971 return;
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 );
978 else
979 delete ssimt;
983 void Client::requestShortTlvInfo( const QString& contactId, const QByteArray &metaInfoId )
985 Connection* c = d->connections.connectionForFamily( 0x0015 );
986 if ( !c )
987 return;
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 );
998 if ( !c )
999 return;
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 );
1010 if ( !c )
1011 return;
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 );
1022 if ( !c )
1023 return;
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 );
1032 if ( !c )
1033 return;
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 );
1042 if ( !c )
1043 return;
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 );
1055 if ( !c )
1056 return false;
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 );
1062 return true;
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;
1142 Oscar::Message msg;
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 )
1161 case ICQOnline:
1162 case ICQFreeForChat:
1163 case ICQAway:
1164 subTypeId = 1;
1165 subTypeText = "Away Status Message";
1166 break;
1167 case ICQOccupied:
1168 case ICQDoNotDisturb:
1169 subTypeId = 2;
1170 subTypeText = "Busy Status Message";
1171 break;
1172 case ICQNotAvailable:
1173 subTypeId = 3;
1174 subTypeText = "N/A Status Message";
1175 break;
1176 default:
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" );
1179 return;
1182 Oscar::MessagePlugin *plugin = new Oscar::MessagePlugin();
1183 plugin->setType( Oscar::MessagePlugin::StatusMsgExt );
1184 plugin->setSubTypeId( subTypeId );
1185 plugin->setSubTypeText( subTypeText );
1187 Buffer buffer;
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 );
1196 else
1198 msg.addProperty( Oscar::Message::StatusMessageRequest );
1199 switch ( contactStatus & ICQStatusMask )
1201 case ICQAway:
1202 msg.setMessageType( Oscar::MessageType::AutoAway ); // away
1203 break;
1204 case ICQOccupied:
1205 msg.setMessageType( Oscar::MessageType::AutoBusy ); // occupied
1206 break;
1207 case ICQNotAvailable:
1208 msg.setMessageType( Oscar::MessageType::AutoNA ); // not awailable
1209 break;
1210 case ICQDoNotDisturb:
1211 msg.setMessageType( Oscar::MessageType::AutoDND ); // do not disturb
1212 break;
1213 case ICQFreeForChat:
1214 msg.setMessageType( Oscar::MessageType::AutoFFC ); // free for chat
1215 break;
1216 default:
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" );
1219 return;
1222 sendMessage( msg );
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 );
1250 else
1251 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();
1262 return;
1264 else
1266 Connection* c = d->connections.connectionForFamily( 0x0004 );
1267 if ( !c )
1268 return;
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 );
1275 if ( time > 0 )
1277 d->awayMsgRequestTimer->start( time );
1278 return;
1280 else
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 );
1301 if ( !c )
1302 return;
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 );
1314 if ( !c )
1315 return;
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 );
1327 if ( !c )
1328 return;
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 );
1337 if ( !c )
1338 return false;
1340 ICQUserInfoUpdateTask* ui = new ICQUserInfoUpdateTask( c->rootTask() );
1341 ui->setInfo( infoList );
1342 ui->go( Task::AutoDelete );
1343 return true;
1346 void Client::sendTyping( const QString & contact, bool typing )
1348 Connection* c = d->connections.connectionForFamily( 0x0004 );
1349 if ( !c )
1350 return;
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 );
1358 if ( c )
1359 return;
1361 requestServerRedirect( 0x0010 );
1362 return;
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 );
1416 if ( !c )
1417 return;
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 );
1439 if ( !c )
1440 return;
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
1479 else
1481 realHost = host;
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() ) );
1491 //connect
1492 connectToServer( c, realHost, realPort.toInt(), false );
1493 QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
1495 if ( srt )
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 );
1504 if ( !c )
1505 return;
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 );
1531 return;
1534 Connection* c = m_loginTaskTwo->client();
1535 QString roomName = d->connections.chatRoomForConnection( c );
1536 Oscar::WORD exchange = d->connections.exchangeForConnection( c );
1537 if ( 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 );
1571 if ( !c )
1572 return;
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 )
1585 if ( !sender() )
1586 return;
1588 //yay for the sender() hack!
1589 QObject* obj = const_cast<QObject*>( sender() );
1590 Connection* c = dynamic_cast<Connection*>( obj );
1591 if ( !c )
1592 return;
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 );
1602 c = 0;
1605 void Client::sendBuddyIcon( const QByteArray& iconData )
1607 Connection* c = d->connections.connectionForFamily( 0x0010 );
1608 if ( !c )
1609 return;
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 );
1620 if ( !c )
1621 return;
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 );
1643 if ( !c )
1644 return;
1646 d->connections.remove( c );
1647 c = 0;
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 );
1657 return c;
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;
1673 d->errorTask = 0;
1674 d->onlineNotifier = 0;
1675 d->ownStatusTask = 0;
1676 d->messageReceiverTask = 0;
1677 d->ssiAuthTask = 0;
1678 d->icqInfoTask = 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 );
1688 return c;
1691 void Client::sendFiles( const QString& contact, const QStringList& files, Kopete::Transfer *t )
1693 Connection* c = d->connections.connectionForFamily( 0x0004 );
1694 if ( !c )
1695 return;
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 );
1706 if ( !c )
1707 return;
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 ) )
1714 return;
1717 //maybe it's a new request!
1718 if ( type == 0 )
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 );
1729 return;
1732 kDebug(14151) << "nobody wants it :(";
1735 #include "client.moc"
1736 //kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;