1
// vim: set noet ts=4 sts=4 sw=4 :
2
// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
4
// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
5
// Copyright (C) 2002 Zack Rusin <zack@kde.org>
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.
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.
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
27
#include "gadusession.h"
31
#include "kopetemessage.h"
33
#include <qsocketnotifier.h>
34
#include <qtextcodec.h>
35
#include <qdatetime.h>
38
#include "gadurichtextformat.h"
42
#include <netinet/in.h>
44
GaduSession::GaduSession( QObject* parent )
45
: QObject( parent ), session_( 0 ), searchSeqNr_( 0 ), deletingUserList( false )
47
textcodec = QTextCodec::codecForName( "Windows-1250" );
48
rtf = new GaduRichTextFormat;
51
GaduSession::~GaduSession()
57
GaduSession::isConnected() const
60
return ( session_->state & GG_STATE_CONNECTED );
66
GaduSession::status() const
69
kDebug(14100)<<"Status = " << session_->status <<", initial = "<< session_->initial_status;
70
return session_->status & ( ~GG_STATUS_FRIENDS_MASK );
72
return GG_STATUS_NOT_AVAIL;
76
GaduSession::login( struct gg_login_params* p )
78
if ( !isConnected() ) {
80
// turn on in case you have any problems, and you want
81
// to report it better. libgadu needs to be recompiled with debug enabled
82
// gg_debug_level=GG_DEBUG_MISC|GG_DEBUG_FUNCTION;
84
kDebug(14100) << "Login";
86
if ( !( session_ = gg_login( p ) ) ) {
88
kDebug( 14100 ) << "libgadu internal error ";
89
emit connectionFailed( GG_FAILURE_CONNECTING );
93
createNotifiers( true );
94
enableNotifiers( session_->check );
100
GaduSession::destroyNotifiers()
110
GaduSession::createNotifiers( bool connect )
116
read_ = new QSocketNotifier( session_->fd, QSocketNotifier::Read, this );
117
read_->setEnabled( false );
119
write_ = new QSocketNotifier( session_->fd, QSocketNotifier::Write, this );
120
write_->setEnabled( false );
123
QObject::connect( read_, SIGNAL(activated(int)), SLOT(checkDescriptor()) );
124
QObject::connect( write_, SIGNAL(activated(int)), SLOT(checkDescriptor()) );
129
GaduSession::enableNotifiers( int checkWhat )
131
if( (checkWhat & GG_CHECK_READ) && read_ ) {
132
read_->setEnabled( true );
134
if( (checkWhat & GG_CHECK_WRITE) && write_ ) {
135
write_->setEnabled( true );
140
GaduSession::disableNotifiers()
143
read_->setEnabled( false );
146
write_->setEnabled( false );
151
GaduSession::dccRequest( const unsigned int uin )
154
gg_dcc_request( session_, uin );
159
GaduSession::login( KGaduLoginParams* loginp )
161
QByteArray desc = textcodec->fromUnicode( loginp->statusDescr );
163
memset( ¶ms_, 0, sizeof(params_) );
165
params_.status_descr = (char*)desc.data();
167
params_.uin = loginp->uin;
168
params_.password = loginp->password.data();
169
params_.status = loginp->status | ( loginp->forFriends ? GG_STATUS_FRIENDS_MASK : 0 );
171
params_.tls = loginp->useTls;
172
params_.server_addr = loginp->server;
173
params_.client_addr = loginp->client_addr;
174
params_.client_port = loginp->client_port;
176
kDebug(14100) << "LOGIN IP: " << loginp->client_addr;
178
if ( loginp->useTls ) {
179
params_.server_port = GG_HTTPS_PORT;
182
if ( loginp->server ) {
183
params_.server_port = GG_DEFAULT_PORT;
187
kDebug(14100)<<"gadusession::login, server ( " << loginp->server << " ), tls(" << loginp->useTls << ") ";
193
GaduSession::destroySession()
197
gg_free_session( session_ );
203
GaduSession::logoff( Kopete::Account::DisconnectReason reason )
206
emit disconnect( reason );
210
GaduSession::notify( uin_t* userlist, int count )
212
if ( isConnected() ) {
213
return gg_notify( session_, userlist, count );
216
emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
223
GaduSession::addNotify( uin_t uin )
225
if ( isConnected() ) {
226
return gg_add_notify( session_, uin );
229
emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
235
GaduSession::removeNotify( uin_t uin )
237
if ( isConnected() ) {
238
gg_remove_notify( session_, uin );
241
emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
248
GaduSession::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass )
252
KGaduMessage* gadumessage;
254
if ( isConnected() ) {
255
gadumessage = rtf->convertToGaduMessage( msg );
257
const void* data = (const void*)gadumessage->rtf.data();
258
cpMsg = textcodec->fromUnicode( gadumessage->message );
260
o = gg_send_message_richtext( session_, msgClass, recipient, (const unsigned char *)cpMsg.data(), (const unsigned char*) data, gadumessage->rtf.size() );
261
gadumessage->rtf.resize(0);
266
sendMsg = msg.plainBody();
267
sendMsg.replace( QLatin1Char( '\n' ), QString::fromAscii( "\r\n" ) );
268
cpMsg = textcodec->fromUnicode( sendMsg );
270
return gg_send_message( session_, msgClass, recipient, (const unsigned char *)cpMsg.data() );
274
emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
281
GaduSession::changeStatus( int status, bool forFriends )
283
kDebug(14101)<<"## Changing to "<<status;
284
if ( isConnected() ) {
285
return gg_change_status( session_, status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0) );
288
emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") );
295
GaduSession::changeStatusDescription( int status, const QString& descr, bool forFriends )
299
ndescr= textcodec->fromUnicode(descr);
301
if ( isConnected() ) {
302
return gg_change_status_descr( session_,
303
status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0), ndescr.data() );
306
emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") );
315
if ( isConnected() ) {
316
return gg_ping( session_ );
323
GaduSession::pubDirSearchClose()
329
GaduSession::getPersonalInformation()
331
gg_pubdir50_t searchRequest;
334
if ( isConnected() == false ) {
338
searchRequest = gg_pubdir50_new( GG_PUBDIR50_READ );
339
if ( !searchRequest ) {
343
seqNr = gg_pubdir50( session_, searchRequest );
344
gg_pubdir50_free( searchRequest );
350
GaduSession::publishPersonalInformation( ResLine& d )
358
r = gg_pubdir50_new( GG_PUBDIR50_WRITE );
360
if ( d.firstname.length() )
361
gg_pubdir50_add( r, GG_PUBDIR50_FIRSTNAME,
362
(const char *)((const char*)textcodec->fromUnicode( d.firstname ) ) );
363
if ( d.surname.length() )
364
gg_pubdir50_add( r, GG_PUBDIR50_LASTNAME,
365
(const char *)((const char*)textcodec->fromUnicode( d.surname ) ) );
366
if ( d.nickname.length() )
367
gg_pubdir50_add( r, GG_PUBDIR50_NICKNAME,
368
(const char *)((const char*)textcodec->fromUnicode( d.nickname ) ) );
369
if ( d.age.length() )
370
gg_pubdir50_add( r, GG_PUBDIR50_BIRTHYEAR,
371
(const char *)((const char*)textcodec->fromUnicode( d.age ) ) );
372
if ( d.city.length() )
373
gg_pubdir50_add( r, GG_PUBDIR50_CITY,
374
(const char *)((const char*)textcodec->fromUnicode( d.city ) ) );
375
if ( d.meiden.length() )
376
gg_pubdir50_add( r, GG_PUBDIR50_FAMILYNAME,
377
(const char *)((const char*)textcodec->fromUnicode( d.meiden ) ) );
378
if ( d.orgin.length() )
379
gg_pubdir50_add( r, GG_PUBDIR50_FAMILYCITY,
380
(const char *)((const char*)textcodec->fromUnicode( d.orgin ) ) );
381
if ( d.gender.length() == 1 )
382
gg_pubdir50_add( r, GG_PUBDIR50_GENDER,
383
(const char *)((const char*)textcodec->fromUnicode( d.gender ) ) );
385
gg_pubdir50( session_, r );
387
gg_pubdir50_free( r );
393
GaduSession::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive )
397
gg_pubdir50_t searchRequest;
403
searchRequest = gg_pubdir50_new( GG_PUBDIR50_SEARCH_REQUEST );
404
if ( !searchRequest ) {
408
if ( query.uin == 0 ) {
409
if (query.firstname.length()) {
410
gg_pubdir50_add( searchRequest, GG_PUBDIR50_FIRSTNAME,
411
(const char*)textcodec->fromUnicode( query.firstname ) );
413
if ( query.surname.length() ) {
414
gg_pubdir50_add( searchRequest, GG_PUBDIR50_LASTNAME,
415
(const char*)textcodec->fromUnicode( query.surname ) );
417
if ( query.nickname.length() ) {
418
gg_pubdir50_add( searchRequest, GG_PUBDIR50_NICKNAME,
419
(const char*)textcodec->fromUnicode( query.nickname ) );
421
if ( query.city.length() ) {
422
gg_pubdir50_add( searchRequest, GG_PUBDIR50_CITY,
423
(const char*)textcodec->fromUnicode( query.city ) );
425
if ( ageFrom || ageTo ) {
426
QString yearFrom = QString::number( QDate::currentDate().year() - ageFrom );
427
QString yearTo = QString::number( QDate::currentDate().year() - ageTo );
429
if ( ageFrom && ageTo ) {
430
gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
431
(const char*)textcodec->fromUnicode( yearFrom + ' ' + yearTo ) );
434
gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
435
(const char*)textcodec->fromUnicode( yearFrom ) );
438
gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
439
(const char*)textcodec->fromUnicode( yearTo ) );
443
if ( query.gender.length() == 1 ) {
444
gg_pubdir50_add( searchRequest, GG_PUBDIR50_GENDER,
445
(const char *)((const char*)textcodec->fromUnicode( query.gender ) ) );
449
gg_pubdir50_add( searchRequest, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE );
452
// otherwise we are looking only for one fellow with this nice UIN
454
gg_pubdir50_add( searchRequest, GG_PUBDIR50_UIN, QString::number( query.uin ).toAscii() );
457
gg_pubdir50_add( searchRequest, GG_PUBDIR50_START, QString::number( searchSeqNr_ ).toAscii() );
458
reqNr = gg_pubdir50( session_, searchRequest );
459
gg_pubdir50_free( searchRequest );
465
GaduSession::sendResult( gg_pubdir50_t result )
471
count = gg_pubdir50_count( result );
474
kDebug(14100) << "there was nothing found in public directory for requested details";
477
for ( i = 0; i < count; i++ ) {
478
resultLine.uin = QString( gg_pubdir50_get( result, i, GG_PUBDIR50_UIN ) ).toInt();
479
resultLine.firstname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FIRSTNAME ) );
480
resultLine.surname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_LASTNAME ) );
481
resultLine.nickname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_NICKNAME ) );
482
resultLine.age = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_BIRTHYEAR ) );
483
resultLine.city = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_CITY ) );
484
QString stat = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_STATUS ) );
485
resultLine.orgin = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYCITY ) );
486
resultLine.meiden = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYNAME ) );
487
resultLine.gender = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_GENDER ) );
489
resultLine.status = stat.toInt();
490
age = resultLine.age.toInt();
492
resultLine.age = QString::number( QDate::currentDate().year() - age );
495
resultLine.age.truncate( 0 );
497
sres.append( resultLine );
498
kDebug(14100) << "found line "<< resultLine.uin << ' ' << resultLine.firstname;
501
searchSeqNr_ = gg_pubdir50_next( result );
502
emit pubDirSearchResult( sres, gg_pubdir50_seq( result ) );
506
GaduSession::requestContacts()
508
if ( !session_ || session_->state != GG_STATE_CONNECTED ) {
509
kDebug(14100) <<" you need to be connected to send ";
513
if ( gg_userlist_request( session_, GG_USERLIST_GET, NULL ) == -1 ) {
514
kDebug(14100) <<" userlist export ERROR ";
517
kDebug( 14100 ) << "Contacts list import..started ";
521
GaduSession::exportContactsOnServer( GaduContactsList* contactsList )
525
if ( !session_ || session_->state != GG_STATE_CONNECTED ) {
526
kDebug( 14100 ) << "you need to connect to export Contacts list ";
529
if ( deletingUserList) {
530
kDebug( 14100 ) << "you are currently deleting list ";
533
plist = textcodec->fromUnicode( contactsList->asString() );
534
kDebug(14100) <<"--------------------userlists\n" << plist;
535
kDebug(14100) << "----------------------------";
536
if ( gg_userlist_request( session_, GG_USERLIST_PUT, plist.data() ) == -1 ) {
537
kDebug( 14100 ) << "export contact list failed ";
540
kDebug( 14100 ) << "Contacts list export..started ";
545
GaduSession::deleteContactsOnServer( )
548
if ( !session_ || session_->state != GG_STATE_CONNECTED ) {
549
kDebug( 14100 ) << "you need to connect to delete Contacts list ";
553
if ( gg_userlist_request( session_, GG_USERLIST_PUT, " " ) == -1 ) {
554
kDebug( 14100 ) << "delete contact list failed ";
557
deletingUserList=true;
558
kDebug( 14100 ) << "Contacts list delete... started ";
563
GaduSession::handleUserlist( gg_event* event )
566
switch( event->event.userlist.type ) {
567
case GG_USERLIST_GET_REPLY:
568
if ( event->event.userlist.reply ) {
569
ul = textcodec->toUnicode(event->event.userlist.reply);
570
kDebug( 14100 ) << "Got Contacts list OK ";
573
kDebug( 14100 ) << "Got Contacts list FAILED/EMPTY ";
574
// FIXME: send failed?
576
emit userListRecieved( ul );
579
case GG_USERLIST_PUT_REPLY:
580
if ( deletingUserList ) {
581
deletingUserList = false;
582
kDebug( 14100 ) << "Contacts list deleted OK ";
583
emit userListDeleted();
586
kDebug( 14100 ) << "Contacts list exported OK ";
587
emit userListExported();
595
GaduSession::stateDescription( int state )
599
return i18n( "idle" );
600
case GG_STATE_RESOLVING:
601
return i18n( "resolving host" );
602
case GG_STATE_CONNECTING:
603
return i18n( "connecting" );
604
case GG_STATE_READING_DATA:
605
return i18n( "reading data" );
607
return i18n( "error" );
608
case GG_STATE_CONNECTING_HUB:
609
return i18n( "connecting to hub" );
610
case GG_STATE_CONNECTING_GG:
611
return i18n( "connecting to server" );
612
case GG_STATE_READING_KEY:
613
return i18n( "retrieving key" );
614
case GG_STATE_READING_REPLY:
615
return i18n( "waiting for reply" );
616
case GG_STATE_CONNECTED:
617
return i18n( "connected" );
618
case GG_STATE_SENDING_QUERY:
619
return i18n( "sending query" );
620
case GG_STATE_READING_HEADER:
621
return i18n( "reading header" );
622
case GG_STATE_PARSING:
623
return i18n( "parsing data" );
625
return i18n( "done" );
626
case GG_STATE_TLS_NEGOTIATION:
627
return i18n( "TLS connection negotiation" );
629
return i18n( "unknown" );
633
GaduSession::errorDescription( int err )
636
case GG_ERROR_RESOLVING:
637
return i18n( "Resolving error." );
638
case GG_ERROR_CONNECTING:
639
return i18n( "Connecting error." );
640
case GG_ERROR_READING:
641
return i18n( "Reading error." );
642
case GG_ERROR_WRITING:
643
return i18n( "Writing error." );
645
return i18n( "Unknown error number %1." , err );
650
GaduSession::failureDescription( gg_failure_t f )
653
case GG_FAILURE_RESOLVING:
654
return i18n( "Unable to resolve server address. DNS failure." );
655
case GG_FAILURE_CONNECTING:
656
return i18n( "Unable to connect to server." );
657
case GG_FAILURE_INVALID:
658
return i18n( "Server sent incorrect data. Protocol error." );
659
case GG_FAILURE_READING:
660
return i18n( "Problem reading data from server." );
661
case GG_FAILURE_WRITING:
662
return i18n( "Problem sending data to server." );
663
case GG_FAILURE_PASSWORD:
664
return i18n( "Incorrect password." );
666
return QString::fromAscii( "404." );
668
return i18n( "Unable to connect over an encrypted channel.\nTry to turn off encryption support in the Gadu account settings, then reconnect." );
670
return i18n( "Unknown error number %1." , f );
675
GaduSession::notify60( gg_event* event )
677
KGaduNotify* gn = NULL;
680
if ( event->event.notify60[0].uin ) {
681
gn = new KGaduNotify;
687
for( n=0 ; event->event.notify60[n].uin ; n++ ) {
688
gn->contact_id = event->event.notify60[n].uin;
689
gn->status = event->event.notify60[n].status;
690
gn->remote_ip.setAddress( ntohl( event->event.notify60[n].remote_ip ) );
691
gn->remote_port = event->event.notify60[n].remote_port;
692
if ( event->event.notify60[n].remote_ip && gn->remote_port > 10 ) {
698
gn->version = event->event.notify60[n].version;
699
gn->image_size = event->event.notify60[n].image_size;
700
gn->description = textcodec->toUnicode( event->event.notify60[n].descr );
701
emit contactStatusChanged( gn );
707
GaduSession::checkDescriptor()
711
struct gg_event* event;
712
// struct gg_dcc* dccSock;
713
KGaduMessage gaduMessage;
714
KGaduNotify gaduNotify;
716
if ( !( event = gg_watch_fd( session_ ) ) ) {
717
kDebug(14100)<<"Connection was broken for some reason";
719
logoff( Kopete::Account::ConnectionReset );
723
// FD changed, recreate socket notifiers
724
if ( session_->state == GG_STATE_CONNECTING_HUB || session_->state == GG_STATE_CONNECTING_GG ) {
725
kDebug(14100)<<"recreating notifiers";
727
createNotifiers( true );
730
switch( event->type ) {
732
kDebug(14100) << "incoming message:class:" << event->event.msg.msgclass;
733
if ( event->event.msg.msgclass & GG_CLASS_CTCP ) {
734
kDebug( 14100 ) << "incoming ctcp ";
735
// TODO: DCC CONNECTION
736
emit incomingCtcp( event->event.msg.sender );
739
if ( (event->event.msg.msgclass & GG_CLASS_MSG) || (event->event.msg.msgclass & GG_CLASS_CHAT) ) {
740
gaduMessage.message =
741
textcodec->toUnicode((const char*)event->event.msg.message);
742
gaduMessage.sender_id = event->event.msg.sender;
743
gaduMessage.sendTime.setTime_t( event->event.msg.time );
744
gaduMessage.message = rtf->convertToHtml( gaduMessage.message, event->event.msg.formats_length, event->event.msg.formats );
745
emit messageReceived( &gaduMessage );
749
emit ackReceived( event->event.ack.recipient );
751
case GG_EVENT_STATUS:
752
gaduNotify.status = event->event.status.status;
753
gaduNotify.contact_id = event->event.status.uin;
754
if ( event->event.status.descr ) {
755
gaduNotify.description = textcodec->toUnicode( event->event.status.descr );
758
gaduNotify.description.clear();
760
gaduNotify.remote_port = 0;
761
gaduNotify.version = 0;
762
gaduNotify.image_size = 0;
764
gaduNotify.fileCap = false;
766
emit contactStatusChanged( &gaduNotify );
768
case GG_EVENT_STATUS60:
769
gaduNotify.status = event->event.status60.status;
770
gaduNotify.contact_id = event->event.status60.uin;
771
if ( event->event.status60.descr ) {
772
gaduNotify.description = textcodec->toUnicode( event->event.status60.descr );
775
gaduNotify.description.clear();
777
gaduNotify.remote_ip.setAddress( ntohl( event->event.status60.remote_ip ) );
778
gaduNotify.remote_port = event->event.status60.remote_port;
779
gaduNotify.version = event->event.status60.version;
780
gaduNotify.image_size = event->event.status60.image_size;
781
gaduNotify.time = event->event.status60.time;
782
if ( event->event.status60.remote_ip && gaduNotify.remote_port > 10 ) {
783
gaduNotify.fileCap = true;
786
gaduNotify.fileCap = false;
789
emit contactStatusChanged( &gaduNotify );
791
case GG_EVENT_NOTIFY60:
794
case GG_EVENT_CONN_SUCCESS:
795
kDebug(14100) << "success server: " << session_->server_addr;
796
emit connectionSucceed();
798
case GG_EVENT_CONN_FAILED:
799
kDebug(14100) << "failed server: " << session_->server_addr;
801
kDebug(14100) << "emit connection failed(" << event->event.failure << ") signal";
802
emit connectionFailed( (gg_failure_t)event->event.failure );
804
case GG_EVENT_DISCONNECT:
805
kDebug(14100)<<"event Disconnected";
806
// it should be called either when we requested disconnect, or when other client connects with our UID
807
logoff( Kopete::Account::Manual );
814
case GG_EVENT_PUBDIR50_SEARCH_REPLY:
815
case GG_EVENT_PUBDIR50_WRITE:
816
case GG_EVENT_PUBDIR50_READ:
817
sendResult( event->event.pubdir50 );
819
case GG_EVENT_USERLIST:
820
handleUserlist( event );
823
kDebug(14100)<<"Unprocessed GaduGadu Event = "<<event->type;
828
gg_free_event( event );
832
enableNotifiers( session_->check );
836
#include "gadusession.moc"