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