1 /*
2 	client.cpp - Kopete Oscar Protocol
3 
4 	Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
5     Copyright (c) 2008 Roman Jarosz <kedgedev@centrum.cz>
6 
7 	Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
8 	Based on Iris, Copyright (C) 2003  Justin Karneges <justin@affinix.com>
9 
10 	Kopete (c) 2002-2008 by the Kopete developers <kopete-devel@kde.org>
11 
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 	*************************************************************************
20 */
21 
22 #include "client.h"
23 
24 #include <QTimer>
25 #include <QList>
26 #include <QByteArray>
27 #include <QPointer>
28 #include <QTextCodec>
29 #include <QSslSocket>
30 
31 #include <kdebug.h> //for qDebug()
32 #include <KLocalizedString>
33 
34 #include "filetransfertask.h"
35 #include "buddyicontask.h"
36 #include "clientreadytask.h"
37 #include "connectionhandler.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 "messageacktask.h"
46 #include "onlinenotifiertask.h"
47 #include "oscarclientstream.h"
48 #include "oscarsettings.h"
49 #include "oscarutils.h"
50 #include "ownuserinfotask.h"
51 #include "profiletask.h"
52 #include "senddcinfotask.h"
53 #include "sendmessagetask.h"
54 #include "serverredirecttask.h"
55 #include "servicesetuptask.h"
56 #include "contactmanager.h"
57 #include "ssimodifytask.h"
58 #include "ssiauthtask.h"
59 #include "offlinemessagestask.h"
60 #include "task.h"
61 #include "typingnotifytask.h"
62 #include "userinfotask.h"
63 #include "usersearchtask.h"
64 #include "warningtask.h"
65 #include "chatservicetask.h"
66 #include "rateclassmanager.h"
67 #include "icquserinfoupdatetask.h"
68 #include "icqchangepasswordtask.h"
69 #include "oscarmessageplugin.h"
70 #include "xtrazxtraznotify.h"
71 #include "xtrazxawayservice.h"
72 #include "closeconnectiontask.h"
73 #include "icqtlvinforequesttask.h"
74 #include "icqtlvinfoupdatetask.h"
75 #include "filetransferhandler.h"
76 #include "chatroomtask.h"
77 #include "chatroomhandler.h"
78 
79 namespace
80 {
81 	class DefaultCodecProvider : public Client::CodecProvider
82 	{
83 	public:
codecForContact(const QString &) const84 		QTextCodec* codecForContact( const QString& ) const Q_DECL_OVERRIDE
85 		{
86 			return QTextCodec::codecForMib( 4 );
87 		}
codecForAccount() const88 		QTextCodec* codecForAccount() const Q_DECL_OVERRIDE
89 		{
90 			return QTextCodec::codecForMib( 4 );
91 		}
92 	};
93 
94 	DefaultCodecProvider defaultCodecProvider;
95 }
96 
97 namespace Oscar {
98 
99 class Client::ClientPrivate
100 {
101 public:
ClientPrivate()102 	ClientPrivate() {}
103 
104 	QString host, user, pass;
105 	uint port;
106 	bool encrypted;
107 	bool encrypted2;
108 	QString SSLName;
109 	int tzoffset;
110 	bool active;
111 
112 	enum { StageOne, StageTwo };
113 	int stage;
114 
115 	StageOneLoginTask* loginTask;
116 	QPointer<StageTwoLoginTask> loginTaskTwo;
117 
118 	//Protocol specific data
119 	bool isIcq;
120 	bool redirectRequested;
121 	QList<Oscar::WORD> redirectionServices;
122 	Oscar::WORD currentRedirect;
123 	bool offlineMessagesRequested;
124 	QByteArray cookie;
125 	Oscar::Settings* settings;
126 
127 	//Tasks
128 	ErrorTask* errorTask;
129 	OnlineNotifierTask* onlineNotifier;
130 	OwnUserInfoTask* ownStatusTask;
131 	MessageReceiverTask* messageReceiverTask;
132 	MessageAckTask* messageAckTask;
133 	SSIAuthTask* ssiAuthTask;
134 	ICQUserInfoRequestTask* icqInfoTask;
135 	ICQTlvInfoRequestTask* icqTlvInfoTask;
136 	UserInfoTask* userInfoTask;
137 	TypingNotifyTask * typingNotifyTask;
138 	SSIModifyTask* ssiModifyTask;
139 	//Managers
140 	ContactManager* ssiManager;
141 	ConnectionHandler connections;
142 
143 	//Our Userinfo
144 	UserDetails ourDetails;
145 
146     //Infos
147     QList<int> exchanges;
148 
149 	struct Status
150 	{
151 		Oscar::DWORD status;
152 		QString message;     // for away-,DND-message etc., and for Xtraz status
153 		int xtraz;           // Xtraz status
154 		int mood;            // Mood
155 		QString title;       // Xtraz/Mood title
156 		bool sent;
157 	} status;
158 
159 	//away messages
160 	struct AwayMsgRequest
161 	{
162 		QString contact;
163 		ICQStatus contactStatus;
164 	};
165 	QList<AwayMsgRequest> awayMsgRequestQueue;
166 	QTimer* awayMsgRequestTimer;
167 	CodecProvider* codecProvider;
168 
169 	const Oscar::ClientVersion* version;
170 	Guid versionCap;
171 };
172 
Client(QObject * parent)173 Client::Client( QObject* parent )
174 :QObject( parent )
175 {
176 	setObjectName( QStringLiteral("oscarclient") );
177 
178 	d = new ClientPrivate;
179 	d->tzoffset = 0;
180 	d->active = false;
181 	d->isIcq = false; //default to AIM
182 	d->redirectRequested = false;
183 	d->currentRedirect = 0;
184 	d->offlineMessagesRequested = false;
185 	d->status.status = 0x0; // default to online
186 	d->status.xtraz = -1; // default to no Xtraz
187 	d->status.mood = -1;
188 	d->status.sent = false;
189 	d->ssiManager = new ContactManager( this );
190 	d->settings = new Oscar::Settings();
191 	d->errorTask = nullptr;
192 	d->onlineNotifier = nullptr;
193 	d->ownStatusTask = nullptr;
194 	d->messageReceiverTask = nullptr;
195 	d->messageAckTask = nullptr;
196 	d->ssiAuthTask = nullptr;
197 	d->icqInfoTask = nullptr;
198 	d->icqTlvInfoTask = nullptr;
199 	d->userInfoTask = nullptr;
200 	d->stage = ClientPrivate::StageOne;
201 	d->loginTask = nullptr;
202 	d->loginTaskTwo = nullptr;
203 	d->typingNotifyTask = nullptr;
204 	d->ssiModifyTask = nullptr;
205 	d->awayMsgRequestTimer = new QTimer();
206 	d->codecProvider = &defaultCodecProvider;
207 
208 	connect( this, SIGNAL(redirectionFinished(Oscar::WORD)),
209 	         this, SLOT(checkRedirectionQueue(Oscar::WORD)) );
210 	connect( d->awayMsgRequestTimer, SIGNAL(timeout()),
211 	         this, SLOT(nextICQAwayMessageRequest()) );
212 }
213 
~Client()214 Client::~Client()
215 {
216 
217 	//delete the connections differently than in deleteConnections()
218 	//deleteLater() seems to cause destruction order issues
219 	deleteStaticTasks();
220     delete d->settings;
221 	delete d->ssiManager;
222 	delete d->awayMsgRequestTimer;
223 	delete d;
224 }
225 
clientSettings() const226 Oscar::Settings* Client::clientSettings() const
227 {
228 	return d->settings;
229 }
230 
connectToServer(const QString & host,quint16 port,bool encrypted,const QString & name)231 void Client::connectToServer( const QString& host, quint16 port, bool encrypted, const QString &name )
232 {
233 	ClientStream* cs = createClientStream();
234 	Connection* c = new Connection( cs, "AUTHORIZER" );
235 	c->setClient( this );
236 
237 	d->encrypted2 = encrypted;
238 
239 	d->loginTask = new StageOneLoginTask( c->rootTask() );
240 	connect( d->loginTask, SIGNAL(finished()), this, SLOT(lt_loginFinished()) );
241 	connectToServer( c, host, port, encrypted, name );
242 }
243 
start(const QString & host,const uint port,const QString & userId,const QString & pass)244 void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass )
245 {
246 	Q_UNUSED( host );
247 	Q_UNUSED( port );
248 
249 	// Cleanup client
250 	close();
251 
252 	d->user = userId;
253 	d->pass = pass;
254 	d->stage = ClientPrivate::StageOne;
255 	d->active = false;
256 }
257 
close()258 void Client::close()
259 {
260 	QList<Connection*> cList = d->connections.connections();
261 	for ( int i = 0; i < cList.size(); i++ )
262 	{
263 		Connection* c = cList.at(i);
264 		(new CloseConnectionTask( c->rootTask() ))->go( Task::AutoDelete );
265 
266 		foreach ( Oscar::MessageInfo info, c->messageInfoList() )
267 			emit messageError( info.contact, info.id );
268 	}
269 
270 	d->active = false;
271 	d->awayMsgRequestTimer->stop();
272 	d->awayMsgRequestQueue.clear();
273 	d->connections.clear();
274 
275 	deleteStaticTasks();
276 
277 	//don't clear the stored status between stage one and two
278 	if ( d->stage == ClientPrivate::StageTwo )
279 	{
280 		d->status.status = 0x0;
281 		d->status.xtraz = -1;
282 		d->status.mood = -1;
283 		d->status.sent = false;
284 		d->status.message.clear();
285 		d->status.title.clear();
286 	}
287 
288 	d->exchanges.clear();
289 	d->redirectRequested = false;
290 	d->currentRedirect = 0;
291 	d->redirectionServices.clear();
292 	d->ssiManager->clear();
293 	d->offlineMessagesRequested = false;
294 }
295 
setStatus(Oscar::DWORD status,const QString & message,int xtraz,const QString & title,int mood)296 void Client::setStatus( Oscar::DWORD status, const QString &message, int xtraz, const QString &title, int mood )
297 {
298 	kDebug(OSCAR_RAW_DEBUG) << "Setting status message to "<< message;
299 
300 	// remember the values to reply with, when requested
301 	bool xtrazChanged = (xtraz > -1 || d->status.xtraz != xtraz);
302 	bool moodChanged = (mood > -1 || d->status.mood != mood);
303 	bool statusInfoChanged = ( !d->status.sent || message != d->status.message || title != d->status.title );
304 	d->status.status = status;
305 	d->status.message = message;
306 	d->status.xtraz = xtraz;
307 	d->status.mood = mood;
308 	d->status.title = title;
309 	d->status.sent = false;
310 
311 	if ( d->active )
312 	{
313 		if ( d->isIcq )
314 		{
315 			// Set invisible/visible flag
316 			Oscar::BYTE privacyByte = ( ( status & 0x0100 ) == 0x0100 ) ? 0x03 : 0x04;
317 			setPrivacyTLVs( privacyByte );
318 		}
319 
320 		Connection* c = d->connections.connectionForFamily( 0x0002 );
321 		if ( !c )
322 			return;
323 
324 		if ( d->isIcq && statusInfoChanged )
325 		{
326 			ICQFullInfo info( false );
327 			info.statusDescription.set( title.toUtf8() );
328 
329 			ICQTlvInfoUpdateTask* infoUpdateTask = new ICQTlvInfoUpdateTask( c->rootTask() );
330 			infoUpdateTask->setInfo( info );
331 			infoUpdateTask->go( Task::AutoDelete );
332 		}
333 
334 		SendDCInfoTask* sdcit = new SendDCInfoTask( c->rootTask(), status );
335 		if ( d->isIcq && moodChanged )
336 			sdcit->setIcqMood( mood );
337 
338 		if ( d->isIcq && statusInfoChanged )
339 			sdcit->setStatusMessage( title );
340 		if ( !d->isIcq && (status & 0xFF) == 0x00 ) //not icq and status is online
341 			sdcit->setStatusMessage( message );
342 
343 		QString msg;
344 		// AIM: you're away exactly when your away message isn't empty.
345 		// can't use QString() as a message either; ProfileTask
346 		// interprets null as "don't change".
347 		if ( (status & 0xFF) == 0x00 ) //is status online?
348 		{
349 			msg = QString::fromAscii("");
350 		}
351 		else
352 		{
353 			if ( message.isEmpty() )
354 				msg = QString::fromAscii(" ");
355 			else
356 				msg = message;
357 		}
358 
359 		ProfileTask* pt = new ProfileTask( c->rootTask() );
360 		pt->setAwayMessage( msg );
361 
362 		if ( d->isIcq && xtrazChanged )
363 			pt->setXtrazStatus( xtraz );
364 
365 		pt->go( Task::AutoDelete );
366 		//Has to be sent after ProfileTask otherwise the EA, DND will be wrong
367 		sdcit->go( Task::AutoDelete );
368 
369 		d->status.sent = true;
370 	}
371 }
372 
ourInfo() const373 UserDetails Client::ourInfo() const
374 {
375 	return d->ourDetails;
376 }
377 
host()378 QString Client::host()
379 {
380 	return d->host;
381 }
382 
port()383 int Client::port()
384 {
385 	return d->port;
386 }
387 
encrypted()388 bool Client::encrypted()
389 {
390 	return d->encrypted;
391 }
392 
SSLName()393 QString Client::SSLName()
394 {
395 	return d->SSLName;
396 }
397 
ssiManager() const398 ContactManager* Client::ssiManager() const
399 {
400 	return d->ssiManager;
401 }
402 
version() const403 const Oscar::ClientVersion* Client::version() const
404 {
405 	return d->version;
406 }
407 
versionCap() const408 Guid Client::versionCap() const
409 {
410 	return d->versionCap;
411 }
412 
413 // SLOTS //
414 
streamConnected()415 void Client::streamConnected()
416 {
417 	kDebug(OSCAR_RAW_DEBUG) ;
418 	if ( d->loginTaskTwo )
419 		d->loginTaskTwo->go( Task::AutoDelete );
420 }
421 
lt_loginFinished()422 void Client::lt_loginFinished()
423 {
424 	/* Check for stage two login first, since we create the stage two
425 	 * task when we finish stage one
426 	 */
427 	if ( d->stage == ClientPrivate::StageTwo )
428 	{
429 		//we've finished logging in. start the services setup
430 		kDebug(OSCAR_RAW_DEBUG) << "stage two done. setting up services";
431 		initializeStaticTasks();
432 		ServiceSetupTask* ssTask = new ServiceSetupTask( d->connections.defaultConnection()->rootTask() );
433 		connect( ssTask, SIGNAL(finished()), this, SLOT(serviceSetupFinished()) );
434 		ssTask->go( Task::AutoDelete ); //fire and forget
435 	}
436 	else if ( d->stage == ClientPrivate::StageOne )
437 	{
438 		kDebug(OSCAR_RAW_DEBUG) << "stage one login done";
439 		disconnect( d->loginTask, SIGNAL(finished()), this, SLOT(lt_loginFinished()) );
440 
441 		if ( d->loginTask->statusCode() == 0 ) //we can start stage two
442 		{
443 			kDebug(OSCAR_RAW_DEBUG) << "no errors from stage one. moving to stage two";
444 
445 			//cache these values since they'll be deleted when we close the connections (which deletes the tasks)
446 			d->host = d->loginTask->bosServer();
447 			d->port = d->loginTask->bosPort().toUInt();
448 			d->encrypted = d->loginTask->bosEncrypted();
449 			d->SSLName = d->loginTask->bosSSLName();
450 			d->cookie = d->loginTask->loginCookie();
451 			close();
452 			QTimer::singleShot( 100, this, SLOT(startStageTwo()) );
453 			d->stage = ClientPrivate::StageTwo;
454 		}
455 		else
456 		{
457 			kDebug(OSCAR_RAW_DEBUG) << "errors reported. not moving to stage two";
458 			close(); //deletes the connections for us
459 		}
460 		d->loginTask->deleteLater();
461 		d->loginTask = 0;
462 	}
463 
464 }
465 
startStageTwo()466 void Client::startStageTwo()
467 {
468 	//create a new connection and set it up
469 	Connection* c = createConnection();
470 	new CloseConnectionTask( c->rootTask() );
471 
472 	//create the new login task
473 	d->loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
474 	d->loginTaskTwo->setCookie( d->cookie );
475 	QObject::connect( d->loginTaskTwo, SIGNAL(finished()), this, SLOT(lt_loginFinished()) );
476 
477 	//connect
478 	QObject::connect( c, SIGNAL(connected()), this, SLOT(streamConnected()) );
479 	connectToServer( c, d->host, d->port, d->encrypted, d->SSLName ) ;
480 
481 }
482 
serviceSetupFinished()483 void Client::serviceSetupFinished()
484 {
485 	d->active = true;
486 
487 	setStatus( d->status.status, d->status.message, d->status.xtraz, d->status.title, d->status.mood );
488 	d->ownStatusTask->go();
489 
490 	emit haveContactList();
491 	emit loggedIn();
492 }
493 
receivedIcqInfo(const QString & contact,unsigned int type)494 void Client::receivedIcqInfo( const QString& contact, unsigned int type )
495 {
496 	kDebug(OSCAR_RAW_DEBUG) << "received icq info for " << contact
497 		<< " of type " << type << endl;
498 
499 	if ( type == ICQUserInfoRequestTask::Short )
500 		emit receivedIcqShortInfo( contact );
501 	else
502 		emit receivedIcqLongInfo( contact );
503 }
504 
receivedInfo(Oscar::DWORD sequence)505 void Client::receivedInfo( Oscar::DWORD sequence )
506 {
507 	UserDetails details = d->userInfoTask->getInfoFor( sequence );
508 	emit receivedUserInfo( details.userId(), details );
509 }
510 
offlineUser(const QString & user,const UserDetails &)511 void Client::offlineUser( const QString& user, const UserDetails& )
512 {
513 	emit userIsOffline( user );
514 }
515 
haveOwnUserInfo()516 void Client::haveOwnUserInfo()
517 {
518 	kDebug( OSCAR_RAW_DEBUG );
519 	UserDetails ud = d->ownStatusTask->getInfo();
520 	d->ourDetails = ud;
521 	emit haveOwnInfo();
522 
523 	if ( !d->offlineMessagesRequested && d->active )
524 	{
525 		//retrieve offline messages
526 		Connection* c = d->connections.connectionForFamily( 0x0004 );
527 		if ( !c )
528 			return;
529 
530 		OfflineMessagesTask *offlineMsgTask = new OfflineMessagesTask( c->rootTask() );
531 		offlineMsgTask->go( Task::AutoDelete );
532 		d->offlineMessagesRequested = true;
533 	}
534 }
535 
setCodecProvider(Client::CodecProvider * codecProvider)536 void Client::setCodecProvider( Client::CodecProvider* codecProvider )
537 {
538 	d->codecProvider = codecProvider;
539 }
540 
setVersion(const Oscar::ClientVersion * version)541 void Client::setVersion( const Oscar::ClientVersion* version )
542 {
543 	d->version = version;
544 }
545 
setVersionCap(const QByteArray & cap)546 void Client::setVersionCap( const QByteArray &cap )
547 {
548 	d->versionCap = Guid( cap );
549 }
550 
551 // INTERNALS //
552 
userId() const553 QString Client::userId() const
554 {
555 	return d->user;
556 }
557 
password() const558 QString Client::password() const
559 {
560 	return d->pass;
561 }
562 
statusXtraz() const563 int Client::statusXtraz() const
564 {
565 	return d->status.xtraz;
566 }
567 
statusMood() const568 int Client::statusMood() const
569 {
570 	return d->status.mood;
571 }
572 
statusTitle() const573 QString Client::statusTitle() const
574 {
575 	return d->status.title;
576 }
577 
statusMessage() const578 QString Client::statusMessage() const
579 {
580 	return d->status.message;
581 }
582 
setStatusMessage(const QString & message)583 void Client::setStatusMessage( const QString &message )
584 {
585 	d->status.message = message;
586 }
587 
ipAddress() const588 QByteArray Client::ipAddress() const
589 {
590 	//!TODO determine ip address
591 	return "127.0.0.1";
592 }
593 
notifyTaskError(const Oscar::SNAC & s,int errCode,bool fatal)594 void Client::notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal )
595 {
596 	emit taskError( s, errCode, fatal );
597 }
598 
notifySocketError(int errCode,const QString & msg)599 void Client::notifySocketError( int errCode, const QString& msg )
600 {
601 	emit socketError( errCode, msg );
602 }
603 
sendMessage(const Oscar::Message & msg,bool isAuto)604 void Client::sendMessage( const Oscar::Message& msg, bool isAuto)
605 {
606     Connection* c = nullptr;
607     if ( msg.channel() == 0x0003 )
608     {
609         c = d->connections.connectionForChatRoom( msg.exchange(), msg.chatRoom() );
610         if ( !c )
611             return;
612 
613 	    kDebug(OSCAR_RAW_DEBUG) << "sending message to chat room: " << msg.chatRoom() << " on exchange " << msg.exchange();
614         ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), msg.exchange(), msg.chatRoom() );
615         cst->setMessage( msg );
616         cst->setEncoding( d->codecProvider->codecForAccount()->name() );
617         cst->go( Task::AutoDelete );
618     }
619     else
620     {
621         c = d->connections.connectionForFamily( 0x0004 );
622         if ( !c )
623             return;
624         SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
625         // Set whether or not the message is an automated response
626         sendMsgTask->setAutoResponse( isAuto );
627         sendMsgTask->setMessage( msg );
628         sendMsgTask->go( Task::AutoDelete );
629     }
630 }
631 
receivedMessage(const Oscar::Message & msg)632 void Client::receivedMessage( const Oscar::Message& msg )
633 {
634 	if ( msg.channel() == 2 && !msg.hasProperty( Oscar::Message::AutoResponse ) )
635 	{
636 		// channel 2 message needs an autoresponse, regardless of type
637 		Connection* c = d->connections.connectionForFamily( 0x0004 );
638 		if ( !c )
639 			return;
640 
641 		Oscar::Message response ( msg );
642 		if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
643 		{
644 			kDebug( OSCAR_RAW_DEBUG ) << "Away message request";
645 			QTextCodec* codec = d->codecProvider->codecForContact( msg.sender() );
646 			response.setText( Oscar::Message::UserDefined, statusMessage(), codec );
647 			emit userReadsStatusMessage( msg.sender() );
648 		}
649 		else if ( msg.messageType() == Oscar::MessageType::Plugin )
650 		{
651 			Oscar::MessagePlugin::Types type = msg.plugin()->type();
652 			Oscar::WORD subType = msg.plugin()->subTypeId();
653 			if ( type == Oscar::MessagePlugin::XtrazScript )
654 			{
655 				if ( subType == Oscar::MessagePlugin::SubScriptNotify )
656 				{
657 					using namespace Xtraz;
658 					XtrazNotify xNotify;
659 					xNotify.handle( msg.plugin() );
660 					if ( xNotify.type() == XtrazNotify::Request && xNotify.pluginId() == QLatin1String("srvMng") )
661 					{
662 						if ( xNotify.findService( QStringLiteral("cAwaySrv") ) )
663 						{
664 							XtrazNotify xNotifyResponse;
665 							xNotifyResponse.setSenderUni( userId() );
666 							response.setPlugin( xNotifyResponse.statusResponse( statusXtraz(), statusTitle(), statusMessage() ) );
667 							emit userReadsStatusMessage( msg.sender() );
668 						}
669 					}
670 				}
671 			}
672 			else if ( type == Oscar::MessagePlugin::StatusMsgExt )
673 			{
674 				Buffer buffer;
675 
676 				buffer.addLEDBlock( statusMessage().toUtf8() );
677 				//TODO: Change this to text/x-aolrtf
678 				buffer.addLEDBlock( "text/plain" );
679 
680 				msg.plugin()->setData( buffer.buffer() );
681 				emit userReadsStatusMessage( msg.sender() );
682 			}
683 		}
684 		else
685 		{
686 			response.setEncoding( Oscar::Message::UserDefined );
687 			response.setTextArray( QByteArray() );
688 		}
689 		response.setReceiver( msg.sender() );
690 		response.addProperty( Oscar::Message::AutoResponse );
691 		SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
692 		sendMsgTask->setMessage( response );
693 		sendMsgTask->go( Task::AutoDelete );
694 	}
695 
696 	if ( msg.hasProperty( Oscar::Message::AutoResponse ) )
697 	{
698 		if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
699 		{
700 			// we got a response to a status message request.
701 			QString awayMessage( msg.text( d->codecProvider->codecForContact( msg.sender() ) ) );
702 			kDebug( OSCAR_RAW_DEBUG ) << "Received an away message: " << awayMessage;
703 			emit receivedAwayMessage( msg.sender(), awayMessage );
704 		}
705 		else if ( msg.messageType() == Oscar::MessageType::Plugin )
706 		{
707 			kDebug( OSCAR_RAW_DEBUG ) << "Received an plugin message response.";
708 
709 			Oscar::MessagePlugin::Types type = msg.plugin()->type();
710 			Oscar::WORD subType = msg.plugin()->subTypeId();
711 			if ( type == Oscar::MessagePlugin::XtrazScript )
712 			{
713 				if ( subType == Oscar::MessagePlugin::SubScriptNotify )
714 				{
715 					using namespace Xtraz;
716 					XtrazNotify xNotify;
717 					xNotify.handle( msg.plugin() );
718 					if ( xNotify.type() == XtrazNotify::Response )
719 					{
720 						const Xtraz::XAwayService* service = dynamic_cast<const XAwayService*>(xNotify.findService( QStringLiteral("cAwaySrv") ));
721 						if ( service )
722 							emit receivedXStatusMessage( service->senderId(), service->iconIndex(),
723 							                             service->description(), service->message() );
724 					}
725 				}
726 			}
727 			else if ( type == Oscar::MessagePlugin::StatusMsgExt )
728 			{
729 				// we got a response to a status message request.
730 				Buffer buffer( msg.plugin()->data() );
731 
732 				QString awayMessage = QString::fromUtf8( buffer.getLEDBlock() );
733 				kDebug( OSCAR_RAW_DEBUG ) << "Received an away message: " << awayMessage;
734 				emit receivedAwayMessage( msg.sender(), awayMessage );
735 			}
736 		}
737 	}
738 	else
739 	{
740 		if ( msg.messageType() == Oscar::MessageType::Plugin )
741 		{
742 			kDebug( OSCAR_RAW_DEBUG ) << "Received a plugin message.";
743 		}
744 		else if ( !msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
745 		{
746 			// Filter out miranda's invisible check
747 			if ( msg.messageType() == 0x0004 && msg.textArray().isEmpty() )
748 				return;
749 
750 			// let application handle it
751 			kDebug( OSCAR_RAW_DEBUG ) << "Emitting receivedMessage";
752 			emit messageReceived( msg );
753 		}
754 	}
755 }
756 
fileMessage(const Oscar::Message & msg)757 void Client::fileMessage( const Oscar::Message& msg )
758 {
759 	Connection* c = d->connections.connectionForFamily( 0x0004 );
760 	if ( !c )
761 		return;
762 
763 	kDebug( OSCAR_RAW_DEBUG ) << "internal ip: " << c->localAddress().toString();
764 	kDebug( OSCAR_RAW_DEBUG ) << "external ip: " << ourInfo().dcExternalIp().toString();
765 
766 	SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
767 	// Set whether or not the message is an automated response
768 	sendMsgTask->setAutoResponse( false );
769 	sendMsgTask->setMessage( msg );
770 	sendMsgTask->setIp( c->localAddress().toIPv4Address() );
771 	sendMsgTask->go( Task::AutoDelete );
772 }
773 
requestAuth(const QString & contactid,const QString & reason)774 void Client::requestAuth( const QString& contactid, const QString& reason )
775 {
776 	Connection* c = d->connections.connectionForFamily( 0x0013 );
777 	if ( !c )
778 		return;
779 	d->ssiAuthTask->sendAuthRequest( contactid, reason );
780 }
781 
sendAuth(const QString & contactid,const QString & reason,bool auth)782 void Client::sendAuth( const QString& contactid, const QString& reason, bool auth )
783 {
784 	Connection* c = d->connections.connectionForFamily( 0x0013 );
785 	if ( !c )
786 		return;
787 	d->ssiAuthTask->sendAuthReply( contactid, reason, auth );
788 }
789 
isActive() const790 bool Client::isActive() const
791 {
792 	return d->active;
793 }
794 
isIcq() const795 bool Client::isIcq() const
796 {
797 	return d->isIcq;
798 }
799 
setIsIcq(bool isIcq)800 void Client::setIsIcq( bool isIcq )
801 {
802 	d->isIcq = isIcq;
803 }
804 
debug(const QString & str)805 void Client::debug( const QString& str )
806 {
807 	Q_UNUSED(str);
808 //	qDebug( "CLIENT: %s", str.toAscii() );
809 }
810 
initializeStaticTasks()811 void Client::initializeStaticTasks()
812 {
813 	//set up the extra tasks
814 	Connection* c = d->connections.defaultConnection();
815 	if ( !c )
816 		return;
817 	d->errorTask = new ErrorTask( c->rootTask() );
818 	d->onlineNotifier = new OnlineNotifierTask( c->rootTask() );
819 	d->ownStatusTask = new OwnUserInfoTask( c->rootTask() );
820 	d->messageReceiverTask = new MessageReceiverTask( c->rootTask() );
821 	d->messageAckTask = new MessageAckTask( c->rootTask() );
822 	d->ssiAuthTask = new SSIAuthTask( c->rootTask() );
823 	d->icqInfoTask = new ICQUserInfoRequestTask( c->rootTask() );
824 	d->icqTlvInfoTask = new ICQTlvInfoRequestTask( c->rootTask() );
825 	d->userInfoTask = new UserInfoTask( c->rootTask() );
826 	d->typingNotifyTask = new TypingNotifyTask( c->rootTask() );
827 	d->ssiModifyTask = new SSIModifyTask( c->rootTask(), true );
828 
829 	connect( d->onlineNotifier, SIGNAL(userIsOnline(QString,UserDetails)),
830 	         this, SIGNAL(receivedUserInfo(QString,UserDetails)) );
831 	connect( d->onlineNotifier, SIGNAL(userIsOffline(QString,UserDetails)),
832 	         this, SLOT(offlineUser(QString,UserDetails)) );
833 
834 	connect( d->ownStatusTask, SIGNAL(gotInfo()), this, SLOT(haveOwnUserInfo()) );
835 	connect( d->ownStatusTask, SIGNAL(buddyIconUploadRequested()), this,
836 	         SIGNAL(iconNeedsUploading()) );
837 
838 	connect( d->messageReceiverTask, SIGNAL(receivedMessage(Oscar::Message)),
839 	         this, SLOT(receivedMessage(Oscar::Message)) );
840 	connect( d->messageReceiverTask, SIGNAL(fileMessage(int,QString,QByteArray,Buffer)),
841 	         this, SLOT(gotFileMessage(int,QString,QByteArray,Buffer)) );
842 	connect( d->messageReceiverTask, SIGNAL(chatroomMessage(Oscar::Message,QByteArray)),
843 	         this, SLOT(gotChatRoomMessage(Oscar::Message,QByteArray)) );
844 
845 	connect( d->messageAckTask, SIGNAL(messageAck(QString,uint)),
846 	         this, SIGNAL(messageAck(QString,uint)) );
847 	connect( d->errorTask, SIGNAL(messageError(QString,uint)),
848 	         this, SIGNAL(messageError(QString,uint)) );
849 
850 	connect( d->ssiAuthTask, SIGNAL(authRequested(QString,QString)),
851 	         this, SIGNAL(authRequestReceived(QString,QString)) );
852 	connect( d->ssiAuthTask, SIGNAL(authReplied(QString,QString,bool)),
853 	         this, SIGNAL(authReplyReceived(QString,QString,bool)) );
854 
855 	connect( d->icqInfoTask, SIGNAL(receivedInfoFor(QString,uint)),
856 	         this, SLOT(receivedIcqInfo(QString,uint)) );
857 	connect( d->icqTlvInfoTask, SIGNAL(receivedInfoFor(QString)),
858 	         this, SIGNAL(receivedIcqTlvInfo(QString)) );
859 
860 	connect( d->userInfoTask, SIGNAL(receivedProfile(QString,QString)),
861 	         this, SIGNAL(receivedProfile(QString,QString)) );
862 	connect( d->userInfoTask, SIGNAL(receivedAwayMessage(QString,QString)),
863 	         this, SIGNAL(receivedAwayMessage(QString,QString)) );
864 	connect( d->typingNotifyTask, SIGNAL(typingStarted(QString)),
865 	         this, SIGNAL(userStartedTyping(QString)) );
866 	connect( d->typingNotifyTask, SIGNAL(typingFinished(QString)),
867 	         this, SIGNAL(userStoppedTyping(QString)) );
868 }
869 
removeGroup(const QString & groupName)870 void Client::removeGroup( const QString& groupName )
871 {
872 	Connection* c = d->connections.connectionForFamily( 0x0013 );
873 	if ( !c )
874 		return;
875 
876 	kDebug( OSCAR_RAW_DEBUG ) << "Removing group " << groupName << " from Contact";
877 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
878 	if ( ssimt->removeGroup( groupName ) )
879 		ssimt->go( Task::AutoDelete );
880 	else
881 		delete ssimt;
882 }
883 
addGroup(const QString & groupName)884 void Client::addGroup( const QString& groupName )
885 {
886 	Connection* c = d->connections.connectionForFamily( 0x0013 );
887 	if ( !c )
888 		return;
889 
890 	kDebug( OSCAR_RAW_DEBUG ) << "Adding group " << groupName << " to Contact";
891 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
892 	if ( ssimt->addGroup( groupName ) )
893 		ssimt->go( Task::AutoDelete );
894 	else
895 		delete ssimt;
896 }
897 
addContact(const QString & contactName,const QString & groupName)898 void Client::addContact( const QString& contactName, const QString& groupName )
899 {
900 	Connection* c = d->connections.connectionForFamily( 0x0013 );
901 	if ( !c )
902 		return;
903 
904 	kDebug( OSCAR_RAW_DEBUG ) << "Adding contact " << contactName << " to ssi in group " << groupName;
905 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
906 	if ( ssimt->addContact( contactName, groupName )  )
907 		ssimt->go( Task::AutoDelete );
908 	else
909 		delete ssimt;
910 }
911 
removeContact(const QString & contactName)912 void Client::removeContact( const QString& contactName )
913 {
914 	Connection* c = d->connections.connectionForFamily( 0x0013 );
915 	if ( !c )
916 		return;
917 
918 	kDebug( OSCAR_RAW_DEBUG ) << "Removing contact " << contactName << " from ssi";
919 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
920 	if ( ssimt->removeContact( contactName ) )
921 		ssimt->go( Task::AutoDelete );
922 	else
923 		delete ssimt;
924 }
925 
renameGroup(const QString & oldGroupName,const QString & newGroupName)926 void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName )
927 {
928 	Connection* c = d->connections.connectionForFamily( 0x0013 );
929 	if ( !c )
930 		return;
931 
932 	kDebug( OSCAR_RAW_DEBUG ) << "Renaming group " << oldGroupName << " to " << newGroupName;
933 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
934 	if ( ssimt->renameGroup( oldGroupName, newGroupName ) )
935 		ssimt->go( Task::AutoDelete );
936 	else
937 		delete ssimt;
938 }
939 
modifyContactItem(const OContact & oldItem,const OContact & newItem)940 void Client::modifyContactItem( const OContact& oldItem, const OContact& newItem )
941 {
942 	int action = 0; //0 modify, 1 add, 2 remove TODO cleanup!
943 	Connection* c = d->connections.connectionForFamily( 0x0013 );
944 	if ( !c )
945 		return;
946 
947 	if ( !oldItem && newItem )
948 		action = 1;
949 	if ( oldItem && !newItem )
950 		action = 2;
951 
952 	kDebug(OSCAR_RAW_DEBUG) << "Add/Mod/Del item on server";
953 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
954 	switch ( action )
955 	{
956 	case 0:
957 		if ( ssimt->modifyItem( oldItem, newItem ) )
958 			ssimt->go( Task::AutoDelete );
959 		else
960 			delete ssimt;
961 		break;
962 	case 1:
963 		if ( ssimt->addItem( newItem ) )
964 			ssimt->go( Task::AutoDelete );
965 		else
966 			delete ssimt;
967 		break;
968 	case 2:
969 		if ( ssimt->removeItem( oldItem ) )
970 			ssimt->go( Task::AutoDelete );
971 		else
972 			delete ssimt;
973 		break;
974 	}
975 }
976 
changeContactGroup(const QString & contact,const QString & newGroupName)977 void Client::changeContactGroup( const QString& contact, const QString& newGroupName )
978 {
979 	Connection* c = d->connections.connectionForFamily( 0x0013 );
980 	if ( !c )
981 		return;
982 
983 	kDebug(OSCAR_RAW_DEBUG) << "Changing " << contact << "'s group to "
984 		<< newGroupName << endl;
985 	SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
986 	if ( ssimt->changeGroup( contact, newGroupName ) )
987 		ssimt->go( Task::AutoDelete );
988 	else
989 		delete ssimt;
990 }
991 
changeContactAlias(const QString & contact,const QString & alias)992 void Client::changeContactAlias( const QString& contact, const QString& alias )
993 {
994 	Connection* c = d->connections.connectionForFamily( 0x0013 );
995 	if ( !c )
996 		return;
997 
998 	OContact item = ssiManager()->findContact( contact );
999 	if ( item )
1000 	{
1001 		OContact oldItem(item);
1002 
1003 		if ( alias.isEmpty() )
1004 		{
1005 			QList<TLV> tList( item.tlvList() );
1006 			TLV tlv = Oscar::findTLV( tList, 0x0131 );
1007 			if ( !tlv )
1008 				return;
1009 
1010 			tList.removeAll( tlv );
1011 			item.setTLVList( tList );
1012 		}
1013 		else
1014 		{
1015 			QList<TLV> tList;
1016 
1017 			QByteArray data = alias.toUtf8();
1018 			tList.append( TLV( 0x0131, data.size(), data ) );
1019 
1020 			if ( !Oscar::updateTLVs( item, tList ) )
1021 				return;
1022 		}
1023 
1024 		kDebug( OSCAR_RAW_DEBUG ) << "Changing " << contact << "'s alias to " << alias;
1025 		SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
1026 		if ( ssimt->modifyContact( oldItem, item ) )
1027 			ssimt->go( Task::AutoDelete );
1028 		else
1029 			delete ssimt;
1030 	}
1031 }
1032 
setPrivacyTLVs(Oscar::BYTE privacy,Oscar::DWORD userClasses)1033 void Client::setPrivacyTLVs( Oscar::BYTE privacy, Oscar::DWORD userClasses )
1034 {
1035 	OContact item = ssiManager()->findItem( QString(), ROSTER_VISIBILITY );
1036 
1037 	QList<Oscar::TLV> tList;
1038 	tList.append( TLV( 0x00CA, 1, (char *)&privacy ) );
1039 	tList.append( TLV( 0x00CB, sizeof(userClasses), (char *)&userClasses ) );
1040 
1041 	if ( !item )
1042 	{
1043 		kDebug( OSCAR_RAW_DEBUG ) << "Adding new privacy TLV item";
1044 		QString empty;
1045 		OContact s( empty, 0, ssiManager()->nextContactId(), ROSTER_VISIBILITY, tList );
1046 		modifyContactItem( item, s );
1047 	}
1048 	else
1049 	{ //found an item
1050 		OContact s(item);
1051 
1052 		if ( Oscar::updateTLVs( s, tList ) == true )
1053 		{
1054 			kDebug( OSCAR_RAW_DEBUG ) << "Updating privacy TLV item";
1055 			modifyContactItem( item, s );
1056 		}
1057 	}
1058 }
1059 
requestShortTlvInfo(const QString & contactId,const QByteArray & metaInfoId)1060 void Client::requestShortTlvInfo( const QString& contactId, const QByteArray &metaInfoId )
1061 {
1062 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1063 	if ( !c )
1064 		return;
1065 
1066 	d->icqTlvInfoTask->setUser( Oscar::normalize( contactId ) );
1067 	d->icqTlvInfoTask->setMetaInfoId( metaInfoId );
1068 	d->icqTlvInfoTask->setType( ICQTlvInfoRequestTask::Short );
1069 	d->icqTlvInfoTask->go();
1070 }
1071 
requestMediumTlvInfo(const QString & contactId,const QByteArray & metaInfoId)1072 void Client::requestMediumTlvInfo( const QString& contactId, const QByteArray &metaInfoId )
1073 {
1074 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1075 	if ( !c )
1076 		return;
1077 
1078 	d->icqTlvInfoTask->setUser( Oscar::normalize( contactId ) );
1079 	d->icqTlvInfoTask->setMetaInfoId( metaInfoId );
1080 	d->icqTlvInfoTask->setType( ICQTlvInfoRequestTask::Medium );
1081 	d->icqTlvInfoTask->go();
1082 }
1083 
requestLongTlvInfo(const QString & contactId,const QByteArray & metaInfoId)1084 void Client::requestLongTlvInfo( const QString& contactId, const QByteArray &metaInfoId )
1085 {
1086 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1087 	if ( !c )
1088 		return;
1089 
1090 	d->icqTlvInfoTask->setUser( Oscar::normalize( contactId ) );
1091 	d->icqTlvInfoTask->setMetaInfoId( metaInfoId );
1092 	d->icqTlvInfoTask->setType( ICQTlvInfoRequestTask::Long );
1093 	d->icqTlvInfoTask->go();
1094 }
1095 
requestFullInfo(const QString & contactId)1096 void Client::requestFullInfo( const QString& contactId )
1097 {
1098 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1099 	if ( !c )
1100 		return;
1101 	d->icqInfoTask->setUser( contactId );
1102 	d->icqInfoTask->setType( ICQUserInfoRequestTask::Long );
1103 	d->icqInfoTask->go();
1104 }
1105 
requestShortInfo(const QString & contactId)1106 void Client::requestShortInfo( const QString& contactId )
1107 {
1108 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1109 	if ( !c )
1110 		return;
1111 	d->icqInfoTask->setUser( contactId );
1112 	d->icqInfoTask->setType( ICQUserInfoRequestTask::Short );
1113 	d->icqInfoTask->go();
1114 }
1115 
sendWarning(const QString & contact,bool anonymous)1116 void Client::sendWarning( const QString& contact, bool anonymous )
1117 {
1118 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1119 	if ( !c )
1120 		return;
1121 	WarningTask* warnTask = new WarningTask( c->rootTask() );
1122 	warnTask->setContact( contact );
1123 	warnTask->setAnonymous( anonymous );
1124 	QObject::connect( warnTask, SIGNAL(userWarned(QString,quint16,quint16)),
1125 	                  this, SIGNAL(userWarned(QString,quint16,quint16)) );
1126 	warnTask->go( Task::AutoDelete );
1127 }
1128 
changeICQPassword(const QString & password)1129 bool Client::changeICQPassword( const QString& password )
1130 {
1131 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1132 	if ( !c )
1133 		return false;
1134 
1135 	ICQChangePasswordTask* task = new ICQChangePasswordTask( c->rootTask() );
1136 	QObject::connect( task, SIGNAL(finished()), this, SLOT(changeICQPasswordFinished()) );
1137 	task->setPassword( password );
1138 	task->go( Task::AutoDelete );
1139 	return true;
1140 }
1141 
changeICQPasswordFinished()1142 void Client::changeICQPasswordFinished()
1143 {
1144 	ICQChangePasswordTask* task = (ICQChangePasswordTask*)sender();
1145 	if ( task->success() )
1146 		d->pass = task->password();
1147 
1148 	emit icqPasswordChanged( !task->success() );
1149 }
1150 
getFullInfo(const QString & contact)1151 ICQFullInfo Client::getFullInfo( const QString& contact )
1152 {
1153 	return d->icqTlvInfoTask->fullInfoFor( contact );
1154 }
1155 
getGeneralInfo(const QString & contact)1156 ICQGeneralUserInfo Client::getGeneralInfo( const QString& contact )
1157 {
1158 	return d->icqInfoTask->generalInfoFor( contact );
1159 }
1160 
getWorkInfo(const QString & contact)1161 ICQWorkUserInfo Client::getWorkInfo( const QString& contact )
1162 {
1163 	return d->icqInfoTask->workInfoFor( contact );
1164 }
1165 
getEmailInfo(const QString & contact)1166 ICQEmailInfo Client::getEmailInfo( const QString& contact )
1167 {
1168 	return d->icqInfoTask->emailInfoFor( contact );
1169 }
1170 
getNotesInfo(const QString & contact)1171 ICQNotesInfo Client::getNotesInfo( const QString& contact )
1172 {
1173 	return d->icqInfoTask->notesInfoFor( contact );
1174 }
1175 
getMoreInfo(const QString & contact)1176 ICQMoreUserInfo Client::getMoreInfo( const QString& contact )
1177 {
1178 	return d->icqInfoTask->moreInfoFor( contact );
1179 }
1180 
getInterestInfo(const QString & contact)1181 ICQInterestInfo Client::getInterestInfo( const QString& contact )
1182 {
1183 	return d->icqInfoTask->interestInfoFor( contact );
1184 }
1185 
getOrgAffInfo(const QString & contact)1186 ICQOrgAffInfo Client::getOrgAffInfo( const QString& contact )
1187 {
1188 	return d->icqInfoTask->orgAffInfoFor( contact );
1189 }
1190 
getShortInfo(const QString & contact)1191 ICQShortInfo Client::getShortInfo( const QString& contact )
1192 {
1193 	return d->icqInfoTask->shortInfoFor( contact );
1194 }
1195 
chatExchangeList() const1196 QList<int> Client::chatExchangeList() const
1197 {
1198     return d->exchanges;
1199 }
1200 
setChatExchangeList(const QList<int> & exchanges)1201 void Client::setChatExchangeList( const QList<int>& exchanges )
1202 {
1203     d->exchanges = exchanges;
1204 }
1205 
requestAIMProfile(const QString & contact)1206 void Client::requestAIMProfile( const QString& contact )
1207 {
1208 	d->userInfoTask->requestInfoFor( contact, UserInfoTask::Profile );
1209 }
1210 
requestAIMAwayMessage(const QString & contact)1211 void Client::requestAIMAwayMessage( const QString& contact )
1212 {
1213 	d->userInfoTask->requestInfoFor( contact, UserInfoTask::AwayMessage );
1214 }
1215 
requestICQAwayMessage(const QString & contact,ICQStatus contactStatus)1216 void Client::requestICQAwayMessage( const QString& contact, ICQStatus contactStatus )
1217 {
1218 	kDebug(OSCAR_RAW_DEBUG) << "requesting away message for " << contact;
1219 	Oscar::Message msg;
1220 	msg.setChannel( 2 );
1221 	msg.setReceiver( contact );
1222 
1223 	if ( (contactStatus & ICQXStatus) == ICQXStatus )
1224 	{
1225 		Xtraz::XtrazNotify xNotify;
1226 		xNotify.setSenderUni( userId() );
1227 
1228 		msg.setMessageType( Oscar::MessageType::Plugin ); // plugin message
1229 		msg.setPlugin( xNotify.statusRequest() );
1230 	}
1231 	else if ( (contactStatus & ICQPluginStatus) == ICQPluginStatus )
1232 	{
1233 		Oscar::WORD subTypeId = 0xFFFF;
1234 		QByteArray subTypeText;
1235 
1236 		switch ( contactStatus & ICQStatusMask )
1237 		{
1238 		case ICQOnline:
1239 		case ICQFreeForChat:
1240 		case ICQAway:
1241 			subTypeId = 1;
1242 			subTypeText = "Away Status Message";
1243 			break;
1244 		case ICQOccupied:
1245 		case ICQDoNotDisturb:
1246 			subTypeId = 2;
1247 			subTypeText = "Busy Status Message";
1248 			break;
1249 		case ICQNotAvailable:
1250 			subTypeId = 3;
1251 			subTypeText = "N/A Status Message";
1252 			break;
1253 		default:
1254 			// may be a good way to deal with possible error and lack of online status message?
1255 			emit receivedAwayMessage( contact, QStringLiteral("Sorry, this protocol does not support this type of status message") );
1256 			return;
1257 		}
1258 
1259 		Oscar::MessagePlugin *plugin = new Oscar::MessagePlugin();
1260 		plugin->setType( Oscar::MessagePlugin::StatusMsgExt );
1261 		plugin->setSubTypeId( subTypeId );
1262 		plugin->setSubTypeText( subTypeText );
1263 
1264 		Buffer buffer;
1265 		buffer.addLEDWord( 0x00000000 );
1266 		//TODO: Change this to text/x-aolrtf
1267 		buffer.addLEDBlock( "text/plain" );
1268 		plugin->setData( buffer.buffer() );
1269 
1270 		msg.setMessageType( Oscar::MessageType::Plugin ); // plugin message
1271 		msg.setPlugin( plugin );
1272 	}
1273 	else
1274 	{
1275 		msg.addProperty( Oscar::Message::StatusMessageRequest );
1276 		switch ( contactStatus & ICQStatusMask )
1277 		{
1278 		case ICQAway:
1279 			msg.setMessageType( Oscar::MessageType::AutoAway ); // away
1280 			break;
1281 		case ICQOccupied:
1282 			msg.setMessageType( Oscar::MessageType::AutoBusy ); // occupied
1283 			break;
1284 		case ICQNotAvailable:
1285 			msg.setMessageType( Oscar::MessageType::AutoNA ); // not awailable
1286 			break;
1287 		case ICQDoNotDisturb:
1288 			msg.setMessageType( Oscar::MessageType::AutoDND ); // do not disturb
1289 			break;
1290 		case ICQFreeForChat:
1291 			msg.setMessageType( Oscar::MessageType::AutoFFC ); // free for chat
1292 			break;
1293 		default:
1294 			// may be a good way to deal with possible error and lack of online status message?
1295 			emit receivedAwayMessage( contact, QStringLiteral("Sorry, this protocol does not support this type of status message") );
1296 			return;
1297 		}
1298 	}
1299 	sendMessage( msg );
1300 }
1301 
addICQAwayMessageRequest(const QString & contact,ICQStatus contactStatus)1302 void Client::addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus )
1303 {
1304 	kDebug(OSCAR_RAW_DEBUG) << "adding away message request for "
1305 	                         << contact << " to queue" << endl;
1306 
1307 	//remove old request if still exists
1308 	removeICQAwayMessageRequest( contact );
1309 
1310 	ClientPrivate::AwayMsgRequest amr = { contact, contactStatus };
1311 	d->awayMsgRequestQueue.prepend( amr );
1312 
1313 	if ( !d->awayMsgRequestTimer->isActive() )
1314 		d->awayMsgRequestTimer->start( 1000 );
1315 }
1316 
removeICQAwayMessageRequest(const QString & contact)1317 void Client::removeICQAwayMessageRequest( const QString& contact )
1318 {
1319 	kDebug(OSCAR_RAW_DEBUG) << "removing away message request for "
1320 	                         << contact << " from queue" << endl;
1321 
1322 	QList<ClientPrivate::AwayMsgRequest>::iterator it = d->awayMsgRequestQueue.begin();
1323 	while ( it != d->awayMsgRequestQueue.end() )
1324 	{
1325 		if ( (*it).contact == contact )
1326 			it = d->awayMsgRequestQueue.erase( it );
1327 		else
1328 			it++;
1329 	}
1330 }
1331 
nextICQAwayMessageRequest()1332 void Client::nextICQAwayMessageRequest()
1333 {
1334 	kDebug(OSCAR_RAW_DEBUG) << "request queue count " << d->awayMsgRequestQueue.count();
1335 
1336 	if ( d->awayMsgRequestQueue.empty() )
1337 	{
1338 		d->awayMsgRequestTimer->stop();
1339 		return;
1340 	}
1341 	else
1342 	{
1343 		Connection* c = d->connections.connectionForFamily( 0x0004 );
1344 		if ( !c )
1345 			return;
1346 
1347 		SNAC s = { 0x0004, 0x0006, 0x0000, 0x00000000 };
1348 		//get time needed to restore level to initial
1349 		//for some reason when we are long under initial level
1350 		//icq server will start to block our messages
1351 		int time = c->rateManager()->timeToInitialLevel( s );
1352 		if ( time > 0 )
1353 		{
1354 			d->awayMsgRequestTimer->start( time );
1355 			return;
1356 		}
1357 		else
1358 		{
1359 			d->awayMsgRequestTimer->start( 5000 );
1360 		}
1361 	}
1362 
1363 	ClientPrivate::AwayMsgRequest amr;
1364 
1365 	amr = d->awayMsgRequestQueue.back();
1366 	d->awayMsgRequestQueue.pop_back();
1367 	requestICQAwayMessage( amr.contact, amr.contactStatus );
1368 }
1369 
requestStatusInfo(const QString & contact)1370 void Client::requestStatusInfo( const QString& contact )
1371 {
1372 	d->userInfoTask->requestInfoFor( contact, UserInfoTask::General );
1373 }
1374 
whitePagesSearch(const ICQWPSearchInfo & info)1375 void Client::whitePagesSearch( const ICQWPSearchInfo& info )
1376 {
1377 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1378 	if ( !c )
1379 		return;
1380 	UserSearchTask* ust = new UserSearchTask( c->rootTask() );
1381 	connect( ust, SIGNAL(foundUser(ICQSearchResult)),
1382 	         this, SIGNAL(gotSearchResults(ICQSearchResult)) );
1383 	connect( ust, SIGNAL(searchFinished(int)), this, SIGNAL(endOfSearch(int)) );
1384 	ust->go( Task::AutoDelete ); //onGo does nothing in this task. This is just here so autodelete works
1385 	ust->searchWhitePages( info );
1386 }
1387 
uinSearch(const QString & uin)1388 void Client::uinSearch( const QString& uin )
1389 {
1390 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1391 	if ( !c )
1392 		return;
1393 	UserSearchTask* ust = new UserSearchTask( c->rootTask() );
1394 	connect( ust, SIGNAL(foundUser(ICQSearchResult)),
1395 	         this, SIGNAL(gotSearchResults(ICQSearchResult)) );
1396 	connect( ust, SIGNAL(searchFinished(int)), this, SIGNAL(endOfSearch(int)) );
1397 	ust->go( Task::AutoDelete ); //onGo does nothing in this task. This is just here so autodelete works
1398 	ust->searchUserByUIN( uin );
1399 }
1400 
updateProfile(const QString & profile)1401 void Client::updateProfile( const QString& profile )
1402 {
1403 	Connection* c = d->connections.connectionForFamily( 0x0002 );
1404 	if ( !c )
1405 		return;
1406 	ProfileTask* pt = new ProfileTask( c->rootTask() );
1407 	pt->setProfileText( profile );
1408 	pt->go( Task::AutoDelete );
1409 }
1410 
updateProfile(const QList<ICQInfoBase * > & infoList)1411 bool Client::updateProfile( const QList<ICQInfoBase*>& infoList )
1412 {
1413 	Connection* c = d->connections.connectionForFamily( 0x0015 );
1414 	if ( !c )
1415 		return false;
1416 
1417 	ICQUserInfoUpdateTask* ui = new ICQUserInfoUpdateTask( c->rootTask() );
1418 	ui->setInfo( infoList );
1419 	ui->go( Task::AutoDelete );
1420 	return true;
1421 }
1422 
sendTyping(const QString & contact,bool typing)1423 void Client::sendTyping( const QString & contact, bool typing )
1424 {
1425 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1426 	if ( !c || !d->active )
1427 		return;
1428 	d->typingNotifyTask->setParams( contact, ( typing ? TypingNotifyTask::Begin : TypingNotifyTask::Finished ) );
1429 	d->typingNotifyTask->go(); 	// don't delete the task after sending
1430 }
1431 
connectToIconServer()1432 void Client::connectToIconServer()
1433 {
1434 	Connection* c = d->connections.connectionForFamily( 0x0010 );
1435 	if ( c )
1436 		return;
1437 
1438 	requestServerRedirect( 0x0010 );
1439 }
1440 
setIgnore(const QString & user,bool ignore)1441 void Client::setIgnore( const QString& user, bool ignore )
1442 {
1443 	OContact item = ssiManager()->findItem( user,  ROSTER_IGNORE );
1444 	if ( item && !ignore )
1445 	{
1446 		kDebug(OSCAR_RAW_DEBUG) << "Removing " << user << " from ignore list";
1447 		this->modifyContactItem( item, OContact() );
1448 	}
1449 	else if ( !item && ignore )
1450 	{
1451 		kDebug(OSCAR_RAW_DEBUG) << "Adding " << user << " to ignore list";
1452 		OContact s( user, 0, ssiManager()->nextContactId(), ROSTER_IGNORE, QList<TLV>() );
1453 		this->modifyContactItem( OContact(), s );
1454 	}
1455 }
1456 
setVisibleTo(const QString & user,bool visible)1457 void Client::setVisibleTo( const QString& user, bool visible )
1458 {
1459 	OContact item = ssiManager()->findItem( user,  ROSTER_VISIBLE );
1460 	if ( item && !visible )
1461 	{
1462 		kDebug(OSCAR_RAW_DEBUG) << "Removing " << user << " from visible list";
1463 		this->modifyContactItem( item, OContact() );
1464 	}
1465 	else if ( !item && visible )
1466 	{
1467 		kDebug(OSCAR_RAW_DEBUG) << "Adding " << user << " to visible list";
1468 		OContact s( user, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE, QList<TLV>() );
1469 		this->modifyContactItem( OContact(), s );
1470 	}
1471 }
1472 
setInvisibleTo(const QString & user,bool invisible)1473 void Client::setInvisibleTo( const QString& user, bool invisible )
1474 {
1475 	OContact item = ssiManager()->findItem( user,  ROSTER_INVISIBLE );
1476 	if ( item && !invisible )
1477 	{
1478 		kDebug(OSCAR_RAW_DEBUG) << "Removing " << user << " from invisible list";
1479 		this->modifyContactItem( item, OContact() );
1480 	}
1481 	else if ( !item && invisible )
1482 	{
1483 		kDebug(OSCAR_RAW_DEBUG) << "Adding " << user << " to invisible list";
1484 		OContact s( user, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE, QList<TLV>() );
1485 		this->modifyContactItem( OContact(), s );
1486 	}
1487 }
1488 
requestBuddyIcon(const QString & user,const QByteArray & hash,Oscar::WORD iconType,Oscar::BYTE hashType)1489 void Client::requestBuddyIcon( const QString& user, const QByteArray& hash, Oscar::WORD iconType, Oscar::BYTE hashType )
1490 {
1491 	Connection* c = d->connections.connectionForFamily( 0x0010 );
1492 	if ( !c )
1493 		return;
1494 
1495 	BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
1496 	connect( bit, SIGNAL(haveIcon(QString,QByteArray)),
1497 	         this, SIGNAL(haveIconForContact(QString,QByteArray)) );
1498 	bit->requestIconFor( user );
1499 	bit->setIconType( iconType );
1500 	bit->setHashType( hashType );
1501 	bit->setHash( hash );
1502 	bit->go( Task::AutoDelete );
1503 }
1504 
requestServerRedirect(Oscar::WORD family,Oscar::WORD exchange,QByteArray cookie,Oscar::WORD instance,const QString & room)1505 void Client::requestServerRedirect( Oscar::WORD family, Oscar::WORD exchange,
1506                                     QByteArray cookie, Oscar::WORD instance,
1507                                     const QString& room )
1508 {
1509 	//making the assumption that family 2 will always be the BOS connection
1510 	//use it instead since we can't query for family 1
1511 	Connection* c = d->connections.connectionForFamily( family );
1512 	if ( c && family != 0x000E )
1513 		return; //we already have the connection
1514 
1515 	c = d->connections.connectionForFamily( 0x0002 );
1516 	if ( !c )
1517 		return;
1518 
1519     if ( d->redirectionServices.indexOf( family ) == -1 )
1520         d->redirectionServices.append( family ); //don't add families twice
1521 
1522     if ( d->currentRedirect != 0 )
1523         return; //we're already doing one redirection
1524 
1525 	d->currentRedirect = family;
1526 
1527     //FIXME. this won't work if we have to defer the connection because we're
1528     //already connecting to something
1529 	ServerRedirectTask* srt = new ServerRedirectTask( c->rootTask() );
1530     if ( family == 0x000E )
1531     {
1532         srt->setChatParams( exchange, cookie, instance );
1533         srt->setChatRoom( room );
1534     }
1535 
1536 	connect( srt, SIGNAL(haveServer(QString,QByteArray,Oscar::WORD)),
1537 	         this, SLOT(haveServerForRedirect(QString,QByteArray,Oscar::WORD)) );
1538 	srt->setService( family );
1539 	srt->go( Task::AutoDelete );
1540 }
1541 
haveServerForRedirect(const QString & host,const QByteArray & cookie,Oscar::WORD)1542 void Client::haveServerForRedirect( const QString& host, const QByteArray& cookie, Oscar::WORD )
1543 {
1544     //nasty sender() usage to get the task with chat room info
1545 	QObject* o = const_cast<QObject*>( sender() );
1546     ServerRedirectTask* srt = dynamic_cast<ServerRedirectTask*>( o );
1547 
1548 	//create a new connection and set it up
1549 	int colonPos = host.indexOf(':');
1550 	QString realHost;
1551 	uint realPort;
1552 	if ( colonPos != -1 )
1553 	{
1554 		realHost = host.left( colonPos );
1555 		realPort = host.rightRef(4).toUInt(); //we only need 4 bytes
1556 	}
1557 	else
1558 	{
1559 		realHost = host;
1560 		realPort = d->port;
1561 	}
1562 
1563 	bool encrypted = d->encrypted2;
1564 
1565 	Connection* c = createConnection();
1566 	//create the new login task
1567 	d->loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
1568 	d->loginTaskTwo->setCookie( cookie );
1569 	QObject::connect( d->loginTaskTwo, SIGNAL(finished()), this, SLOT(serverRedirectFinished()) );
1570 
1571 	//connect
1572 	connectToServer( c, realHost, realPort, encrypted, QString() );
1573   	QObject::connect( c, SIGNAL(connected()), this, SLOT(streamConnected()) );
1574 
1575     if ( srt )
1576         d->connections.addChatInfoForConnection( c, srt->chatExchange(), srt->chatRoomName() );
1577 }
1578 
serverRedirectFinished()1579 void Client::serverRedirectFinished()
1580 {
1581 	StageTwoLoginTask* loginTaskTwo = qobject_cast<StageTwoLoginTask*>( sender() );
1582 
1583 	if ( loginTaskTwo && loginTaskTwo->statusCode() == 0 )
1584 	{ //stage two was successful
1585 		Connection* c = d->connections.connectionForFamily( d->currentRedirect );
1586 		if ( !c )
1587 			return;
1588 		ClientReadyTask* crt = new ClientReadyTask( c->rootTask() );
1589 		crt->setFamilies( c->supportedFamilies() );
1590 		crt->go( Task::AutoDelete );
1591 	}
1592 
1593 	kDebug(OSCAR_RAW_DEBUG) << "redirection finished for service "
1594 	                         << d->currentRedirect << endl;
1595 
1596 	if ( d->currentRedirect == 0x0010 )
1597 		emit iconServerConnected();
1598 
1599 	if ( d->currentRedirect == 0x000D )
1600 	{
1601 		connect( this, SIGNAL(chatNavigationConnected()),
1602 				 this, SLOT(requestChatNavLimits()) );
1603 		emit chatNavigationConnected();
1604 	}
1605 
1606 	if ( d->currentRedirect == 0x000E )
1607 	{
1608 		//HACK! such abuse! think of a better way
1609 		if ( !loginTaskTwo )
1610 		{
1611 			kWarning(OSCAR_RAW_DEBUG) << "no login task to get connection from!";
1612 			emit redirectionFinished( d->currentRedirect );
1613 			return;
1614 		}
1615 
1616 		Connection* c = loginTaskTwo->client();
1617 		QString roomName = d->connections.chatRoomForConnection( c );
1618 		Oscar::WORD exchange = d->connections.exchangeForConnection( c );
1619 		if ( c )
1620 		{
1621 			kDebug(OSCAR_RAW_DEBUG) << "setting up chat connection";
1622 			ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), exchange, roomName );
1623 			connect( cst, SIGNAL(userJoinedChat(Oscar::WORD,QString,QString)),
1624 			         this, SIGNAL(userJoinedChat(Oscar::WORD,QString,QString)) );
1625 			connect( cst, SIGNAL(userLeftChat(Oscar::WORD,QString,QString)),
1626 			         this, SIGNAL(userLeftChat(Oscar::WORD,QString,QString)) );
1627 			connect( cst, SIGNAL(newChatMessage(Oscar::Message)),
1628 			         this, SIGNAL(messageReceived(Oscar::Message)) );
1629 		}
1630 		emit chatRoomConnected( exchange, roomName );
1631 	}
1632 
1633 	emit redirectionFinished( d->currentRedirect );
1634 
1635 }
1636 
checkRedirectionQueue(Oscar::WORD family)1637 void Client::checkRedirectionQueue( Oscar::WORD family )
1638 {
1639 	kDebug(OSCAR_RAW_DEBUG) << "checking redirection queue";
1640 	d->redirectionServices.removeAll( family );
1641     d->currentRedirect = 0;
1642 	if ( !d->redirectionServices.isEmpty() )
1643 	{
1644 		kDebug(OSCAR_RAW_DEBUG) << "scheduling new redirection";
1645 		requestServerRedirect( d->redirectionServices.front() );
1646 	}
1647 }
1648 
requestChatNavLimits()1649 void Client::requestChatNavLimits()
1650 {
1651 	Connection* c = d->connections.connectionForFamily( 0x000D );
1652 	if ( !c )
1653 		return;
1654 
1655 	kDebug(OSCAR_RAW_DEBUG) << "requesting chat nav service limits";
1656 	ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
1657     cnst->setRequestType( ChatNavServiceTask::Limits );
1658     QObject::connect( cnst, SIGNAL(haveChatExchanges(QList<int>)),
1659                       this, SLOT(setChatExchangeList(QList<int>)) );
1660 	cnst->go( Task::AutoDelete ); //autodelete
1661 
1662 }
1663 
determineDisconnection(int code,const QString & string)1664 void Client::determineDisconnection( int code, const QString& string )
1665 {
1666     if ( !sender() )
1667         return;
1668 
1669     //yay for the sender() hack!
1670     QObject* obj = const_cast<QObject*>( sender() );
1671     Connection* c = dynamic_cast<Connection*>( obj );
1672     if ( !c )
1673         return;
1674 
1675 	if ( c->isSupported( 0x0002 ) ||
1676 	     d->stage == ClientPrivate::StageOne ) //emit on login
1677 	{
1678 		emit socketError( code, string );
1679 	}
1680 
1681 	foreach ( Oscar::MessageInfo info, c->messageInfoList() )
1682 		emit messageError( info.contact, info.id );
1683 
1684     //connection is deleted. deleteLater() is used
1685     d->connections.remove( c );
1686     c = 0;
1687 }
1688 
sendBuddyIcon(const QByteArray & iconData)1689 void Client::sendBuddyIcon( const QByteArray& iconData )
1690 {
1691 	Connection* c = d->connections.connectionForFamily( 0x0010 );
1692 	if ( !c )
1693 		return;
1694 
1695 	kDebug(OSCAR_RAW_DEBUG) << "icon length is " << iconData.size();
1696 	BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
1697 	bit->uploadIcon( iconData.size(), iconData );
1698 	bit->go( Task::AutoDelete );
1699 }
1700 
joinChatRoom(const QString & roomName,int exchange)1701 void Client::joinChatRoom( const QString& roomName, int exchange )
1702 {
1703     Connection* c = d->connections.connectionForFamily( 0x000D );
1704     if ( !c )
1705         return;
1706 
1707     kDebug(OSCAR_RAW_DEBUG) << "joining the chat room '" << roomName
1708                              << "' on exchange " << exchange << endl;
1709     ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
1710     connect( cnst, SIGNAL(connectChat(Oscar::WORD,QByteArray,Oscar::WORD,QString)),
1711              this, SLOT(setupChatConnection(Oscar::WORD,QByteArray,Oscar::WORD,QString)) );
1712     cnst->createRoom( exchange, roomName );
1713 
1714 }
1715 
setupChatConnection(Oscar::WORD exchange,QByteArray cookie,Oscar::WORD instance,const QString & room)1716 void Client::setupChatConnection( Oscar::WORD exchange, QByteArray cookie, Oscar::WORD instance, const QString& room )
1717 {
1718     kDebug(OSCAR_RAW_DEBUG) << "cookie is:" << cookie;
1719     QByteArray realCookie( cookie );
1720     kDebug(OSCAR_RAW_DEBUG) << "connection to chat room";
1721     requestServerRedirect( 0x000E, exchange, realCookie, instance, room );
1722 }
1723 
disconnectChatRoom(Oscar::WORD exchange,const QString & room)1724 void Client::disconnectChatRoom( Oscar::WORD exchange, const QString& room )
1725 {
1726     Connection* c = d->connections.connectionForChatRoom( exchange, room );
1727     if ( !c )
1728         return;
1729 
1730     d->connections.remove( c );
1731     c = 0;
1732 }
1733 
connectToServer(Connection * c,const QString & host,quint16 port,bool encrypted,const QString & name)1734 void Client::connectToServer( Connection* c, const QString& host, quint16 port, bool encrypted, const QString &name )
1735 {
1736 	d->connections.append( c );
1737 	connect( c, SIGNAL(socketError(int,QString)), this, SLOT(determineDisconnection(int,QString)) );
1738 	c->connectToServer( host, port, encrypted, name );
1739 }
1740 
createClientStream()1741 ClientStream* Client::createClientStream()
1742 {
1743 	ClientStream* cs = 0;
1744 	emit createClientStream( &cs );
1745 	if ( !cs )
1746 		cs = new ClientStream( new QSslSocket(), 0 );
1747 
1748 	return cs;
1749 }
1750 
createConnection()1751 Connection* Client::createConnection()
1752 {
1753 	ClientStream* cs = createClientStream();
1754 	cs->setNoopTime( 60000 );
1755 	Connection* c = new Connection( cs, "BOS" );
1756 	cs->setConnection( c );
1757 	c->setClient( this );
1758 	return c;
1759 }
1760 
deleteStaticTasks()1761 void Client::deleteStaticTasks()
1762 {
1763 	delete d->errorTask;
1764 	delete d->onlineNotifier;
1765 	delete d->ownStatusTask;
1766 	delete d->messageReceiverTask;
1767 	delete d->messageAckTask;
1768 	delete d->ssiAuthTask;
1769 	delete d->icqInfoTask;
1770 	delete d->icqTlvInfoTask;
1771 	delete d->userInfoTask;
1772 	delete d->typingNotifyTask;
1773 	delete d->ssiModifyTask;
1774 
1775 	d->errorTask = 0;
1776 	d->onlineNotifier = 0;
1777 	d->ownStatusTask = 0;
1778 	d->messageReceiverTask = 0;
1779 	d->messageAckTask = 0;
1780 	d->ssiAuthTask = 0;
1781 	d->icqInfoTask = 0;
1782 	d->icqTlvInfoTask = 0;
1783 	d->userInfoTask = 0;
1784 	d->typingNotifyTask = 0;
1785 	d->ssiModifyTask = 0;
1786 }
1787 
hasIconConnection() const1788 bool Client::hasIconConnection( ) const
1789 {
1790 	Connection* c = d->connections.connectionForFamily( 0x0010 );
1791 	return c;
1792 }
1793 
createFileTransfer(const QString & contact,const QStringList & files)1794 FileTransferHandler* Client::createFileTransfer( const QString& contact, const QStringList& files )
1795 {
1796 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1797 	if ( !c )
1798 		return 0;
1799 
1800 	FileTransferTask *ft = new FileTransferTask( c->rootTask(), contact, ourInfo().userId(), files );
1801 	connect( ft, SIGNAL(sendMessage(Oscar::Message)),
1802 	         this, SLOT(fileMessage(Oscar::Message)) );
1803 
1804 	return new FileTransferHandler(ft);
1805 }
1806 
inviteToChatRoom(const QString & contact,Oscar::WORD exchange,const QString & room,QString msg)1807 void Client::inviteToChatRoom( const QString& contact, Oscar::WORD exchange, const QString& room, QString msg)
1808 {
1809 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1810 	ChatRoomTask *chatRoomTask = new ChatRoomTask( c->rootTask(), contact, ourInfo().userId(), msg, exchange, room);
1811 	chatRoomTask->go( Task::AutoDelete );
1812 	chatRoomTask->doInvite();
1813 }
1814 
gotChatRoomMessage(const Oscar::Message & msg,const QByteArray & cookie)1815 void Client::gotChatRoomMessage( const Oscar::Message & msg, const QByteArray & cookie )
1816 {
1817 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1818 	if ( msg.requestType() == 0 )
1819 	{
1820 		ChatRoomTask *task = new ChatRoomTask( c->rootTask(), msg.sender(), msg.receiver(), cookie, msg.text( QTextCodec::codecForName( "UTF-8" ) ), msg.exchange(), msg.chatRoom() );
1821 		task->go( Task::AutoDelete );
1822 		emit chatroomRequest( new ChatRoomHandler( task ) );
1823 	}
1824 }
1825 
gotFileMessage(int type,const QString from,const QByteArray cookie,Buffer buf)1826 void Client::gotFileMessage( int type, const QString from, const QByteArray cookie, Buffer buf)
1827 {
1828 	Connection* c = d->connections.connectionForFamily( 0x0004 );
1829 	if ( !c )
1830 		return;
1831 	//pass the message to the matching task if we can
1832 	const QList<FileTransferTask*> p = c->rootTask()->findChildren<FileTransferTask*>();
1833 	foreach( FileTransferTask *t, p)
1834 	{
1835 		if ( t->take( type, cookie, buf ) )
1836 		{
1837 			return;
1838 		}
1839 	}
1840 	//maybe it's a new request!
1841 	if ( type == 0 )
1842 	{
1843 		kDebug(14151) << "new request :)";
1844 		FileTransferTask *ft = new FileTransferTask( c->rootTask(), from, ourInfo().userId(), cookie, buf );
1845 		connect( ft, SIGNAL(sendMessage(Oscar::Message)),
1846 				this, SLOT(fileMessage(Oscar::Message)) );
1847 		ft->go( Task::AutoDelete );
1848 
1849 		emit incomingFileTransfer( new FileTransferHandler(ft) );
1850 	}
1851 
1852 	kDebug(14151) << "nobody wants it :(";
1853 }
1854 
1855 }
1856 
1857