1// vim: set noet ts=4 sts=4 sw=4 :
2// -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
3//
4// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
5// Copyright (C) 2003 Zack Rusin <zack@kde.org>
6//
7// gaduaccount.cpp
8//
9// This program is free software; you can redistribute it and/or
10// modify it under the terms of the GNU General Public License
11// as published by the Free Software Foundation; either version 2
12// of the License, or (at your option) any later version.
13//
14// This program is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this program; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22// 02110-1301, USA
23
24#include "gaduaccount.h"
25#include "gaducontact.h"
26#include "gaduprotocol.h"
27#include "gaduaway.h"
28#include "gadupubdir.h"
29#include "gadudcc.h"
30#include "gadudcctransaction.h"
31
32#include "kopetemetacontact.h"
33#include "kopetecontactlist.h"
34#include "kopetegroup.h"
35#include "kopetepassword.h"
36#include "kopeteuiglobal.h"
37#include "kopeteglobal.h"
38
39#include <kpassworddialog.h>
40#include <kconfig.h>
41#include <kdebug.h>
42#include <kglobal.h>
43#include <klocale.h>
44#include <kmenu.h>
45#include <kmessagebox.h>
46#include <knotification.h>
47#include <ktemporaryfile.h>
48#include <kactionmenu.h>
49#include <ktoggleaction.h>
50#include <kio/netaccess.h>
51#include <kicon.h>
52
53#include <qapplication.h>
54#include <qdialog.h>
55#include <qtimer.h>
56#include <qtextcodec.h>
57#include <qtextstream.h>
58#include <qhostaddress.h>
59#include <QByteArray>
60#include <QList>
61#include <QPointer>
62
63#include <netinet/in.h>
64#include <kconfiggroup.h>
65
66class GaduAccountPrivate {
67
68public:
69 GaduAccountPrivate() {}
70
71 GaduSession* session_;
72 GaduDCC* gaduDcc_;
73
74 QTimer* pingTimer_;
75
76 QTextCodec* textcodec_;
77 KFileDialog* saveListDialog;
78 KFileDialog* loadListDialog;
79
80 KAction* searchAction;
81 KAction* listPutAction;
82 KAction* listGetAction;
83 KAction* listDeleteAction;
84 KAction* listToFileAction;
85 KAction* listFromFileAction;
86 KAction* friendsModeAction;
87
88
89 bool connectWithSSL;
90
91 int currentServer;
92 unsigned int serverIP;
93
94 QString lastDescription;
95 bool forFriends;
96 bool ignoreAnons;
97
98 QTimer* exportTimer_;
99 bool exportUserlist;
100 bool exportListMode;
101 bool importListMode;
102 KConfigGroup* config;
103 Kopete::OnlineStatus status;
104 QList<unsigned int> servers;
105 KGaduLoginParams loginInfo;
106};
107
108// 10s is enough ;p
109#define USERLISTEXPORT_TIMEOUT (10*1000)
110
111// FIXME: use dynamic cache please, i consider this as broken resolution of this problem
112static const char* const servers_ip[] = {
113 "217.17.41.88",
114 "217.17.41.85",
115 "217.17.45.143",
116 "217.17.45.144",
117 "217.17.45.145",
118 "217.17.45.146",
119 "217.17.45.147",
120 "217.17.41.82",
121 "217.17.41.83",
122 "217.17.41.84",
123 "217.17.41.86",
124 "217.17.41.87",
125 "217.17.41.92",
126 "217.17.41.93",
127 "217.17.45.133"
128};
129
130#define NUM_SERVERS (sizeof(servers_ip)/sizeof(char*))
131
132 GaduAccount::GaduAccount( Kopete::Protocol* parent, const QString& accountID )
133: Kopete::PasswordedAccount( parent, accountID, false )
134{
135 QHostAddress ip;
136 p = new GaduAccountPrivate;
137
138 p->pingTimer_ = NULL;
139 p->saveListDialog = NULL;
140 p->loadListDialog = NULL;
141 p->forFriends = false;
142
143 p->textcodec_ = QTextCodec::codecForName( "CP1250" );
144 p->session_ = new GaduSession( this );
145 p->session_->setObjectName( QLatin1String("GaduSession") );
146
147 setMyself( new GaduContact( accountId().toInt(), this, Kopete::ContactList::self()->myself() ) );
148
149 p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
150 p->lastDescription.clear();
151
152 for ( unsigned int i = 0; i < NUM_SERVERS ; i++ ) {
153 ip.setAddress( QString( servers_ip[i] ) );
154 p->servers.append( htonl( ip.toIPv4Address() ) );
155 kDebug( 14100 ) << "adding IP: " << p->servers[ i ] << " to cache";
156 }
157 p->currentServer = -1;
158 p->serverIP = 0;
159
160 // initialize KGaduLogin structure to default values
161 p->loginInfo.uin = accountId().toInt();
162 p->loginInfo.useTls = false;
163 p->loginInfo.status = GG_STATUS_AVAIL;
164 p->loginInfo.server = 0;
165 p->loginInfo.client_port = 0;
166 p->loginInfo.client_addr = 0;
167
168 p->pingTimer_ = new QTimer( this );
169 p->exportTimer_ = new QTimer( this );
170
171 p->gaduDcc_ = NULL;
172
173 p->config = configGroup();
174
175 p->exportUserlist=false;
176 p->exportListMode = loadExportListOnChange();
177 p->importListMode = loadImportListOnLogin();
178 p->ignoreAnons = ignoreAnons();
179 p->forFriends = loadFriendsMode();
180
181 initConnections();
182 initActions();
183
184 QString nick = p->config->readEntry( QString::fromAscii( "nickName" ), QString() );
185 if ( !nick.isNull() ) {
186 myself()->setNickName( nick );
187 }
188}
189
190GaduAccount::~GaduAccount()
191{
192 delete p;
193}
194
195void
196GaduAccount::initActions()
197{
198
199 p->searchAction = new KAction( i18n( "&Search for Friends" ), this );
200 QObject::connect( p->searchAction, SIGNAL(triggered(bool)), this, SLOT(search()) );
201
202 p->listPutAction = new KAction( i18n( "Export Contacts to Server" ), this );
203 p->listPutAction->setIcon ( KIcon ( "document-export" ) );
204 QObject::connect( p->listPutAction, SIGNAL(triggered(bool)), this, SLOT(slotExportContactsList()) );
205
206 p->listGetAction = new KAction( i18n( "Import Contacts from Server" ), this );
207 p->listGetAction->setIcon ( KIcon ( "document-import" ) );
208 QObject::connect( p->listGetAction, SIGNAL(triggered(bool)), this, SLOT(slotImportContactsList()) );
209
210 p->listDeleteAction = new KAction( i18n( "Delete Contacts from Server" ), this );
211 p->listDeleteAction->setIcon ( KIcon ( "document-close" ) );
212 QObject::connect( p->listDeleteAction, SIGNAL(triggered(bool)), this, SLOT(slotDeleteContactsList()) );
213
214 p->listToFileAction = new KAction( i18n( "Export Contacts to File..." ), this );
215 p->listToFileAction->setIcon ( KIcon ( "document-save" ) );
216 QObject::connect( p->listToFileAction, SIGNAL(triggered(bool)), this, SLOT(slotExportContactsListToFile()) );
217
218 p->listFromFileAction = new KAction( i18n( "Import Contacts from File..." ), this );
219 p->listFromFileAction->setIcon ( KIcon ( "document-open" ) );
220 QObject::connect( p->listFromFileAction, SIGNAL(triggered(bool)), this, SLOT(slotImportContactsFromFile()) );
221
222 p->friendsModeAction = new KToggleAction( i18n( "Only for Friends" ), this );
223 QObject::connect( p->friendsModeAction, SIGNAL(triggered(bool)), this, SLOT(slotFriendsMode()) );
224
225 static_cast<KToggleAction*>(p->friendsModeAction)->setChecked( p->forFriends );
226}
227
228void
229GaduAccount::initConnections()
230{
231 QObject::connect( p->session_, SIGNAL(error(QString,QString)),
232 SLOT(error(QString,QString)) );
233 QObject::connect( p->session_, SIGNAL(messageReceived(KGaduMessage*)),
234 SLOT(messageReceived(KGaduMessage*)) );
235 QObject::connect( p->session_, SIGNAL(contactStatusChanged(KGaduNotify*)),
236 SLOT(contactStatusChanged(KGaduNotify*)) );
237 QObject::connect( p->session_, SIGNAL(connectionFailed(gg_failure_t)),
238 SLOT(connectionFailed(gg_failure_t)) );
239 QObject::connect( p->session_, SIGNAL(connectionSucceed()),
240 SLOT(connectionSucceed()) );
241 QObject::connect( p->session_, SIGNAL(disconnect(Kopete::Account::DisconnectReason)),
242 SLOT(slotSessionDisconnect(Kopete::Account::DisconnectReason)) );
243 QObject::connect( p->session_, SIGNAL(ackReceived(uint)),
244 SLOT(ackReceived(uint)) );
245 QObject::connect( p->session_, SIGNAL(pubDirSearchResult(SearchResult,uint)),
246 SLOT(slotSearchResult(SearchResult,uint)) );
247 QObject::connect( p->session_, SIGNAL(userListExported()),
248 SLOT(userListExportDone()) );
249 QObject::connect( p->session_, SIGNAL(userListDeleted()),
250 SLOT(userListDeleteDone()) );
251 QObject::connect( p->session_, SIGNAL(userListRecieved(QString)),
252 SLOT(userlist(QString)) );
253 QObject::connect( p->session_, SIGNAL(incomingCtcp(uint)),
254 SLOT(slotIncomingDcc(uint)) );
255
256 QObject::connect( p->pingTimer_, SIGNAL(timeout()),
257 SLOT(pingServer()) );
258
259 QObject::connect( p->exportTimer_, SIGNAL(timeout()),
260 SLOT(slotUserlistSynch()) );
261}
262
263void
264GaduAccount::setAway( bool isAway, const QString& awayMessage )
265{
266 unsigned int currentStatus;
267
268 if ( isAway ) {
269 currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_BUSY : GG_STATUS_BUSY_DESCR;
270 }
271 else{
272 currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR;
273 }
274 changeStatus( GaduProtocol::protocol()->convertStatus( currentStatus ), awayMessage );
275}
276
277
278void
279GaduAccount::fillActionMenu( KActionMenu *actionMenu )
280{
281 kDebug(14100);
282
283 actionMenu->setIcon( myself()->onlineStatus().iconFor( this ) );
284 actionMenu->menu()->addTitle( myself()->onlineStatus().iconFor( myself() ), i18n( "%1 <%2> ",
285 myself()->displayName(), accountId() ) );
286
287 if ( p->session_->isConnected() ) {
288 p->searchAction->setEnabled( true );
289 p->listPutAction->setEnabled( true );
290 p->listGetAction->setEnabled( true );
291 p->listDeleteAction->setEnabled( true );
292 p->friendsModeAction->setEnabled( true );
293 }
294 else {
295 p->searchAction->setEnabled( false );
296 p->listPutAction->setEnabled( false );
297 p->listGetAction->setEnabled( false );
298 p->listDeleteAction->setEnabled( false );
299 p->friendsModeAction->setEnabled( false );
300 }
301
302 if ( !contacts().isEmpty() ) {
303 if ( p->saveListDialog ) {
304 p->listToFileAction->setEnabled( false );
305 }
306 else {
307 p->listToFileAction->setEnabled( true );
308 }
309
310 p->listToFileAction->setEnabled( true );
311 } else {
312 p->listPutAction->setEnabled( false );
313 p->listToFileAction->setEnabled( false );
314 }
315
316 if ( p->loadListDialog ) {
317 p->listFromFileAction->setEnabled( false );
318 }
319 else {
320 p->listFromFileAction->setEnabled( true );
321 }
322
323 KAction* action = new KAction(
324 KIcon(QIcon(GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ).iconFor( this ))),
325 i18n("Go O&nline"), this );
326 //, "actionGaduConnect" );
327 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(slotGoOnline()));
328 actionMenu->addAction( action );
329
330 action = new KAction(
331 KIcon(QIcon(GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ).iconFor( this ))),
332 i18n( "Set &Busy" ), this );
333 //, "actionGaduConnect" );
334 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(slotGoBusy()) );
335 actionMenu->addAction( action );
336
337 action = new KAction(
338 KIcon(QIcon(GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ).iconFor( this ))),
339 i18n( "Set &Invisible" ), this );
340 //, "actionGaduConnect" );
341 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(slotGoInvisible()) );
342 actionMenu->addAction( action );
343
344 action = new KAction(
345 KIcon(QIcon(GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ).iconFor( this ))),
346 i18n( "Go &Offline" ), this );
347 //, "actionGaduConnect" );
348 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(slotGoOffline()) );
349 actionMenu->addAction( action );
350
351 action = new KAction( KIcon("edit-rename"), i18n( "Set &Description..." ), this );
352 //, "actionGaduDescription" );
353 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(slotDescription()) );
354 actionMenu->addAction( action );
355 actionMenu->addAction( p->friendsModeAction );
356
357 actionMenu->addSeparator();
358
359 actionMenu->addAction( p->searchAction );
360
361 actionMenu->addSeparator();
362
363 KActionMenu *listMenuAction = new KActionMenu(
364 KIcon ( "user-identity" ),
365 i18n("Contacts"), this);
366
367 listMenuAction->addAction( p->listPutAction );
368 listMenuAction->addAction( p->listGetAction );
369 listMenuAction->addAction( p->listDeleteAction );
370 listMenuAction->addSeparator();
371 listMenuAction->addAction( p->listToFileAction );
372 listMenuAction->addAction( p->listFromFileAction );
373 listMenuAction->addSeparator();
374
375 action = new KToggleAction( i18n( "Export Contacts on change" ), this );
376 QObject::connect( action, SIGNAL(triggered(bool)), this, SLOT(setExportListOnChange(bool)));
377 static_cast<KToggleAction*>(action)->setChecked(p->exportListMode);
378
379 listMenuAction->addAction( action );
380
381 actionMenu->addAction( listMenuAction );
382
383 KAction *propertiesAction = new KAction( KIcon("configure"), i18n("Properties"), actionMenu );
384 QObject::connect( propertiesAction, SIGNAL(triggered(bool)), this, SLOT(editAccount()) );
385 actionMenu->addAction( propertiesAction );
386
387}
388
389bool
390GaduAccount::hasCustomStatusMenu() const
391{
392 return true;
393}
394
395void
396GaduAccount::connectWithPassword(const QString& password)
397{
398 if (password.isEmpty()) {
399 return;
400 }
401 if (isConnected ())
402 return;
403 // FIXME: add status description to this mechainsm, this is a hack now. libkopete design issue.
404 changeStatus( initialStatus(), p->lastDescription );
405}
406
407void
408GaduAccount::disconnect()
409{
410 disconnect( Manual );
411}
412
413void
414GaduAccount::disconnect( DisconnectReason reason )
415{
416 slotGoOffline();
417 p->connectWithSSL = true;
418 Kopete::Account::disconnected( reason );
419}
420
421void
422GaduAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const Kopete::StatusMessage &reason, const OnlineStatusOptions& /*options*/ )
423{
424 kDebug(14100) << "Called";
425 changeStatus( status, reason.message() );
426}
427
428void
429GaduAccount::setStatusMessage( const Kopete::StatusMessage& statusMessage )
430{
431 changeStatus( myself()->onlineStatus(), statusMessage.message() );
432}
433
434void
435GaduAccount::slotUserlistSynch()
436{
437 if ( !p->exportUserlist || p->exportListMode ) {
438 return;
439 }
440 p->exportUserlist = false;
441 kDebug(14100) << "userlist changed, exporting";
442 slotExportContactsList();
443}
444
445void
446GaduAccount::userlistChanged()
447{
448 p->exportUserlist = true;
449 p->exportTimer_->start( USERLISTEXPORT_TIMEOUT );
450}
451
452bool
453GaduAccount::createContact( const QString& contactId, Kopete::MetaContact* parentContact )
454{
455 kDebug(14100) << "createContact " << contactId;
456
457 bool ok=false;
458 uin_t uinNumber = contactId.toUInt(&ok);
459 if(!ok || uinNumber == 0) {
460 kDebug( 14100 ) << "Invalid GaduGadu number:" << contactId;
461 return false;
462 }
463
464 GaduContact* newContact = new GaduContact( uinNumber, this, parentContact );
465 newContact->setParentIdentity( accountId() );
466 addNotify( uinNumber );
467
468 userlistChanged();
469
470 return true;
471}
472
473void
474GaduAccount::changeStatus( const Kopete::OnlineStatus& status, const QString& descr )
475{
476 unsigned int ns;
477
478 kDebug(14100) << "##### change status #####";
479 kDebug(14100) << "### Status = " << p->session_->isConnected();
480 kDebug(14100) << "### Status description = \"" << descr << "\"";
481
482 // if change to not available, log off
483 if ( GG_S_NA( status.internalStatus() ) ) {
484 if ( !p->session_->isConnected() ) {
485 return;//already logged off
486 }
487 else {
488 if ( status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) {
489 if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 ) {
490 return;
491 }
492 }
493 }
494 p->session_->logoff();
495 dccOff();
496 }
497 else {
498 // if status is for no desc, but we get some desc, than convert it to status with desc
499 if (!descr.isEmpty() && !GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) {
500 // and rerun us again. This won't cause any recursive call, as both conversions are static
501 ns = GaduProtocol::protocol()->statusToWithDescription( status );
502 changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr );
503 return;
504 }
505
506 // well, if it's empty but we want to set status with desc, change it too
507 if (descr.isEmpty() && GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) {
508 ns = GaduProtocol::protocol()->statusToWithoutDescription( status );
509 changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr );
510 return;
511 }
512
513 if ( !p->session_->isConnected() ) {
514 if ( password().cachedValue().isEmpty() ) {
515 // FIXME: when status string added to connect(), use it here
516 p->lastDescription = descr;
517 connect( status/*, descr*/ );
518 return;
519 }
520
521 if ( useTls() != TLS_no ) {
522 p->connectWithSSL = true;
523 }
524 else {
525 p->connectWithSSL = false;
526 }
527 dccOn();
528 p->serverIP = 0;
529 p->currentServer = -1;
530 p->status = status;
531 kDebug(14100) << "#### Connecting..., tls option "<< (int)useTls() << " ";
532 p->lastDescription = descr;
533 slotLogin( status.internalStatus(), descr );
534 return;
535 }
536 else {
537 p->status = status;
538 if ( descr.isEmpty() ) {
539 if ( p->session_->changeStatus( status.internalStatus(), p->forFriends ) != 0 )
540 return;
541 }
542 else {
543 if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 )
544 return;
545 }
546 }
547 }
548
549 myself()->setOnlineStatus( status );
550 myself()->setStatusMessage( Kopete::StatusMessage(descr) );
551
552 if ( status.internalStatus() == GG_STATUS_NOT_AVAIL || status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) {
553 if ( p->pingTimer_ ){
554 p->pingTimer_->stop();
555 }
556 }
557 p->lastDescription = descr;
558}
559
560void
561GaduAccount::slotLogin( int status, const QString& dscr )
562{
563 p->lastDescription = dscr;
564
565 myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING ));
566 myself()->setStatusMessage( Kopete::StatusMessage(dscr) );
567
568 if ( !p->session_->isConnected() ) {
569 if ( password().cachedValue().isEmpty() ) {
570 connectionFailed( GG_FAILURE_PASSWORD );
571 }
572 else {
573 p->loginInfo.password = p->textcodec_->fromUnicode(password().cachedValue());
574 p->loginInfo.useTls = p->connectWithSSL;
575 p->loginInfo.status = status;
576 p->loginInfo.statusDescr = dscr;
577 p->loginInfo.forFriends = p->forFriends;
578 p->loginInfo.server = p->serverIP;
579 if ( dccEnabled() ) {
580 p->loginInfo.client_addr = gg_dcc_ip;
581 p->loginInfo.client_port = gg_dcc_port;
582 }
583 else {
584 p->loginInfo.client_addr = 0;
585 p->loginInfo.client_port = 0;
586 }
587 p->session_->login( &p->loginInfo );
588 }
589 }
590 else {
591 p->session_->changeStatus( status );
592 }
593}
594
595void
596GaduAccount::slotLogoff()
597{
598 if ( p->session_->isConnected() || p->status == GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING )) {
599 p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
600 changeStatus( p->status );
601 p->session_->logoff();
602 dccOff();
603 }
604}
605
606void
607GaduAccount::slotGoOnline()
608{
609 changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ) );
610}
611void
612GaduAccount::slotGoOffline()
613{
614 slotLogoff();
615 dccOff();
616}
617
618void
619GaduAccount::slotGoInvisible()
620{
621 changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ) );
622}
623
624void
625GaduAccount::slotGoBusy()
626{
627 changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ) );
628}
629
630void
631GaduAccount::removeContact( const GaduContact* c )
632{
633 if ( isConnected() ) {
634 const uin_t u = c->uin();
635 p->session_->removeNotify( u );
636 userlistChanged();
637 }
638}
639
640void
641GaduAccount::addNotify( uin_t uin )
642{
643 if ( p->session_->isConnected() ) {
644 p->session_->addNotify( uin );
645 }
646}
647
648void
649GaduAccount::notify( uin_t* userlist, int count )
650{
651 if ( p->session_->isConnected() ) {
652 p->session_->notify( userlist, count );
653 }
654}
655
656void
657GaduAccount::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass )
658{
659 if ( p->session_->isConnected() ) {
660 p->session_->sendMessage( recipient, msg, msgClass );
661 }
662}
663
664void
665GaduAccount::error( const QString& title, const QString& message )
666{
667 KMessageBox::error( Kopete::UI::Global::mainWidget(), title, message );
668}
669
670void
671GaduAccount::messageReceived( KGaduMessage* gaduMessage )
672{
673 GaduContact* contact = 0;
674 QList<Kopete::Contact*> contactsListTmp;
675
676 // FIXME:check for ignored users list
677
678 if ( gaduMessage->sender_id == 0 ) {
679 //system message, display them or not?
680 kDebug(14100) << "####" << " System Message " << gaduMessage->message;
681 return;
682 }
683
684 contact = static_cast<GaduContact*> ( contacts().value( QString::number( gaduMessage->sender_id ) ) );
685
686 if ( !contact ) {
687 if ( p->ignoreAnons == true ) {
688 return;
689 }
690
691 Kopete::MetaContact* metaContact = new Kopete::MetaContact ();
692 metaContact->setTemporary ( true );
693 contact = new GaduContact( gaduMessage->sender_id, this, metaContact );
694 Kopete::ContactList::self ()->addMetaContact( metaContact );
695 addNotify( gaduMessage->sender_id );
696 }
697
698 contactsListTmp.append( myself() );
699
700 Kopete::Message msg( contact, contactsListTmp );
701 msg.setTimestamp( gaduMessage->sendTime );
702 msg.setHtmlBody( gaduMessage->message );
703 msg.setDirection( Kopete::Message::Inbound );
704
705 contact->messageReceived( msg );
706}
707
708void
709GaduAccount::ackReceived( unsigned int recipient )
710{
711 GaduContact* contact;
712
713 contact = static_cast<GaduContact*> ( contacts().value( QString::number( recipient ) ) );
714 if ( contact ) {
715 kDebug(14100) << "####" << "Received an ACK from " << contact->uin();
716 contact->messageAck();
717 }
718 else {
719 kDebug(14100) << "####" << "Received an ACK from an unknown user : " << recipient;
720 }
721}
722
723void
724GaduAccount::contactStatusChanged( KGaduNotify* gaduNotify )
725{
726 kDebug(14100) << "####" << " contact's status changed, uin:" << gaduNotify->contact_id;
727
728 GaduContact* contact;
729
730 contact = static_cast<GaduContact*>( contacts().value( QString::number( gaduNotify->contact_id ) ) );
731 if( !contact ) {
732 kDebug(14100) << "Notify not in the list " << gaduNotify->contact_id;
733 return;
734 }
735
736 contact->changedStatus( gaduNotify );
737}
738
739void
740GaduAccount::pong()
741{
742 kDebug(14100) << "####" << " Pong...";
743}
744
745void
746GaduAccount::pingServer()
747{
748 kDebug(14100) << "####" << " Ping...";
749 p->session_->ping();
750}
751
752void
753GaduAccount::connectionFailed( gg_failure_t failure )
754{
755 bool tryReconnect = false;
756 QString pass;
757
758
759 switch (failure) {
760 case GG_FAILURE_PASSWORD:
761 password().setWrong();
762 // user pressed CANCEL
763 p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
764 myself()->setOnlineStatus( p->status );
765 disconnected( BadPassword );
766 return;
767 default:
768 if ( p->connectWithSSL ) {
769 if ( useTls() != TLS_only ) {
770 slotCommandDone( QString(), i18n( "connection using SSL was not possible, retrying without." ) );
771 kDebug( 14100 ) << "try without tls now";
772 p->connectWithSSL = false;
773 tryReconnect = true;
774 p->currentServer = -1;
775 p->serverIP = 0;
776 break;
777 }
778 }
779 else {
780 if ( p->currentServer == NUM_SERVERS - 1 ) {
781 p->serverIP = 0;
782 p->currentServer = -1;
783 kDebug(14100) << "trying : " << "IP from hub ";
784 }
785 else {
786 p->serverIP = p->servers[ ++p->currentServer ];
787 kDebug(14100) << "trying : " << p->currentServer << " IP " << p->serverIP;
788 tryReconnect = true;
789 }
790 }
791 break;
792 }
793
794 if ( tryReconnect ) {
795 slotLogin( p->status.internalStatus() , p->lastDescription );
796 }
797 else {
798 error( i18n( "unable to connect to the Gadu-Gadu server(\"%1\").", GaduSession::failureDescription( failure ) ),
799 i18n( "Connection Error" ) );
800 p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
801 myself()->setOnlineStatus( p->status );
802 disconnected( InvalidHost );
803 }
804}
805
806void
807GaduAccount::dccOn()
808{
809 if ( dccEnabled() ) {
810 if ( !p->gaduDcc_ ) {
811 p->gaduDcc_ = new GaduDCC( this );
812 }
813 kDebug( 14100 ) << " turn DCC on for " << accountId();
814 p->gaduDcc_->registerAccount( this );
815 p->loginInfo.client_port = p->gaduDcc_->listeingPort();
816 }
817}
818
819void
820GaduAccount::dccOff()
821{
822 if ( p->gaduDcc_ ) {
823 kDebug( 14100 ) << "destroying dcc in gaduaccount ";
824 delete p->gaduDcc_;
825 p->gaduDcc_ = NULL;
826 p->loginInfo.client_port = 0;
827 p->loginInfo.client_addr = 0;
828 }
829}
830
831void
832GaduAccount::slotIncomingDcc( unsigned int uin )
833{
834 GaduContact* contact;
835 GaduDCCTransaction* trans;
836
837 if ( !uin ) {
838 return;
839 }
840
841 contact = static_cast<GaduContact*>( contacts().value( QString::number( uin ) ) );
842
843 if ( !contact ) {
844 kDebug(14100) << "attempt to make dcc connection from unknown uin " << uin;
845 return;
846 }
847
848 // if incapabile to transfer files, forget about it.
849 if ( contact->contactPort() < 10 ) {
850 kDebug(14100) << "can't respond to " << uin << " request, his listeing port is too low";
851 return;
852 }
853
854 trans = new GaduDCCTransaction( p->gaduDcc_ );
855 if ( trans->setupIncoming( p->loginInfo.uin, contact ) == false ) {
856 delete trans;
857 }
858
859}
860
861void
862GaduAccount::connectionSucceed( )
863{
864 kDebug(14100) << "#### Gadu-Gadu connected! ";
865 p->status = GaduProtocol::protocol()->convertStatus( p->session_->status() );
866 myself()->setOnlineStatus( p->status );
867 myself()->setStatusMessage( Kopete::StatusMessage(p->lastDescription) );
868 startNotify();
869
870
871 if ( p->importListMode ) {
872 p->session_->requestContacts();
873 }
874 p->pingTimer_->start( 3*60*1000 );//3 minute timeout
875 pingServer();
876
877 // check if we need to export userlist every USERLISTEXPORT_TIMEOUT ms
878 p->exportTimer_->start( USERLISTEXPORT_TIMEOUT );
879}
880
881void
882GaduAccount::startNotify()
883{
884 int i = 0;
885 if ( contacts().isEmpty() ) {
886 // we MUST send at least empty notify request to receive messages
887 p->session_->notify(NULL, 0);
888 return;
889 }
890
891 uin_t* userlist = 0;
892 userlist = new uin_t[ contacts().count() ];
893
894 QHashIterator<QString, Kopete::Contact*> it(contacts());
895 for( i=0 ; it.hasNext() ; ) {
896 it.next();
897 userlist[i++] = static_cast<GaduContact*> (it.value())->uin();
898 }
899
900 p->session_->notify( userlist, contacts().count() );
901 delete [] userlist;
902}
903
904void
905GaduAccount::slotSessionDisconnect( Kopete::Account::DisconnectReason reason )
906{
907 uin_t status;
908
909 kDebug(14100) << "Disconnecting";
910
911 if (p->pingTimer_) {
912 p->pingTimer_->stop();
913 }
914
915 setAllContactsStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) );
916
917 status = myself()->onlineStatus().internalStatus();
918 if ( status != GG_STATUS_NOT_AVAIL || status != GG_STATUS_NOT_AVAIL_DESCR ) {
919 myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) );
920 }
921 GaduAccount::disconnect( reason );
922}
923
924void
925GaduAccount::userlist( const QString& contactsListString )
926{
927 kDebug(14100)<<"### Got userlist - gadu account\n" << contactsListString;
928
929 GaduContactsList contactsList( contactsListString );
930 QString contactName;
931 QStringList groups;
932 GaduContact* contact;
933 Kopete::MetaContact* metaContact;
934 unsigned int i;
935
936 if ( contactsList.size() == 0 ) {
937 userListNotification(i18n("Imported contacts list is empty."));
938 return;
939 }
940
941 // don't export any new changes that were just imported :-)
942 p->exportTimer_->stop();
943 for ( i = 0; i != contactsList.size() ; i++ ) {
944 kDebug(14100) << "uin " << contactsList[i].uin;
945
946 if ( contactsList[i].uin.isEmpty() ) {
947 kDebug(14100) << "no Uin, strange.. ";
948 continue;
949 }
950
951 if ( contacts().value( contactsList[i].uin ) ) {
952 kDebug(14100) << "UIN already exists in contacts "<< contactsList[i].uin;
953 }
954 else {
955 kDebug(14100) << "Adding UIN";
956 contactName = GaduContact::findBestContactName( &contactsList[i] );
957 bool s = addContact( contactsList[i].uin, contactName, 0L, Kopete::Account::DontChangeKABC);
958 if ( s == false ) {
959 kDebug(14100) << "There was a problem adding UIN "<< contactsList[i].uin << "to users list";
960 continue;
961 }
962 }
963 contact = static_cast<GaduContact*>( contacts().value( contactsList[i].uin ) );
964 if ( contact == NULL ) {
965 kDebug(14100) << "oops, no Kopete::Contact in contacts()[] for some reason, for \"" << contactsList[i].uin << "\"";
966 continue;
967 }
968
969 // update/add infor for contact
970 contact->setContactDetails( &contactsList[i] );
971
972 if ( !( contactsList[i].group.isEmpty() ) ) {
973 // FIXME: libkopete bug i guess, by default contact goes to top level group
974 // if user desrired to see contact somewhere else, remove it from top level one
975 metaContact = contact->metaContact();
976 metaContact->removeFromGroup( Kopete::Group::topLevel() );
977 // put him in all desired groups:
978 groups = contactsList[i].group.split( ',', QString::SkipEmptyParts );
979 for ( QStringList::Iterator groupsIterator = groups.begin(); groupsIterator != groups.end(); ++groupsIterator ) {
980 metaContact->addToGroup( Kopete::ContactList::self ()->findGroup ( *groupsIterator) );
981 }
982 }
983 }
984 userListNotification(i18n("Contacts imported."));
985 // start to check if we need to export userlist
986 p->exportUserlist = false;
987 p->exportTimer_->start( USERLISTEXPORT_TIMEOUT );
988}
989
990void
991GaduAccount::userListExportDone()
992{
993 userListNotification(i18n( "Contacts exported.") );
994}
995
996void
997GaduAccount::userListDeleteDone()
998{
999 userListNotification(i18n( "Contacts deleted from the server.") );
1000}
1001
1002void
1003GaduAccount::userListNotification( QString what )
1004{
1005 if ( !isBusy() )
1006 KNotification::event( QString::fromLatin1("kopete_gadu_contactslist"), what, accountIcon());
1007}
1008
1009void
1010GaduAccount::slotFriendsMode()
1011{
1012 p->forFriends = !p->forFriends;
1013 kDebug( 14100 ) << "for friends mode: " << p->forFriends << " desc" << p->lastDescription;
1014 // now change status, it will changing it with p->forFriends flag
1015 changeStatus( p->status, p->lastDescription );
1016
1017 saveFriendsMode( p->forFriends );
1018
1019}
1020
1021// FIXME: make loading and saving nonblocking (at the moment KFileDialog stops plugin/kopete)
1022
1023void
1024GaduAccount::slotExportContactsListToFile()
1025{
1026 KTemporaryFile tempFile;
1027
1028 if ( p->saveListDialog ) {
1029 kDebug( 14100 ) << " save contacts to file: alread waiting for input ";
1030 return;
1031 }
1032
1033 p->saveListDialog = new KFileDialog( QString( "::kopete-gadu" + accountId() ), QString(),
1034 Kopete::UI::Global::mainWidget() );
1035 p->saveListDialog->setCaption(
1036 i18n("Save Contacts List for Account %1 As",
1037 myself()->displayName() ) );
1038
1039 if ( p->saveListDialog->exec() == QDialog::Accepted ) {
1040 QByteArray list = p->textcodec_->fromUnicode( userlist()->asString() );
1041
1042 if ( !tempFile.open() ) {
1043 // say cheese, can't create file.....
1044 error( i18n( "Unable to create temporary file." ), i18n( "Save Contacts List Failed" ) );
1045 }
1046 else {
1047 QTextStream tempStream ( &tempFile );
1048 tempStream << list.data();
1049 tempStream.flush();
1050
1051 bool res = KIO::NetAccess::upload(
1052 tempFile.fileName() ,
1053 p->saveListDialog->selectedUrl() ,
1054 Kopete::UI::Global::mainWidget()
1055 );
1056 if ( !res ) {
1057 // say it failed
1058 error( KIO::NetAccess::lastErrorString(), i18n( "Save Contacts List Failed" ) );
1059 }
1060 }
1061
1062 }
1063 delete p->saveListDialog;
1064 p->saveListDialog = NULL;
1065}
1066
1067void
1068GaduAccount::slotImportContactsFromFile()
1069{
1070 KUrl url;
1071 QByteArray list;
1072 QString oname;
1073
1074 if ( p->loadListDialog ) {
1075 kDebug( 14100 ) << "load contacts from file: alread waiting for input ";
1076 return;
1077 }
1078
1079 p->loadListDialog = new KFileDialog( QString( "::kopete-gadu" + accountId() ), QString(),
1080 Kopete::UI::Global::mainWidget() );
1081 p->loadListDialog->setCaption(
1082 i18n("Load Contacts List for Account %1 As",
1083 myself()->displayName() ) );
1084
1085 if ( p->loadListDialog->exec() == QDialog::Accepted ) {
1086 url = p->loadListDialog->selectedUrl();
1087 kDebug(14100) << "a:" << url << "\nb:" << oname;
1088 if ( KIO::NetAccess::download( url, oname, Kopete::UI::Global::mainWidget() ) ) {
1089 QFile tempFile( oname );
1090 if ( tempFile.open( QIODevice::ReadOnly ) ) {
1091 list = tempFile.readAll();
1092 tempFile.close();
1093 KIO::NetAccess::removeTempFile( oname );
1094 // and store it
1095 kDebug( 14100 ) << "loaded list:";
1096 kDebug( 14100 ) << list;
1097 kDebug( 14100 ) << " --------------- ";
1098 userlist( p->textcodec_->toUnicode( list ) );
1099 }
1100 else {
1101 error( tempFile.errorString(),
1102 i18n( "Contacts List Load Has Failed" ) );
1103 }
1104 }
1105 else {
1106 // say, it failed misourably
1107 error( KIO::NetAccess::lastErrorString(),
1108 i18n( "Contacts List Load Has Failed" ) );
1109 }
1110
1111 }
1112 delete p->loadListDialog;
1113 p->loadListDialog = NULL;
1114}
1115
1116unsigned int
1117GaduAccount::getPersonalInformation()
1118{
1119 return p->session_->getPersonalInformation();
1120}
1121
1122bool
1123GaduAccount::publishPersonalInformation( ResLine& d )
1124{
1125 return p->session_->publishPersonalInformation( d );
1126}
1127
1128void
1129GaduAccount::slotExportContactsList()
1130{
1131 p->session_->exportContactsOnServer( userlist() );
1132}
1133
1134void
1135GaduAccount::slotDeleteContactsList()
1136{
1137 p->session_->deleteContactsOnServer( );
1138}
1139
1140void
1141GaduAccount::slotImportContactsList()
1142{
1143 p->session_->requestContacts();
1144}
1145
1146
1147GaduContactsList*
1148GaduAccount::userlist()
1149{
1150 GaduContact* contact;
1151 GaduContactsList* contactsList = new GaduContactsList();
1152
1153 if ( contacts().isEmpty() ) {
1154 return contactsList;
1155 }
1156
1157 QHashIterator<QString, Kopete::Contact*> contactsIterator( contacts() );
1158
1159 for( ; contactsIterator.hasNext() ; ) {
1160 contactsIterator.next();
1161 contact = static_cast<GaduContact*>( contactsIterator.value() );
1162 contactsList->addContact( *contact->contactDetails() );
1163 }
1164
1165 return contactsList;
1166}
1167
1168void
1169GaduAccount::slotSearch( int uin )
1170{
1171 GaduPublicDir* dir = new GaduPublicDir( this, uin );
1172 dir->setObjectName( QLatin1String("GaduPublicDir") );
1173}
1174
1175void
1176GaduAccount::slotChangePassword()
1177{
1178}
1179
1180void
1181GaduAccount::slotCommandDone( const QString& /*title*/, const QString& what )
1182{
1183 if ( !isBusy() )
1184 KNotification::event( KNotification::Notification, what );
1185}
1186
1187void
1188GaduAccount::slotCommandError(const QString& title, const QString& what )
1189{
1190 error( title, what );
1191}
1192
1193void
1194GaduAccount::slotDescription()
1195{
1196 QPointer <GaduAway> away = new GaduAway( this );
1197
1198 if( away->exec() == QDialog::Accepted && away ) {
1199 changeStatus( GaduProtocol::protocol()->convertStatus( away->status() ),
1200 away->awayText() );
1201 }
1202 delete away;
1203}
1204
1205unsigned int
1206GaduAccount::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive )
1207{
1208 return p->session_->pubDirSearch( query, ageFrom, ageTo, onlyAlive );
1209}
1210
1211void
1212GaduAccount::pubDirSearchClose()
1213{
1214 p->session_->pubDirSearchClose();
1215}
1216
1217void
1218GaduAccount::slotSearchResult( const SearchResult& result, unsigned int seq )
1219{
1220 emit pubDirSearchResult( result, seq );
1221}
1222
1223void
1224GaduAccount::sendFile( GaduContact* peer, QString& filePath )
1225{
1226 GaduDCCTransaction* gtran = new GaduDCCTransaction( p->gaduDcc_ );
1227 gtran->setupOutgoing( peer, filePath );
1228}
1229
1230void
1231GaduAccount::dccRequest( GaduContact* peer )
1232{
1233 if ( peer && p->session_ ) {
1234 p->session_->dccRequest( peer->uin() );
1235 }
1236}
1237
1238// dcc settings
1239bool
1240GaduAccount::dccEnabled()
1241{
1242 QString s = p->config->readEntry( QString::fromAscii( "useDcc" ), QString() );
1243 kDebug( 14100 ) << "dccEnabled: " << s;
1244 if ( s == QString::fromAscii( "enabled" ) ) {
1245 return true;
1246 }
1247 return false;
1248}
1249
1250bool
1251GaduAccount::setDcc( bool d )
1252{
1253 QString s;
1254 bool f = true;
1255
1256 if ( d == false ) {
1257 dccOff();
1258 s = QString::fromAscii( "disabled" );
1259 }
1260 else {
1261 s = QString::fromAscii( "enabled" );
1262 }
1263
1264 p->config->writeEntry( QString::fromAscii( "useDcc" ), s );
1265
1266 if ( p->session_->isConnected() && d ) {
1267 dccOn();
1268 }
1269 kDebug( 14100 ) << "s: "<<s;
1270
1271 return f;
1272}
1273
1274void
1275GaduAccount::saveFriendsMode( bool i )
1276{
1277 p->config->writeEntry( QString::fromAscii( "forFriends" ),
1278 i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
1279}
1280
1281
1282bool
1283GaduAccount::loadFriendsMode()
1284{
1285 QString s;
1286 bool r;
1287 int n;
1288
1289 s = p->config->readEntry( QString::fromAscii( "forFriends" ), QString() );
1290 n = s.toInt( &r );
1291
1292 if ( n ) {
1293 return true;
1294 }
1295
1296 return false;
1297
1298}
1299
1300bool
1301GaduAccount::exportListOnChange()
1302{
1303 return p->exportListMode;
1304}
1305
1306void
1307GaduAccount::setExportListOnChange( bool i )
1308{
1309 p->exportListMode = i;
1310 p->config->writeEntry( QString::fromAscii( "exportListOnChange" ),
1311 i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
1312
1313 // stop timer and do not export until next change
1314 p->exportTimer_->stop();
1315 p->exportUserlist = false;
1316
1317}
1318
1319bool
1320GaduAccount::loadExportListOnChange()
1321{
1322 QString s;
1323 bool r;
1324 int n;
1325
1326 s = p->config->readEntry( QString::fromAscii( "exportListOnChange" ), QString("1") );
1327 n = s.toInt( &r );
1328
1329 if ( n ) {
1330 return true;
1331 }
1332
1333 return false;
1334}
1335
1336bool
1337GaduAccount::importListOnLogin()
1338{
1339 return p->importListMode;
1340}
1341
1342void
1343GaduAccount::setImportListOnLogin( bool i )
1344{
1345 p->importListMode = i;
1346 p->config->writeEntry( QString::fromAscii( "importListOnLogin" ),
1347 i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
1348}
1349
1350bool
1351GaduAccount::loadImportListOnLogin()
1352{
1353 QString s;
1354 bool r;
1355 int n;
1356
1357 s = p->config->readEntry( QString::fromAscii( "importListOnLogin" ), QString("1") );
1358 n = s.toInt( &r );
1359
1360 if ( n ) {
1361 return true;
1362 }
1363
1364 return false;
1365}
1366
1367
1368// might be bit inconsistent with what I used in DCC, but hell, it is so much easier to parse :-)
1369bool
1370GaduAccount::ignoreAnons()
1371{
1372 QString s;
1373 bool r;
1374 int n;
1375
1376 s = p->config->readEntry( QString( "ignoreAnons" ), QString() );
1377 n = s.toInt( &r );
1378
1379 if ( n ) {
1380 return true;
1381 }
1382
1383 return false;
1384
1385}
1386
1387void
1388GaduAccount::setIgnoreAnons( bool i )
1389{
1390 p->ignoreAnons = i;
1391 p->config->writeEntry( QString::fromAscii( "ignoreAnons" ),
1392 i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
1393}
1394
1395GaduAccount::tlsConnection
1396GaduAccount::useTls()
1397{
1398 QString s;
1399 bool c;
1400 unsigned int oldC;
1401 tlsConnection Tls;
1402
1403 s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ), QString() );
1404 oldC = s.toUInt( &c );
1405 // we have old format
1406 if ( c ) {
1407 kDebug( 14100 ) << "old format for param useEncryptedConnection, value " <<
1408 oldC << " will be converted to new string value" << endl;
1409 setUseTls( (tlsConnection) oldC );
1410 // should be string now, unless there was an error reading
1411 s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ), QString() );
1412 kDebug( 14100 ) << "new useEncryptedConnection value : " << s;
1413 }
1414
1415 Tls = TLS_no;
1416 if ( s == "TLS_ifAvaliable" ) {
1417 Tls = TLS_ifAvaliable;
1418 }
1419 if ( s == "TLS_only" ) {
1420 Tls = TLS_only;
1421 }
1422
1423 return Tls;
1424}
1425
1426void
1427GaduAccount::setUseTls( tlsConnection ut )
1428{
1429 QString s;
1430 switch( ut ) {
1431 case TLS_ifAvaliable:
1432 s = "TLS_ifAvaliable";
1433 break;
1434
1435 case TLS_only:
1436 s = "TLS_only";
1437 break;
1438
1439 default:
1440 case TLS_no:
1441 s = "TLS_no";
1442 break;
1443 }
1444
1445 p->config->writeEntry( QString::fromAscii( "useEncryptedConnection" ), s );
1446}
1447
1448#include "gaduaccount.moc"
1449