diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/gadu | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/gadu')
70 files changed, 18924 insertions, 0 deletions
diff --git a/kopete/protocols/gadu/Makefile.am b/kopete/protocols/gadu/Makefile.am new file mode 100644 index 00000000..d896950a --- /dev/null +++ b/kopete/protocols/gadu/Makefile.am @@ -0,0 +1,38 @@ +METASOURCES = AUTO +if include_libggcopy +LIBGADU_COPY=libgadu +GG_INCLUDES=-Ilibgadu -I$(srcdir)/libgadu $(SSL_INCLUDES) +GG_LIBS=$(top_builddir)/kopete/protocols/gadu/libgadu/libgadu_copy.la \ + $(LIBPTHREAD) +else +LIBGADU_COPY= +GG_INCLUDES=$(LIBGG_INCLUDES) +GG_LIBS=$(LIBGG_LIBS) +endif + + +SUBDIRS = ui icons $(LIBGADU_COPY) +AM_CPPFLAGS = -I$(srcdir)/ui \ + -I./ui \ + -I./lib \ + -I$(srcdir)/lib \ + $(KOPETE_INCLUDES) \ + $(all_includes) \ + $(GG_INCLUDES) + +kde_module_LTLIBRARIES = kopete_gadu.la + +kopete_gadu_la_SOURCES = gaduaway.cpp gadueditcontact.cpp gaducommands.cpp \ + gadueditaccount.cpp gadusession.cpp gaducontact.cpp \ + gaduaddcontactpage.cpp gaduprotocol.cpp gaduaccount.cpp \ + gadupubdir.cpp gaduregisteraccount.cpp \ + gaducontactlist.cpp gadurichtextformat.cpp \ + gadudccserver.cpp gadudcctransaction.cpp gadudcc.cpp + +kopete_gadu_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries) +kopete_gadu_la_LIBADD = ./ui/libgaduui.la \ + $(top_builddir)/kopete/libkopete/libkopete.la \ + $(GG_LIBS) + +service_DATA = kopete_gadu.desktop +servicedir = $(kde_servicesdir) diff --git a/kopete/protocols/gadu/README.gadu b/kopete/protocols/gadu/README.gadu new file mode 100644 index 00000000..6c7a929e --- /dev/null +++ b/kopete/protocols/gadu/README.gadu @@ -0,0 +1,43 @@ + +GaduGadu is primarily used and created for Poles, but it does not mean that it +cannot be used by anyone else. There is only one small issue, by design +protocol uses cp1250 encoding. Unfortunately, there's nothing we can do about +that, as it's needed to maintain compatibility with the Windows client, which +is the only one supported (and created) by the creators of gadu-gadu - sms- +express company. + +Gadu-Gadu plugin uses libgadu. If it's not avaliable in the system, +it will automaticaly switch to it's copy in /protocols/gadu/lib (v1.5). + +Following describes what to do if you still want to use external library version: + +You can download libgadu (part of ekg package) from http://dev.null.pl/ekg, +This should be version 1.5 (release candidate or stable), please stick with version +1.5 if possible. Author of libgadu/ekg does not keep any compatibility between +minor releases. It should support protocol version 6.0 by default. + +You have to download the ekg communicator package +(which is a gadugadu client for the console). +To install from sources, run: + +./configure --enable-shared --disable-static --enable-dynamic --with-pthread --prefix=/usr +make +(and as root) +make install. + +It is also available pre-packaged, e.g. for debian unstable, by default as +libgadu2 (binary) and libgadu-dev (development). +In order to compile kopete from sources, including the gadu-gadu plugin you +will need the libgadu headers and libraries installed and set up in your +system. + +Please refer INSTALL for information about building kopete from sources. + +If you're looking for more information, please refer to http://kopete.kde.org +and read the FAQ . + +If you have found an error in kopete, or in the gadu-gadu plugin +- please use the kde bug tracking system at http://bugs.kde.org/ + +Grzegorz Jaskiewicz aka Kain/K4 +gj AT pointblue DOT com DOT pl diff --git a/kopete/protocols/gadu/gaduaccount.cpp b/kopete/protocols/gadu/gaduaccount.cpp new file mode 100644 index 00000000..6dd737ce --- /dev/null +++ b/kopete/protocols/gadu/gaduaccount.cpp @@ -0,0 +1,1261 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2003 Zack Rusin <zack@kde.org> +// +// gaduaccount.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA + +#include "gaduaccount.h" +#include "gaducontact.h" +#include "gaduprotocol.h" +#include "gaduawayui.h" +#include "gaduaway.h" +#include "gadupubdir.h" +#include "gadudcc.h" +#include "gadudcctransaction.h" + +#include "kopetemetacontact.h" +#include "kopetecontactlist.h" +#include "kopetegroup.h" +#include "kopetepassword.h" +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" + +#include <kpassdlg.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <ktempfile.h> +#include <kio/netaccess.h> + +#include <qapplication.h> +#include <qdialog.h> +#include <qtimer.h> +#include <qtextcodec.h> +#include <qptrlist.h> +#include <qtextstream.h> +#include <qhostaddress.h> + +#include <netinet/in.h> + +class GaduAccountPrivate { + +public: + GaduAccountPrivate() {} + + GaduSession* session_; + GaduDCC* gaduDcc_; + + QTimer* pingTimer_; + + QTextCodec* textcodec_; + KFileDialog* saveListDialog; + KFileDialog* loadListDialog; + + KActionMenu* actionMenu_; + KAction* searchAction; + KAction* listputAction; + KAction* listToFileAction; + KAction* listFromFileAction; + KAction* friendsModeAction; + bool connectWithSSL; + + int currentServer; + unsigned int serverIP; + + QString lastDescription; + bool forFriends; + bool ignoreAnons; + + QTimer* exportTimer_; + bool exportUserlist; + + KConfigGroup* config; + Kopete::OnlineStatus status; + QValueList<unsigned int> servers; + KGaduLoginParams loginInfo; +}; + +// 10s is enough ;p +#define USERLISTEXPORT_TIMEOUT (10*1000) + +// FIXME: use dynamic cache please, i consider this as broken resolution of this problem +static const char* const servers_ip[] = { + "217.17.41.85", + "217.17.41.83", + "217.17.41.84", + "217.17.41.86", + "217.17.41.87", + "217.17.41.88", + "217.17.41.92", + "217.17.41.93", + "217.17.45.133", + "217.17.45.143", + "217.17.45.144" +}; + +#define NUM_SERVERS (sizeof(servers_ip)/sizeof(char*)) + + GaduAccount::GaduAccount( Kopete::Protocol* parent, const QString& accountID,const char* name ) +: Kopete::PasswordedAccount( parent, accountID, 0, name ) +{ + QHostAddress ip; + p = new GaduAccountPrivate; + + p->pingTimer_ = NULL; + p->saveListDialog = NULL; + p->loadListDialog = NULL; + p->forFriends = false; + + p->textcodec_ = QTextCodec::codecForName( "CP1250" ); + p->session_ = new GaduSession( this, "GaduSession" ); + + KGlobal::config()->setGroup( "Gadu" ); + + setMyself( new GaduContact( accountId().toInt(), accountId(), this, Kopete::ContactList::self()->myself() ) ); + + p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ); + p->lastDescription = QString::null; + + for ( unsigned int i = 0; i < NUM_SERVERS ; i++ ) { + ip.setAddress( QString( servers_ip[i] ) ); + p->servers.append( htonl( ip.toIPv4Address() ) ); + kdDebug( 14100 ) << "adding IP: " << p->servers[ i ] << " to cache" << endl; + } + p->currentServer = -1; + p->serverIP = 0; + + // initialize KGaduLogin structure to default values + p->loginInfo.uin = accountId().toInt(); + p->loginInfo.useTls = false; + p->loginInfo.status = GG_STATUS_AVAIL; + p->loginInfo.server = 0; + p->loginInfo.client_port = 0; + p->loginInfo.client_addr = 0; + + p->pingTimer_ = new QTimer( this ); + p->exportTimer_ = new QTimer( this ); + + p->exportUserlist = false; + p->gaduDcc_ = NULL; + + p->config = configGroup(); + + p->ignoreAnons = ignoreAnons(); + p->forFriends = loadFriendsMode(); + + initConnections(); + initActions(); + + QString nick = p->config->readEntry( QString::fromAscii( "nickName" ) ); + if ( !nick.isNull() ) { + myself()->setProperty( Kopete::Global::Properties::self()->nickName(), nick ); + } + else { + myself()->setProperty( Kopete::Global::Properties::self()->nickName(), accountId() ); + p->config->writeEntry( QString::fromAscii( "nickName" ), accountId() ); + } +} + +GaduAccount::~GaduAccount() +{ + delete p; +} + +void +GaduAccount::initActions() +{ + p->searchAction = new KAction( i18n( "&Search for Friends" ), "", 0, + this, SLOT( slotSearch() ), this, "actionSearch" ); + p->listputAction = new KAction( i18n( "Export Contacts to Server" ), "", 0, + this, SLOT( slotExportContactsList() ), this, "actionListput" ); + p->listToFileAction = new KAction( i18n( "Export Contacts to File..." ), "", 0, + this, SLOT( slotExportContactsListToFile() ), this, "actionListputFile" ); + p->listFromFileAction = new KAction( i18n( "Import Contacts From File..." ), "", 0, + this, SLOT( slotImportContactsFromFile() ), this, "actionListgetFile" ); + p->friendsModeAction = new KToggleAction( i18n( "Only for Friends" ), "", 0, + this, SLOT( slotFriendsMode() ), this, + "actionFriendsMode" ); + + static_cast<KToggleAction*>(p->friendsModeAction)->setChecked( p->forFriends ); +} + +void +GaduAccount::initConnections() +{ + QObject::connect( p->session_, SIGNAL( error( const QString&, const QString& ) ), + SLOT( error( const QString&, const QString& ) ) ); + QObject::connect( p->session_, SIGNAL( messageReceived( KGaduMessage* ) ), + SLOT( messageReceived( KGaduMessage* ) ) ); + QObject::connect( p->session_, SIGNAL( contactStatusChanged( KGaduNotify* ) ), + SLOT( contactStatusChanged( KGaduNotify* ) ) ); + QObject::connect( p->session_, SIGNAL( connectionFailed( gg_failure_t )), + SLOT( connectionFailed( gg_failure_t ) ) ); + QObject::connect( p->session_, SIGNAL( connectionSucceed( ) ), + SLOT( connectionSucceed( ) ) ); + QObject::connect( p->session_, SIGNAL( disconnect( Kopete::Account::DisconnectReason ) ), + SLOT( slotSessionDisconnect( Kopete::Account::DisconnectReason ) ) ); + QObject::connect( p->session_, SIGNAL( ackReceived( unsigned int ) ), + SLOT( ackReceived( unsigned int ) ) ); + QObject::connect( p->session_, SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ), + SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) ); + QObject::connect( p->session_, SIGNAL( userListExported() ), + SLOT( userListExportDone() ) ); + QObject::connect( p->session_, SIGNAL( userListRecieved( const QString& ) ), + SLOT( userlist( const QString& ) ) ); + QObject::connect( p->session_, SIGNAL( incomingCtcp( unsigned int ) ), + SLOT( slotIncomingDcc( unsigned int ) ) ); + + QObject::connect( p->pingTimer_, SIGNAL( timeout() ), + SLOT( pingServer() ) ); + + QObject::connect( p->exportTimer_, SIGNAL( timeout() ), + SLOT( slotUserlistSynch() ) ); +} + +void +GaduAccount::setAway( bool isAway, const QString& awayMessage ) +{ + unsigned int currentStatus; + + if ( isAway ) { + currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_BUSY : GG_STATUS_BUSY_DESCR; + } + else{ + currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR; + } + changeStatus( GaduProtocol::protocol()->convertStatus( currentStatus ), awayMessage ); +} + + +KActionMenu* +GaduAccount::actionMenu() +{ + kdDebug(14100) << "actionMenu() " << endl; + + p->actionMenu_ = new KActionMenu( accountId(), myself()->onlineStatus().iconFor( this ), this ); + p->actionMenu_->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ), i18n( "%1 <%2> " ). + arg( myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(), accountId() ) ); + + if ( p->session_->isConnected() ) { + p->searchAction->setEnabled( TRUE ); + p->listputAction->setEnabled( TRUE ); + p->friendsModeAction->setEnabled( TRUE ); + } + else { + p->searchAction->setEnabled( FALSE ); + p->listputAction->setEnabled( FALSE ); + p->friendsModeAction->setEnabled( FALSE ); + } + + if ( contacts().count() > 1 ) { + if ( p->saveListDialog ) { + p->listToFileAction->setEnabled( FALSE ); + } + else { + p->listToFileAction->setEnabled( TRUE ); + } + + p->listToFileAction->setEnabled( TRUE ); + } + else { + p->listToFileAction->setEnabled( FALSE ); + } + + if ( p->loadListDialog ) { + p->listFromFileAction->setEnabled( FALSE ); + } + else { + p->listFromFileAction->setEnabled( TRUE ); + } + p->actionMenu_->insert( new KAction( i18n( "Go O&nline" ), + GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ).iconFor( this ), + 0, this, SLOT( slotGoOnline() ), this, "actionGaduConnect" ) ); + + p->actionMenu_->insert( new KAction( i18n( "Set &Busy" ), + GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ).iconFor( this ), + 0, this, SLOT( slotGoBusy() ), this, "actionGaduConnect" ) ); + + p->actionMenu_->insert( new KAction( i18n( "Set &Invisible" ), + GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ).iconFor( this ), + 0, this, SLOT( slotGoInvisible() ), this, "actionGaduConnect" ) ); + + p->actionMenu_->insert( new KAction( i18n( "Go &Offline" ), + GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ).iconFor( this ), + 0, this, SLOT( slotGoOffline() ), this, "actionGaduConnect" ) ); + + p->actionMenu_->insert( new KAction( i18n( "Set &Description..." ), "info", + 0, this, SLOT( slotDescription() ), this, "actionGaduDescription" ) ); + + p->actionMenu_->insert( p->friendsModeAction ); + + p->actionMenu_->popupMenu()->insertSeparator(); + + p->actionMenu_->insert( p->searchAction ); + + p->actionMenu_->popupMenu()->insertSeparator(); + + p->actionMenu_->insert( p->listputAction ); + + p->actionMenu_->popupMenu()->insertSeparator(); + + p->actionMenu_->insert( p->listToFileAction ); + p->actionMenu_->insert( p->listFromFileAction ); + + return p->actionMenu_; +} + +void +GaduAccount::connectWithPassword(const QString& password) +{ + if (password.isEmpty()) { + return; + } + if (isConnected ()) + return; + // FIXME: add status description to this mechainsm, this is a hack now. libkopete design issue. + changeStatus( initialStatus(), p->lastDescription ); +} + +void +GaduAccount::disconnect() +{ + disconnect( Manual ); +} + +void +GaduAccount::disconnect( DisconnectReason reason ) +{ + slotGoOffline(); + p->connectWithSSL = true; + Kopete::Account::disconnected( reason ); +} + +void +GaduAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason ) +{ + kdDebug(14100) << k_funcinfo << "Called" << endl; + changeStatus( status, reason); +} + +void +GaduAccount::slotUserlistSynch() +{ + if ( !p->exportUserlist ) { + return; + } + p->exportUserlist = false; + kdDebug(14100) << "userlist changed, exporting" << endl; + slotExportContactsList(); +} + +void +GaduAccount::userlistChanged() +{ + p->exportUserlist = true; + p->exportTimer_->changeInterval( USERLISTEXPORT_TIMEOUT ); +} + +bool +GaduAccount::createContact( const QString& contactId, Kopete::MetaContact* parentContact ) +{ + kdDebug(14100) << "createContact " << contactId << endl; + + uin_t uinNumber = contactId.toUInt(); + GaduContact* newContact = new GaduContact( uinNumber, parentContact->displayName(),this, parentContact ); + newContact->setParentIdentity( accountId() ); + addNotify( uinNumber ); + + userlistChanged(); + + return true; +} + +void +GaduAccount::changeStatus( const Kopete::OnlineStatus& status, const QString& descr ) +{ + unsigned int ns; + + kdDebug(14100) << "##### change status #####" << endl; + kdDebug(14100) << "### Status = " << p->session_->isConnected() << endl; + kdDebug(14100) << "### Status description = \"" << descr << "\"" << endl; + + // if change to not available, log off + if ( GG_S_NA( status.internalStatus() ) ) { + if ( !p->session_->isConnected() ) { + return;//already logged off + } + else { + if ( status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) { + if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 ) { + return; + } + } + } + p->session_->logoff(); + dccOff(); + } + else { + // if status is for no desc, but we get some desc, than convert it to status with desc + if (!descr.isEmpty() && !GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) { + // and rerun us again. This won't cause any recursive call, as both conversions are static + ns = GaduProtocol::protocol()->statusToWithDescription( status ); + changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr ); + return; + } + + // well, if it's empty but we want to set status with desc, change it too + if (descr.isEmpty() && GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) { + ns = GaduProtocol::protocol()->statusToWithoutDescription( status ); + changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr ); + return; + } + + if ( !p->session_->isConnected() ) { + if ( password().cachedValue().isEmpty() ) { + // FIXME: when status string added to connect(), use it here + p->lastDescription = descr; + connect( status/*, descr*/ ); + return; + } + + if ( useTls() != TLS_no ) { + p->connectWithSSL = true; + } + else { + p->connectWithSSL = false; + } + dccOn(); + p->serverIP = 0; + p->currentServer = -1; + p->status = status; + kdDebug(14100) << "#### Connecting..., tls option "<< (int)useTls() << " " << endl; + p->lastDescription = descr; + slotLogin( status.internalStatus(), descr ); + return; + } + else { + p->status = status; + if ( descr.isEmpty() ) { + if ( p->session_->changeStatus( status.internalStatus(), p->forFriends ) != 0 ) + return; + } + else { + if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 ) + return; + } + } + } + + myself()->setOnlineStatus( status ); + myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, descr ); + + if ( status.internalStatus() == GG_STATUS_NOT_AVAIL || status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) { + if ( p->pingTimer_ ){ + p->pingTimer_->stop(); + } + } + p->lastDescription = descr; +} + +void +GaduAccount::slotLogin( int status, const QString& dscr ) +{ + p->lastDescription = dscr; + + myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING )); + myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, dscr ); + + if ( !p->session_->isConnected() ) { + if ( password().cachedValue().isEmpty() ) { + connectionFailed( GG_FAILURE_PASSWORD ); + } + else { + p->loginInfo.password = password().cachedValue(); + p->loginInfo.useTls = p->connectWithSSL; + p->loginInfo.status = status; + p->loginInfo.statusDescr = dscr; + p->loginInfo.forFriends = p->forFriends; + p->loginInfo.server = p->serverIP; + if ( dccEnabled() ) { + p->loginInfo.client_addr = gg_dcc_ip; + p->loginInfo.client_port = gg_dcc_port; + } + else { + p->loginInfo.client_addr = 0; + p->loginInfo.client_port = 0; + } + p->session_->login( &p->loginInfo ); + } + } + else { + p->session_->changeStatus( status ); + } +} + +void +GaduAccount::slotLogoff() +{ + if ( p->session_->isConnected() || p->status == GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING )) { + p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ); + changeStatus( p->status ); + p->session_->logoff(); + dccOff(); + } +} + +void +GaduAccount::slotGoOnline() +{ + changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ) ); +} +void +GaduAccount::slotGoOffline() +{ + slotLogoff(); + dccOff(); +} + +void +GaduAccount::slotGoInvisible() +{ + changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ) ); +} + +void +GaduAccount::slotGoBusy() +{ + changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ) ); +} + +void +GaduAccount::removeContact( const GaduContact* c ) +{ + if ( isConnected() ) { + const uin_t u = c->uin(); + p->session_->removeNotify( u ); + userlistChanged(); + } +} + +void +GaduAccount::addNotify( uin_t uin ) +{ + if ( p->session_->isConnected() ) { + p->session_->addNotify( uin ); + } +} + +void +GaduAccount::notify( uin_t* userlist, int count ) +{ + if ( p->session_->isConnected() ) { + p->session_->notify( userlist, count ); + } +} + +void +GaduAccount::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass ) +{ + if ( p->session_->isConnected() ) { + p->session_->sendMessage( recipient, msg, msgClass ); + } +} + +void +GaduAccount::error( const QString& title, const QString& message ) +{ + KMessageBox::error( Kopete::UI::Global::mainWidget(), title, message ); +} + +void +GaduAccount::messageReceived( KGaduMessage* gaduMessage ) +{ + GaduContact* contact = 0; + QPtrList<Kopete::Contact> contactsListTmp; + + // FIXME:check for ignored users list + + if ( gaduMessage->sender_id == 0 ) { + //system message, display them or not? + kdDebug(14100) << "####" << " System Message " << gaduMessage->message << endl; + return; + } + + contact = static_cast<GaduContact*> ( contacts()[ QString::number( gaduMessage->sender_id ) ] ); + + if ( !contact ) { + if ( p->ignoreAnons == true ) { + return; + } + + Kopete::MetaContact* metaContact = new Kopete::MetaContact (); + metaContact->setTemporary ( true ); + contact = new GaduContact( gaduMessage->sender_id, + QString::number( gaduMessage->sender_id ), this, metaContact ); + Kopete::ContactList::self ()->addMetaContact( metaContact ); + addNotify( gaduMessage->sender_id ); + } + + contactsListTmp.append( myself() ); + Kopete::Message msg( gaduMessage->sendTime, contact, contactsListTmp, gaduMessage->message, Kopete::Message::Inbound, Kopete::Message::RichText ); + contact->messageReceived( msg ); +} + +void +GaduAccount::ackReceived( unsigned int recipient ) +{ + GaduContact* contact; + + contact = static_cast<GaduContact*> ( contacts()[ QString::number( recipient ) ] ); + if ( contact ) { + kdDebug(14100) << "####" << "Received an ACK from " << contact->uin() << endl; + contact->messageAck(); + } + else { + kdDebug(14100) << "####" << "Received an ACK from an unknown user : " << recipient << endl; + } +} + +void +GaduAccount::contactStatusChanged( KGaduNotify* gaduNotify ) +{ + kdDebug(14100) << "####" << " contact's status changed, uin:" << gaduNotify->contact_id <<endl; + + GaduContact* contact; + + contact = static_cast<GaduContact*>( contacts()[ QString::number( gaduNotify->contact_id ) ] ); + if( !contact ) { + kdDebug(14100) << "Notify not in the list " << gaduNotify->contact_id << endl; + return; + } + + contact->changedStatus( gaduNotify ); +} + +void +GaduAccount::pong() +{ + kdDebug(14100) << "####" << " Pong..." << endl; +} + +void +GaduAccount::pingServer() +{ + kdDebug(14100) << "####" << " Ping..." << endl; + p->session_->ping(); +} + +void +GaduAccount::connectionFailed( gg_failure_t failure ) +{ + bool tryReconnect = false; + QString pass; + + + switch (failure) { + case GG_FAILURE_PASSWORD: + password().setWrong(); + // user pressed CANCEL + p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ); + myself()->setOnlineStatus( p->status ); + disconnected( BadPassword ); + return; + default: + if ( p->connectWithSSL ) { + if ( useTls() != TLS_only ) { + slotCommandDone( QString::null, i18n( "connection using SSL was not possible, retrying without." ) ); + kdDebug( 14100 ) << "try without tls now" << endl; + p->connectWithSSL = false; + tryReconnect = true; + p->currentServer = -1; + p->serverIP = 0; + break; + } + } + else { + if ( p->currentServer == NUM_SERVERS - 1 ) { + p->serverIP = 0; + p->currentServer = -1; + kdDebug(14100) << "trying : " << "IP from hub " << endl; + } + else { + p->serverIP = p->servers[ ++p->currentServer ]; + kdDebug(14100) << "trying : " << p->currentServer << " IP " << p->serverIP << endl; + tryReconnect = true; + } + } + break; + } + + if ( tryReconnect ) { + slotLogin( p->status.internalStatus() , p->lastDescription ); + } + else { + error( i18n( "unable to connect to the Gadu-Gadu server(\"%1\")." ).arg( GaduSession::failureDescription( failure ) ), + i18n( "Connection Error" ) ); + p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ); + myself()->setOnlineStatus( p->status ); + disconnected( InvalidHost ); + } +} + +void +GaduAccount::dccOn() +{ + if ( dccEnabled() ) { + if ( !p->gaduDcc_ ) { + p->gaduDcc_ = new GaduDCC( this ); + } + kdDebug( 14100 ) << " turn DCC on for " << accountId() << endl; + p->gaduDcc_->registerAccount( this ); + p->loginInfo.client_port = p->gaduDcc_->listeingPort(); + } +} + +void +GaduAccount::dccOff() +{ + if ( p->gaduDcc_ ) { + kdDebug( 14100 ) << "destroying dcc in gaduaccount " << endl; + delete p->gaduDcc_; + p->gaduDcc_ = NULL; + p->loginInfo.client_port = 0; + p->loginInfo.client_addr = 0; + } +} + +void +GaduAccount::slotIncomingDcc( unsigned int uin ) +{ + GaduContact* contact; + GaduDCCTransaction* trans; + + if ( !uin ) { + return; + } + + contact = static_cast<GaduContact*>( contacts()[ QString::number( uin ) ] ); + + if ( !contact ) { + kdDebug(14100) << "attempt to make dcc connection from unknown uin " << uin << endl; + return; + } + + // if incapabile to transfer files, forget about it. + if ( contact->contactPort() < 10 ) { + kdDebug(14100) << "can't respond to " << uin << " request, his listeing port is too low" << endl; + return; + } + + trans = new GaduDCCTransaction( p->gaduDcc_ ); + if ( trans->setupIncoming( p->loginInfo.uin, contact ) == false ) { + delete trans; + } + +} + +void +GaduAccount::connectionSucceed( ) +{ + kdDebug(14100) << "#### Gadu-Gadu connected! " << endl; + p->status = GaduProtocol::protocol()->convertStatus( p->session_->status() ); + myself()->setOnlineStatus( p->status ); + myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, p->lastDescription ); + startNotify(); + + p->session_->requestContacts(); + p->pingTimer_->start( 3*60*1000 );//3 minute timeout + pingServer(); + + // check if we need to export userlist every USERLISTEXPORT_TIMEOUT ms + p->exportTimer_->start( USERLISTEXPORT_TIMEOUT ); +} + +void +GaduAccount::startNotify() +{ + int i = 0; + if ( !contacts().count() ) { + return; + } + + QDictIterator<Kopete::Contact> kopeteContactsList( contacts() ); + + uin_t* userlist = 0; + userlist = new uin_t[ contacts().count() ]; + + for( i=0 ; kopeteContactsList.current() ; ++kopeteContactsList ) { + userlist[i++] = static_cast<GaduContact*> ((*kopeteContactsList))->uin(); + } + + p->session_->notify( userlist, contacts().count() ); + delete [] userlist; +} + +void +GaduAccount::slotSessionDisconnect( Kopete::Account::DisconnectReason reason ) +{ + uin_t status; + + kdDebug(14100) << "Disconnecting" << endl; + + if (p->pingTimer_) { + p->pingTimer_->stop(); + } + + setAllContactsStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) ); + + status = myself()->onlineStatus().internalStatus(); + if ( status != GG_STATUS_NOT_AVAIL || status != GG_STATUS_NOT_AVAIL_DESCR ) { + myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) ); + } + GaduAccount::disconnect( reason ); +} + +void +GaduAccount::userlist( const QString& contactsListString ) +{ + kdDebug(14100)<<"### Got userlist - gadu account"<<endl; + + GaduContactsList contactsList( contactsListString ); + QString contactName; + QStringList groups; + GaduContact* contact; + Kopete::MetaContact* metaContact; + unsigned int i; + + // don't export any new changes that were just imported :-) + p->exportTimer_->stop(); + + for ( i = 0; i != contactsList.size() ; i++ ) { + kdDebug(14100) << "uin " << contactsList[i].uin << endl; + + if ( contactsList[i].uin.isNull() ) { + kdDebug(14100) << "no Uin, strange.. "<<endl; + continue; + } + + if ( contacts()[ contactsList[i].uin ] ) { + kdDebug(14100) << "UIN already exists in contacts "<< contactsList[i].uin << endl; + } + else { + contactName = GaduContact::findBestContactName( &contactsList[i] ); + bool s = addContact( contactsList[i].uin, contactName, 0L, Kopete::Account::DontChangeKABC); + if ( s == false ) { + kdDebug(14100) << "There was a problem adding UIN "<< contactsList[i].uin << "to users list" << endl; + continue; + } + } + contact = static_cast<GaduContact*>( contacts()[ contactsList[i].uin ] ); + if ( contact == NULL ) { + kdDebug(14100) << "oops, no Kopete::Contact in contacts()[] for some reason, for \"" << contactsList[i].uin << "\"" << endl; + continue; + } + + // update/add infor for contact + contact->setContactDetails( &contactsList[i] ); + + if ( !( contactsList[i].group.isEmpty() ) ) { + // FIXME: libkopete bug i guess, by default contact goes to top level group + // if user desrired to see contact somewhere else, remove it from top level one + metaContact = contact->metaContact(); + metaContact->removeFromGroup( Kopete::Group::topLevel() ); + // put him in all desired groups: + groups = QStringList::split( ",", contactsList[i].group ); + for ( QStringList::Iterator groupsIterator = groups.begin(); groupsIterator != groups.end(); ++groupsIterator ) { + metaContact->addToGroup( Kopete::ContactList::self ()->findGroup ( *groupsIterator) ); + } + } + } + // start to check if we need to export userlist + p->exportUserlist = false; + p->exportTimer_->start( USERLISTEXPORT_TIMEOUT ); +} + +void +GaduAccount::userListExportDone() +{ + slotCommandDone( QString::null, i18n( "Contacts exported to the server.") ); +} + +void +GaduAccount::slotFriendsMode() +{ + p->forFriends = !p->forFriends; + kdDebug( 14100 ) << "for friends mode: " << p->forFriends << " desc" << p->lastDescription << endl; + // now change status, it will changing it with p->forFriends flag + changeStatus( p->status, p->lastDescription ); + + saveFriendsMode( p->forFriends ); + +} + +// FIXME: make loading and saving nonblocking (at the moment KFileDialog stops plugin/kopete) + +void +GaduAccount::slotExportContactsListToFile() +{ + KTempFile tempFile; + tempFile.setAutoDelete( true ); + + if ( p->saveListDialog ) { + kdDebug( 14100 ) << " save contacts to file: alread waiting for input " << endl ; + return; + } + + p->saveListDialog = new KFileDialog( "::kopete-gadu" + accountId(), QString::null, + Kopete::UI::Global::mainWidget(), "gadu-list-save", false ); + p->saveListDialog->setCaption( + i18n("Save Contacts List for Account %1 As").arg( + myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) ); + + if ( p->saveListDialog->exec() == QDialog::Accepted ) { + QCString list = p->textcodec_->fromUnicode( userlist()->asString() ); + + if ( tempFile.status() ) { + // say cheese, can't create file..... + error( i18n( "Unable to create temporary file." ), i18n( "Save Contacts List Failed" ) ); + } + else { + QTextStream* tempStream = tempFile.textStream(); + (*tempStream) << list.data(); + tempFile.close(); + + bool res = KIO::NetAccess::upload( + tempFile.name() , + p->saveListDialog->selectedURL() , + Kopete::UI::Global::mainWidget() + ); + if ( !res ) { + // say it failed + error( KIO::NetAccess::lastErrorString(), i18n( "Save Contacts List Failed" ) ); + } + } + + } + delete p->saveListDialog; + p->saveListDialog = NULL; +} + +void +GaduAccount::slotImportContactsFromFile() +{ + KURL url; + QCString list; + QString oname; + + if ( p->loadListDialog ) { + kdDebug( 14100 ) << "load contacts from file: alread waiting for input " << endl ; + return; + } + + p->loadListDialog = new KFileDialog( "::kopete-gadu" + accountId(), QString::null, + Kopete::UI::Global::mainWidget(), "gadu-list-load", true ); + p->loadListDialog->setCaption( + i18n("Load Contacts List for Account %1 As").arg( + myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) ); + + if ( p->loadListDialog->exec() == QDialog::Accepted ) { + url = p->loadListDialog->selectedURL(); + kdDebug(14100) << "a:" << url << "\nb:" << oname << endl; + if ( KIO::NetAccess::download( url, oname, Kopete::UI::Global::mainWidget() ) ) { + QFile tempFile( oname ); + if ( tempFile.open( IO_ReadOnly ) ) { + list = tempFile.readAll(); + tempFile.close(); + KIO::NetAccess::removeTempFile( oname ); + // and store it + kdDebug( 14100 ) << "loaded list:" << endl; + kdDebug( 14100 ) << list << endl; + kdDebug( 14100 ) << " --------------- " << endl; + userlist( p->textcodec_->toUnicode( list ) ); + } + else { + error( tempFile.errorString(), + i18n( "Contacts List Load Has Failed" ) ); + } + } + else { + // say, it failed misourably + error( KIO::NetAccess::lastErrorString(), + i18n( "Contacts List Load Has Failed" ) ); + } + + } + delete p->loadListDialog; + p->loadListDialog = NULL; +} + +unsigned int +GaduAccount::getPersonalInformation() +{ + return p->session_->getPersonalInformation(); +} + +bool +GaduAccount::publishPersonalInformation( ResLine& d ) +{ + return p->session_->publishPersonalInformation( d ); +} + +void +GaduAccount::slotExportContactsList() +{ + p->session_->exportContactsOnServer( userlist() ); +} + +GaduContactsList* +GaduAccount::userlist() +{ + GaduContact* contact; + GaduContactsList* contactsList = new GaduContactsList(); + int i; + + if ( !contacts().count() ) { + return contactsList; + } + + QDictIterator<Kopete::Contact> contactsIterator( contacts() ); + + for( i=0 ; contactsIterator.current() ; ++contactsIterator ) { + contact = static_cast<GaduContact*>( *contactsIterator ); + if ( contact->uin() != static_cast<GaduContact*>( myself() )->uin() ) { + contactsList->addContact( *contact->contactDetails() ); + } + } + + return contactsList; +} + +void +GaduAccount::slotSearch( int uin ) +{ + new GaduPublicDir( this, uin ); +} + +void +GaduAccount::slotChangePassword() +{ +} + +void +GaduAccount::slotCommandDone( const QString& /*title*/, const QString& what ) +{ + //FIXME: any chance to have my own title in event popup ? + KNotifyClient::userEvent( 0, what, + KNotifyClient::PassivePopup, KNotifyClient::Notification ); +} + +void +GaduAccount::slotCommandError(const QString& title, const QString& what ) +{ + error( title, what ); +} + +void +GaduAccount::slotDescription() +{ + GaduAway* away = new GaduAway( this ); + + if( away->exec() == QDialog::Accepted ) { + changeStatus( GaduProtocol::protocol()->convertStatus( away->status() ), + away->awayText() ); + } + delete away; +} + +unsigned int +GaduAccount::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive ) +{ + return p->session_->pubDirSearch( query, ageFrom, ageTo, onlyAlive ); +} + +void +GaduAccount::pubDirSearchClose() +{ + p->session_->pubDirSearchClose(); +} + +void +GaduAccount::slotSearchResult( const SearchResult& result, unsigned int seq ) +{ + emit pubDirSearchResult( result, seq ); +} + +void +GaduAccount::sendFile( GaduContact* peer, QString& filePath ) +{ + GaduDCCTransaction* gtran = new GaduDCCTransaction( p->gaduDcc_ ); + gtran->setupOutgoing( peer, filePath ); +} + +void +GaduAccount::dccRequest( GaduContact* peer ) +{ + if ( peer && p->session_ ) { + p->session_->dccRequest( peer->uin() ); + } +} + +// dcc settings +bool +GaduAccount::dccEnabled() +{ + QString s = p->config->readEntry( QString::fromAscii( "useDcc" ) ); + kdDebug( 14100 ) << "dccEnabled: "<< s << endl; + if ( s == QString::fromAscii( "enabled" ) ) { + return true; + } + return false; +} + +bool +GaduAccount::setDcc( bool d ) +{ + QString s; + bool f = true; + + if ( d == false ) { + dccOff(); + s = QString::fromAscii( "disabled" ); + } + else { + s = QString::fromAscii( "enabled" ); + } + + p->config->writeEntry( QString::fromAscii( "useDcc" ), s ); + + if ( p->session_->isConnected() && d ) { + dccOn(); + } + + kdDebug( 14100 ) << "s: "<<s<<endl; + + return f; +} + +void +GaduAccount::saveFriendsMode( bool i ) +{ + p->config->writeEntry( QString::fromAscii( "forFriends" ), + i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) ); +} + +bool +GaduAccount::loadFriendsMode() +{ + QString s; + bool r; + int n; + + s = p->config->readEntry( QString::fromAscii( "forFriends" ) ); + n = s.toInt( &r ); + + if ( n ) { + return true; + } + + return false; + +} + +// might be bit inconsistent with what I used in DCC, but hell, it is so much easier to parse :-) +bool +GaduAccount::ignoreAnons() +{ + QString s; + bool r; + int n; + + s = p->config->readEntry( QString::fromAscii( "ignoreAnons" ) ); + n = s.toInt( &r ); + + if ( n ) { + return true; + } + + return false; + +} + +void +GaduAccount::setIgnoreAnons( bool i ) +{ + p->ignoreAnons = i; + p->config->writeEntry( QString::fromAscii( "ignoreAnons" ), + i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) ); +} + +GaduAccount::tlsConnection +GaduAccount::useTls() +{ + QString s; + bool c; + unsigned int oldC; + tlsConnection Tls; + + s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ) ); + oldC = s.toUInt( &c ); + // we have old format + if ( c ) { + kdDebug( 14100 ) << "old format for param useEncryptedConnection, value " << + oldC << " willl be converted to new string value" << endl; + setUseTls( (tlsConnection) oldC ); + // should be string now, unless there was an error reading + s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ) ); + kdDebug( 14100 ) << "new useEncryptedConnection value : " << s << endl; + } + + Tls = TLS_no; + if ( s == "TLS_ifAvaliable" ) { + Tls = TLS_ifAvaliable; + } + if ( s == "TLS_only" ) { + Tls = TLS_only; + } + + return Tls; +} + +void +GaduAccount::setUseTls( tlsConnection ut ) +{ + QString s; + switch( ut ) { + case TLS_ifAvaliable: + s = "TLS_ifAvaliable"; + break; + + case TLS_only: + s = "TLS_only"; + break; + + default: + case TLS_no: + s = "TLS_no"; + break; + } + + p->config->writeEntry( QString::fromAscii( "useEncryptedConnection" ), s ); +} + +#include "gaduaccount.moc" diff --git a/kopete/protocols/gadu/gaduaccount.h b/kopete/protocols/gadu/gaduaccount.h new file mode 100644 index 00000000..b71e0d6e --- /dev/null +++ b/kopete/protocols/gadu/gaduaccount.h @@ -0,0 +1,173 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2003 Zack Rusin <zack@kde.org> +// +// gaduaccount.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUACCOUNT_H +#define GADUACCOUNT_H + +#include "kopetepasswordedaccount.h" +#include "kopeteonlinestatus.h" +#include "kopetecontact.h" + +#include "gaducontactlist.h" +#include "gadusession.h" + +#include <libgadu.h> + +#include <qhostaddress.h> +#include <qmap.h> +#include <qstring.h> +#include <qptrlist.h> +#include <kaction.h> +#include <kfiledialog.h> + +class GaduAccountPrivate; + +class GaduContact; +class GaduProtocol; +namespace Kopete { class Protocol; } +namespace Kopete { class Message; } +class GaduCommand; +class QTimer; +class KActionMenu; +class GaduDCC; +class GaduDCCTransaction; + +class GaduAccount : public Kopete::PasswordedAccount +{ + Q_OBJECT + +public: + GaduAccount( Kopete::Protocol*, const QString& accountID, const char* name = 0L ); + ~GaduAccount(); + //{ + void setAway( bool isAway, const QString& awayMessage = QString::null ); + KActionMenu* actionMenu(); + void dccRequest( GaduContact* ); + void sendFile( GaduContact* , QString& ); + //} + enum tlsConnection{ TLS_ifAvaliable = 0, TLS_only, TLS_no }; + unsigned int getPersonalInformation(); + bool publishPersonalInformation( ResLine& d ); + +public slots: + //{ + void connectWithPassword(const QString &password); + void disconnect( DisconnectReason ); + void disconnect(); + void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null); + //} + + void changeStatus( const Kopete::OnlineStatus& status, const QString& descr = QString::null ); + void slotLogin( int status = GG_STATUS_AVAIL, const QString& dscr = QString::null ); + void slotLogoff(); + void slotGoOnline(); + void slotGoOffline(); + void slotGoInvisible(); + void slotGoBusy(); + void slotDescription(); + void slotSearch( int uin = 0); + + void removeContact( const GaduContact* ); + + void addNotify( uin_t ); + void notify( uin_t*, int ); + + void sendMessage( uin_t recipient, const Kopete::Message& msg, + int msgClass = GG_CLASS_CHAT ); + + void error( const QString& title, const QString& message ); + + void pong(); + void pingServer(); + + // those two functions are connected straight to gadusession ones + // with the same names/params. This was the easiest way to + // make this interface public + unsigned int pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive ); + void pubDirSearchClose(); + + // tls + tlsConnection useTls(); + void setUseTls( tlsConnection ); + + // dcc + bool dccEnabled(); + bool setDcc( bool ); + + // anons + bool ignoreAnons(); + void setIgnoreAnons( bool ); + + // forFriends + bool loadFriendsMode(); + void saveFriendsMode( bool ); + +signals: + void pubDirSearchResult( const SearchResult&, unsigned int ); + +protected: + //{ + bool createContact( const QString& contactId, + Kopete::MetaContact* parentContact ); + //} + +private slots: + void startNotify(); + + void messageReceived( KGaduMessage* ); + void ackReceived( unsigned int ); + void contactStatusChanged( KGaduNotify* ); + void slotSessionDisconnect( Kopete::Account::DisconnectReason ); + + void slotExportContactsList(); + void slotExportContactsListToFile(); + void slotImportContactsFromFile(); + void slotFriendsMode(); + + void userlist( const QString& contacts ); + GaduContactsList* userlist(); + void slotUserlistSynch(); + + void connectionFailed( gg_failure_t failure ); + void connectionSucceed( ); + + void slotChangePassword(); + + void slotCommandDone( const QString&, const QString& ); + void slotCommandError( const QString&, const QString& ); + + void slotSearchResult( const SearchResult& result, unsigned int seq ); + void userListExportDone(); + + void slotIncomingDcc( unsigned int ); + +private: + void initConnections(); + void initActions(); + void dccOn(); + void dccOff(); + void userlistChanged(); + + GaduAccountPrivate* p; +}; + +#endif diff --git a/kopete/protocols/gadu/gaduaddcontactpage.cpp b/kopete/protocols/gadu/gaduaddcontactpage.cpp new file mode 100644 index 00000000..d2aed62b --- /dev/null +++ b/kopete/protocols/gadu/gaduaddcontactpage.cpp @@ -0,0 +1,134 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaduaddconectpage.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "kopetemetacontact.h" + +#include "gaduadd.h" +#include "gaduprotocol.h" +#include "gaduaccount.h" +#include "gaduaddcontactpage.h" +#include "gaducontact.h" +#include "gaducontactlist.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kopetecontactlist.h> +#include <kopetegroup.h> + +#include <qwidget.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qptrlist.h> +#include <qcombobox.h> +#include <krestrictedline.h> + +GaduAddContactPage::GaduAddContactPage( GaduAccount* owner, QWidget* parent, const char* name ) +: AddContactPage( parent, name ) +{ + account_ = owner; + ( new QVBoxLayout( this ) )->setAutoAdd( true ); + addUI_ = new GaduAddUI( this ); + connect( addUI_->addEdit_, SIGNAL( textChanged( const QString & ) ), SLOT( slotUinChanged( const QString & ) ) ); + addUI_->addEdit_->setValidChars( "1234567890" ); + addUI_->addEdit_->setText( "" ); + addUI_->groups->setDisabled( TRUE ); + + kdDebug(14100) << "filling gropus" << endl; + + fillGroups(); +} + +GaduAddContactPage::~GaduAddContactPage() +{ + delete addUI_; +} + +void +GaduAddContactPage::fillGroups() +{ + /* + Kopete::Group *g; + QPtrList<Kopete::Group> gl = Kopete::ContactList::self()->groups(); + for( g = gl.first(); g; g = gl.next() ) { + QCheckListItem* item = new QCheckListItem( addUI_->groups, g->displayName(), QCheckListItem::CheckBox ); + kdDebug(14100) << g->displayName() << " " << g->groupId() << endl; + } + */ +} + +void +GaduAddContactPage::showEvent( QShowEvent* e ) +{ + slotUinChanged( QString::null ); + AddContactPage::showEvent( e ); +} + +void +GaduAddContactPage::slotUinChanged( const QString & ) +{ + emit dataValid( this, validateData() ); +} + +bool +GaduAddContactPage::validateData() +{ + bool ok; + long u; + + u = addUI_->addEdit_->text().toULong( &ok ); + if ( u == 0 ) { + return false; + } + + return ok; +} + +bool +GaduAddContactPage::apply( Kopete::Account* a , Kopete::MetaContact* mc ) +{ + if ( validateData() ) { + QString userid = addUI_->addEdit_->text().stripWhiteSpace(); + QString name = addUI_->nickEdit_->text().stripWhiteSpace(); + if ( a != account_ ) { + kdDebug(14100) << "Problem because accounts differ: " << a->accountId() + << " , " << account_->accountId() << endl; + } + if ( !a->addContact( userid, mc, Kopete::Account::ChangeKABC ) ) { + return false; + } + GaduContact *contact = static_cast<GaduContact*>( a->contacts()[ userid ] ); + + contact->setProperty( GaduProtocol::protocol()->propEmail, addUI_->emailEdit_->text().stripWhiteSpace() ); + contact->setProperty( GaduProtocol::protocol()->propFirstName, addUI_->fornameEdit_->text().stripWhiteSpace() ); + contact->setProperty( GaduProtocol::protocol()->propLastName, addUI_->snameEdit_->text().stripWhiteSpace() ); + contact->setProperty( GaduProtocol::protocol()->propPhoneNr, addUI_->telephoneEdit_ ->text().stripWhiteSpace() ); + /* + contact->setProperty( "ignored", i18n( "ignored" ), "false" ); + contact->setProperty( "nickName", i18n( "nick name" ), name ); + */ + } + return true; +} + +#include "gaduaddcontactpage.moc" diff --git a/kopete/protocols/gadu/gaduaddcontactpage.h b/kopete/protocols/gadu/gaduaddcontactpage.h new file mode 100644 index 00000000..39e4d52e --- /dev/null +++ b/kopete/protocols/gadu/gaduaddcontactpage.h @@ -0,0 +1,61 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaduaddconectpage.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUADDCONTACTPAGE_H +#define GADUADDCONTACTPAGE_H + +#include "addcontactpage.h" + +class GaduAccount; +class GaduAddUI; +class QLabel; +namespace Kopete { class MetaContact; } +class QString; +class QShowEvent; +class GaduAddUI; + + +class GaduAddContactPage : public AddContactPage +{ + Q_OBJECT + +public: + GaduAddContactPage( GaduAccount*, QWidget* parent = 0, const char* name = 0 ); + ~GaduAddContactPage(); + virtual bool validateData(); + virtual bool apply( Kopete::Account* , Kopete::MetaContact * ); + + protected: + void showEvent(QShowEvent *e); + +public slots: + void slotUinChanged( const QString& ); + +private: + void fillGroups(); + GaduAccount* account_; + GaduAddUI* addUI_; + QLabel* noaddMsg1_; + QLabel* noaddMsg2_; +}; + +#endif diff --git a/kopete/protocols/gadu/gaduaway.cpp b/kopete/protocols/gadu/gaduaway.cpp new file mode 100644 index 00000000..a84fcd0f --- /dev/null +++ b/kopete/protocols/gadu/gaduaway.cpp @@ -0,0 +1,86 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gaduaway.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "gaduaccount.h" +#include "gaduprotocol.h" +#include "gaduawayui.h" +#include "gaduaway.h" + +#include "kopeteonlinestatus.h" + +#include <ktextedit.h> +#include <klocale.h> + +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qlineedit.h> + +#include "gaduawayui.h" +#include "gaduaway.h" + +GaduAway::GaduAway( GaduAccount* account, QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "Away Dialog" ), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true ), account_( account ) +{ + Kopete::OnlineStatus ks; + int s; + + ui_ = new GaduAwayUI( this ); + setMainWidget( ui_ ); + + ks = account->myself()->onlineStatus(); + s = GaduProtocol::protocol()->statusToWithDescription( ks ); + + if ( s == GG_STATUS_NOT_AVAIL_DESCR ) { + ui_->statusGroup_->find( GG_STATUS_NOT_AVAIL_DESCR )->setDisabled( TRUE ); + ui_->statusGroup_->setButton( GG_STATUS_AVAIL_DESCR ); + } + else { + ui_->statusGroup_->setButton( s ); + } + + ui_->textEdit_->setText( account->myself()->property( "awayMessage" ).value().toString() ); + connect( this, SIGNAL( applyClicked() ), SLOT( slotApply() ) ); +} + +int +GaduAway::status() const +{ + return ui_->statusGroup_->id( ui_->statusGroup_->selected() ); +} + +QString +GaduAway::awayText() const +{ + return ui_->textEdit_->text(); +} + + +void +GaduAway::slotApply() +{ + if ( account_ ) { + account_->changeStatus( GaduProtocol::protocol()->convertStatus( status() ),awayText() ); + } +} + +#include "gaduaway.moc" diff --git a/kopete/protocols/gadu/gaduaway.h b/kopete/protocols/gadu/gaduaway.h new file mode 100644 index 00000000..bebbcd9f --- /dev/null +++ b/kopete/protocols/gadu/gaduaway.h @@ -0,0 +1,49 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaduaway.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUAWAY_H +#define GADUAWAY_H + +#include <kdialogbase.h> +#include <qstring.h> + +class GaduAccount; +class GaduAwayUI; + +class GaduAway : public KDialogBase +{ + Q_OBJECT + +public: + GaduAway( GaduAccount*, QWidget* parent = 0, const char* name = 0 ); + int status() const; + QString awayText() const; + +protected slots: + void slotApply(); + +private: + GaduAccount* account_; + GaduAwayUI* ui_; +}; + +#endif diff --git a/kopete/protocols/gadu/gaducommands.cpp b/kopete/protocols/gadu/gaducommands.cpp new file mode 100644 index 00000000..431b1ab4 --- /dev/null +++ b/kopete/protocols/gadu/gaducommands.cpp @@ -0,0 +1,411 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaducommands.cpp - all basic, and not-session dependent commands +// (meaning you don't have to be logged in for any of these). +// These delete themselves, meaning you don't +// have to/can't delete them explicitly and have to create +// them dynamically (via the 'new' call). +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "gaducommands.h" +#include "gadusession.h" + +#include <qsocketnotifier.h> +#include <qregexp.h> +#include <qtextcodec.h> +#include <qimage.h> + +#include <klocale.h> +#include <kdebug.h> + +#include <errno.h> + +GaduCommand::GaduCommand( QObject* parent, const char* name ) +: QObject( parent, name ), read_( 0 ), write_( 0 ) +{ +} + +GaduCommand::~GaduCommand() +{ + //QSocketNotifiers are children and will + //be deleted anyhow +} + +bool +GaduCommand::done() const +{ + return done_; +} + +void +GaduCommand::checkSocket( int fd, int checkWhat ) +{ + read_ = new QSocketNotifier( fd, QSocketNotifier::Read, this ); + read_->setEnabled( false ); + QObject::connect( read_, SIGNAL( activated(int) ), SLOT( forwarder() ) ); + + write_ = new QSocketNotifier( fd, QSocketNotifier::Write, this ); + write_->setEnabled( false ); + QObject::connect( write_, SIGNAL( activated(int) ), SLOT( forwarder() ) ); + + enableNotifiers( checkWhat ); +} + +void +GaduCommand::enableNotifiers( int checkWhat ) +{ + if ( read_ ) { + if( checkWhat & GG_CHECK_READ ) { + read_->setEnabled( true ); + } + } + + if ( write_ ) { + if( checkWhat & GG_CHECK_WRITE ) { + write_->setEnabled( true ); + } + } +} + +void +GaduCommand::disableNotifiers() +{ + if ( read_ ) { + read_->setEnabled( false ); + } + if ( write_ ) { + write_->setEnabled( false ); + } +} + +void +GaduCommand::deleteNotifiers() +{ + if ( read_ ) { + delete read_; + read_ = NULL; + } + if ( write_ ) { + delete write_; + write_ = NULL; + } +} + +void +GaduCommand::forwarder() +{ + emit socketReady(); +} + +RegisterCommand::RegisterCommand( QObject* parent, const char* name ) +:GaduCommand( parent, name ), state( RegisterStateNoToken ), session_( 0 ), uin( 0 ) +{ +} + +RegisterCommand::RegisterCommand( const QString& email, const QString& password, QObject* parent, const char* name ) +:GaduCommand( parent, name ), state( RegisterStateNoToken ), email_( email ), password_( password ), session_( 0 ), uin( 0 ) +{ +} + +RegisterCommand::~RegisterCommand() +{ +} + +unsigned int RegisterCommand::newUin() +{ + if ( state == RegisterStateDone ) { + return uin; + } +// else + return 0; +} +void +RegisterCommand::requestToken() +{ + kdDebug( 14100 ) << "requestToken Initialisation" << endl; + state = RegisterStateWaitingForToken; + + if ( !( session_ = gg_token( 1 ) ) ) { + emit error( i18n( "Gadu-Gadu" ), i18n( "Unable to retrieve token." ) ); + state = RegisterStateNoToken; + return; + } + + connect( this, SIGNAL( socketReady() ), SLOT( watcher() ) ); + checkSocket( session_->fd, session_->check ); + + return; +} + +void +RegisterCommand::setUserinfo( const QString& email, const QString& password, const QString& token ) +{ + email_ = email; + password_ = password; + tokenString = token; +} + +void +RegisterCommand::cancel() +{ + deleteNotifiers(); + session_ = NULL; + +} + +void +RegisterCommand::execute() +{ + if ( state != RegisterStateGotToken || email_.isEmpty() || password_.isEmpty() || tokenString.isEmpty() ) { + // get token first || fill information + kdDebug(14100) << "not enough info to run execute, state: " << state << " , email: " << email_ << ", password present " << !password_.isEmpty() << ", token string:" << tokenString << endl; + return; + } + session_ = gg_register3( email_.ascii(), password_.ascii(), tokenId.ascii(), tokenString.ascii(), 1 ); + if ( !session_ ) { + error( i18n( "Gadu-Gadu" ), i18n( "Registration FAILED" ) ); + return; + } + state = RegisterStateWaitingForNumber; + connect( this, SIGNAL( socketReady() ), SLOT( watcher() ) ); + checkSocket( session_->fd, session_->check ); +} + +void RegisterCommand::watcher() +{ + gg_pubdir* pubDir; + + if ( state == RegisterStateWaitingForToken ) { + disableNotifiers(); + if ( gg_token_watch_fd( session_ ) == -1 ) { + deleteNotifiers(); + emit error( i18n( "Gadu-Gadu" ), i18n( "Unknown connection error while retrieving token." ) ); + gg_token_free( session_ ); + session_ = NULL; + state = RegisterStateNoToken; + return; + } + + pubDir = (struct gg_pubdir *)session_->data; + emit operationStatus( i18n( "Token retrieving status: %1" ).arg( GaduSession::stateDescription( session_->state ) ) ); + switch ( session_->state ) { + case GG_STATE_CONNECTING: + kdDebug( 14100 ) << "Recreating notifiers " << endl; + deleteNotifiers(); + checkSocket( session_->fd, 0); + break; + case GG_STATE_ERROR: + deleteNotifiers(); + emit error( i18n( "Gadu-Gadu token retrieve problem" ), GaduSession::errorDescription( session_->error ) ); + gg_token_free( session_ ); + session_ = NULL; + state = RegisterStateNoToken; + return; + break; + case GG_STATE_DONE: + struct gg_token* sp = ( struct gg_token* )session_->data; + tokenId = (char *)sp->tokenid; + kdDebug( 14100 ) << "got Token!, ID: " << tokenId << endl; + deleteNotifiers(); + if ( pubDir->success ) { + QPixmap tokenImg; + tokenImg.loadFromData( (const unsigned char *)session_->body, session_->body_size ); + state = RegisterStateGotToken; + emit tokenRecieved( tokenImg, tokenId ); + } + else { + emit error( i18n( "Gadu-Gadu" ), i18n( "Unable to retrieve token." ) ); + state = RegisterStateNoToken; + deleteLater(); + } + gg_token_free( session_ ); + session_ = NULL; + disconnect( this, SLOT( watcher() ) ); + return; + break; + } + enableNotifiers( session_->check ); + } + if ( state == RegisterStateWaitingForNumber ) { + disableNotifiers(); + if ( gg_register_watch_fd( session_ ) == -1 ) { + deleteNotifiers(); + emit error( i18n( "Gadu-Gadu" ), i18n( "Unknown connection error while registering." ) ); + gg_free_register( session_ ); + session_ = NULL; + state = RegisterStateGotToken; + return; + } + pubDir = (gg_pubdir*) session_->data; + emit operationStatus( i18n( "Registration status: %1" ).arg( GaduSession::stateDescription( session_->state ) ) ); + switch ( session_->state ) { + case GG_STATE_CONNECTING: + kdDebug( 14100 ) << "Recreating notifiers " << endl; + deleteNotifiers(); + checkSocket( session_->fd, 0); + break; + case GG_STATE_ERROR: + deleteNotifiers(); + emit error( i18n( "Gadu-Gadu Registration Error" ), GaduSession::errorDescription( session_->error ) ); + gg_free_register( session_ ); + session_ = NULL; + state = RegisterStateGotToken; + return; + break; + + case GG_STATE_DONE: + deleteNotifiers(); + if ( pubDir->success && pubDir->uin ) { + uin= pubDir->uin; + state = RegisterStateDone; + emit done( i18n( "Registration Finished" ), i18n( "Registration has completed successfully." ) ); + } + else { + emit error( i18n( "Registration Error" ), i18n( "Incorrect data sent to server." ) ); + state = RegisterStateGotToken; + } + gg_free_register( session_ ); + session_ = NULL; + disconnect( this, SLOT( watcher() ) ); + deleteLater(); + return; + break; + } + enableNotifiers( session_->check ); + return; + } +} + +RemindPasswordCommand::RemindPasswordCommand( QObject* parent, const char* name ) +: GaduCommand( parent, name ), uin_( 0 ), session_( 0 ) +{ +} + +RemindPasswordCommand::RemindPasswordCommand( uin_t uin, QObject* parent, const char* name ) +: GaduCommand( parent, name ), uin_( uin ), session_( 0 ) +{ +} + +RemindPasswordCommand::~RemindPasswordCommand() +{ +} + +void +RemindPasswordCommand::setUIN( uin_t uin ) +{ + uin_ = uin; +} + +void +RemindPasswordCommand::execute() +{ +} + +void +RemindPasswordCommand::watcher() +{ + disableNotifiers(); + + if ( gg_remind_passwd_watch_fd( session_ ) == -1 ) { + gg_free_remind_passwd( session_ ); + emit error( i18n( "Connection Error" ), i18n( "Password reminding finished prematurely due to a connection error." ) ); + done_ = true; + deleteLater(); + return; + } + + if ( session_->state == GG_STATE_ERROR ) { + gg_free_remind_passwd( session_ ); + emit error( i18n( "Connection Error" ), i18n( "Password reminding finished prematurely due to a connection error." ) ); + done_ = true; + deleteLater(); + return; + } + + if ( session_->state == GG_STATE_DONE ) { + struct gg_pubdir* p = static_cast<struct gg_pubdir*>( session_->data ); + QString finished = (p->success) ? i18n( "Successfully" ) : i18n( "Unsuccessful. Please retry." ); + emit done( i18n( "Remind Password" ), i18n( "Remind password finished: " ) + finished ); + gg_free_remind_passwd( session_ ); + done_ = true; + deleteLater(); + return; + } + enableNotifiers( session_->check ); +} + +ChangePasswordCommand::ChangePasswordCommand( QObject* parent, const char* name ) +: GaduCommand( parent, name ), session_( 0 ) +{ +} + +ChangePasswordCommand::~ChangePasswordCommand() +{ +} + +void +ChangePasswordCommand::setInfo( uin_t uin, const QString& passwd, const QString& newpasswd, const QString& newemail ) +{ + uin_ = uin; + passwd_ = passwd; + newpasswd_ = newpasswd; + newemail_ = newemail; +} + +void +ChangePasswordCommand::execute() +{ +} + +void +ChangePasswordCommand::watcher() +{ + disableNotifiers(); + + if ( gg_pubdir_watch_fd( session_ ) == -1 ) { + gg_change_passwd_free( session_ ); + emit error( i18n( "Connection Error" ), i18n( "Password changing finished prematurely due to a connection error." ) ); + done_ = true; + deleteLater(); + return; + } + + if ( session_->state == GG_STATE_ERROR ) { + gg_free_change_passwd( session_ ); + emit error( i18n( "State Error" ), + i18n( "Password changing finished prematurely due to a session related problem (try again later)." ) ); + done_ = true; + deleteLater(); + return; + } + + if ( session_->state == GG_STATE_DONE ) { + emit done( i18n( "Changed Password" ), i18n( "Your password has been changed." ) ); + gg_free_change_passwd( session_ ); + done_ = true; + deleteLater(); + return; + } + + enableNotifiers( session_->check ); +} + + +#include "gaducommands.moc" diff --git a/kopete/protocols/gadu/gaducommands.h b/kopete/protocols/gadu/gaducommands.h new file mode 100644 index 00000000..8a48ec3d --- /dev/null +++ b/kopete/protocols/gadu/gaducommands.h @@ -0,0 +1,150 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaducommands.h - all basic, and not-session dependent commands +// (meaning you don't have to be logged in for any of these). +// These delete themselves, meaning you don't +// have to/can't delete them explicitly and have to create +// them dynamically (via the 'new' call). +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUCOMMANDS_H +#define GADUCOMMANDS_H + +#include <libgadu.h> + +#include <qobject.h> + +class QSocketNotifier; +class QStringList; +class QPixmap; + +class GaduCommand : public QObject +{ + Q_OBJECT + +public: + GaduCommand( QObject* parent = 0, const char* name = 0 ); + virtual ~GaduCommand(); + + virtual void execute() = 0; + + bool done() const; + +signals: + //e.g. emit done( i18n("Done"), i18n("Registration complete") ); + void done( const QString& title, const QString& what ); + void error( const QString& title, const QString& error ); + void socketReady(); + void operationStatus( const QString ); + +protected: + void checkSocket( int, int ); + void enableNotifiers( int ); + void disableNotifiers(); + void deleteNotifiers(); + + bool done_; + +protected slots: + void forwarder(); + +private: + QSocketNotifier* read_; + QSocketNotifier* write_; +}; + +class RegisterCommand : public GaduCommand +{ + Q_OBJECT + +public: + RegisterCommand( QObject* parent = 0, const char* name = 0 ); + RegisterCommand( const QString& email, const QString& password , + QObject* parent = 0, const char* name = 0 ); + ~RegisterCommand(); + + void setUserinfo( const QString& email, const QString& password, const QString& token ); + void execute(); + unsigned int newUin(); + void requestToken(); + void cancel(); + +signals: + void tokenRecieved( QPixmap, QString ); + +protected slots: + void watcher(); + +private: + enum RegisterState{ RegisterStateNoToken, RegisterStateWaitingForToken, RegisterStateGotToken, RegisterStateWaitingForNumber, RegisterStateDone }; + RegisterState state; + QString email_; + QString password_; + struct gg_http* session_; + int uin; + QString tokenId; + QString tokenString; +}; + +class RemindPasswordCommand : public GaduCommand +{ + Q_OBJECT + +public: + RemindPasswordCommand( uin_t uin, QObject* parent = 0, const char* name = 0 ); + RemindPasswordCommand( QObject* parent = 0, const char* name = 0 ); + ~RemindPasswordCommand(); + + void setUIN( uin_t ); + void execute(); + +protected slots: + void watcher(); + +private: + uin_t uin_; + struct gg_http* session_; +}; + +class ChangePasswordCommand : public GaduCommand +{ + Q_OBJECT + +public: + ChangePasswordCommand( QObject* parent = 0, const char* name = 0 ); + ~ChangePasswordCommand(); + + void setInfo( uin_t uin, const QString& passwd, const QString& newpasswd, + const QString& newemail ); + void execute(); + +protected slots: + void watcher(); + +private: + struct gg_http* session_; + QString passwd_; + QString newpasswd_; + QString newemail_; + uin_t uin_; +}; + + +#endif diff --git a/kopete/protocols/gadu/gaducontact.cpp b/kopete/protocols/gadu/gaducontact.cpp new file mode 100644 index 00000000..dddd965f --- /dev/null +++ b/kopete/protocols/gadu/gaducontact.cpp @@ -0,0 +1,384 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaducontact.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <klocale.h> +#include <kaction.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <kmessagebox.h> + +#include "gaduaccount.h" +#include "gaduprotocol.h" +#include "gaducontact.h" +#include "gadupubdir.h" +#include "gadueditcontact.h" +#include "gaducontactlist.h" +#include "gadusession.h" + +#include "kopetechatsessionmanager.h" +#include "kopetegroup.h" +#include "kopetemetacontact.h" +#include "kopetestdaction.h" +#include "kopeteuiglobal.h" + +#include "userinfodialog.h" + +using Kopete::UserInfoDialog; + +GaduContact::GaduContact( uin_t uin, const QString& name, Kopete::Account* account, Kopete::MetaContact* parent ) +: Kopete::Contact( account, QString::number( uin ), parent ), uin_( uin ) +{ + msgManager_ = 0L; + account_ = static_cast<GaduAccount*>( account ); + + remote_port = 0; + version = 0; + image_size = 0; + // let us not ignore the contact by default right? causes ugly bug if + // setContactDetails is not run on a contact right after it is added + ignored_ = false; + + thisContact_.append( this ); + + initActions(); + + // don't call libkopete functions like these until the object is fully + // constructed. all GaduContact construction must be above this point. + setFileCapable( true ); + + //offline + setOnlineStatus( GaduProtocol::protocol()->convertStatus( 0 ) ); + + setProperty( Kopete::Global::Properties::self()->nickName(), name ); +} + +QString +GaduContact::identityId() const +{ + return parentIdentity_; +} + +void +GaduContact::setParentIdentity( const QString& id) +{ + parentIdentity_ = id; +} + +uin_t +GaduContact::uin() const +{ + return uin_; +} + +void +GaduContact::sendFile( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ ) +{ + QString filePath; + + //If the file location is null, then get it from a file open dialog + if( !sourceURL.isValid() ) + filePath = KFileDialog::getOpenFileName(QString::null, "*", 0l , i18n("Kopete File Transfer")); + else + filePath = sourceURL.path(-1); + + kdDebug(14120) << k_funcinfo << "File chosen to send:" << filePath << endl; + + account_->sendFile( this, filePath ); +} + + +void +GaduContact::changedStatus( KGaduNotify* newstatus ) +{ + if ( newstatus->description.isNull() ) { + setOnlineStatus( GaduProtocol::protocol()->convertStatus( newstatus->status ) ); + removeProperty( GaduProtocol::protocol()->propAwayMessage ); + } + else { + setOnlineStatus( GaduProtocol::protocol()->convertStatus( newstatus->status ) ); + setProperty( GaduProtocol::protocol()->propAwayMessage, newstatus->description ); + } + + remote_ip = newstatus->remote_ip; + remote_port = newstatus->remote_port; + version = newstatus->version; + image_size = newstatus->image_size; + + setFileCapable( newstatus->fileCap ); + + kdDebug(14100) << "uin:" << uin() << " port: " << remote_port << " remote ip: " << remote_ip.ip4Addr() << " image size: " << image_size << " version: " << version << endl; + +} + +QHostAddress& +GaduContact::contactIp() +{ + return remote_ip; +} + +unsigned short +GaduContact::contactPort() +{ + return remote_port; +} + +Kopete::ChatSession* +GaduContact::manager( Kopete::Contact::CanCreateFlags canCreate ) +{ + if ( !msgManager_ && canCreate ) { + msgManager_ = Kopete::ChatSessionManager::self()->create( account_->myself(), thisContact_, GaduProtocol::protocol() ); + connect( msgManager_, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession*) ), + this, SLOT( messageSend( Kopete::Message&, Kopete::ChatSession*) ) ); + connect( msgManager_, SIGNAL( destroyed() ), this, SLOT( slotChatSessionDestroyed() ) ); + + } + kdDebug(14100) << "GaduContact::manager returning: " << msgManager_ << endl; + return msgManager_; +} + +void +GaduContact::slotChatSessionDestroyed() +{ + msgManager_ = 0L; +} + +void +GaduContact::initActions() +{ + actionSendMessage_ = KopeteStdAction::sendMessage( this, SLOT( execute() ), this, "actionMessage" ); + actionInfo_ = KopeteStdAction::contactInfo( this, SLOT( slotUserInfo() ), this, "actionInfo" ); +} + +void +GaduContact::messageReceived( Kopete::Message& msg ) +{ + manager(Kopete::Contact::CanCreate)->appendMessage( msg ); +} + +void +GaduContact::messageSend( Kopete::Message& msg, Kopete::ChatSession* mgr ) +{ + if ( msg.plainBody().isEmpty() ) { + return; + } + mgr->appendMessage( msg ); + account_->sendMessage( uin_, msg ); +} + +bool +GaduContact::isReachable() +{ + return account_->isConnected(); +} + +QPtrList<KAction>* +GaduContact::customContextMenuActions() +{ + QPtrList<KAction> *fakeCollection = new QPtrList<KAction>(); + //show profile + KAction* actionShowProfile = new KAction( i18n("Show Profile") , "info", 0, + this, SLOT( slotShowPublicProfile() ), + this, "actionShowPublicProfile" ); + + fakeCollection->append( actionShowProfile ); + + KAction* actionEditContact = new KAction( i18n("Edit...") , "edit", 0, + this, SLOT( slotEditContact() ), + this, "actionEditContact" ); + + fakeCollection->append( actionEditContact ); + + return fakeCollection; +} + +void +GaduContact::slotEditContact() +{ + new GaduEditContact( static_cast<GaduAccount*>(account()), this, Kopete::UI::Global::mainWidget() ); +} + +void +GaduContact::slotShowPublicProfile() +{ + account_->slotSearch( uin_ ); +} + +void +GaduContact::slotUserInfo() +{ + /// FIXME: use more decent information here + UserInfoDialog *dlg = new UserInfoDialog( i18n( "Gadu contact" ) ); + + dlg->setName( metaContact()->displayName() ); + dlg->setId( QString::number( uin_ ) ); + dlg->setStatus( onlineStatus().description() ); + dlg->setAwayMessage( description_ ); + dlg->show(); +} + +void +GaduContact::deleteContact() +{ + if ( account_->isConnected() ) { + account_->removeContact( this ); + deleteLater(); + } + else { + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Please go online to remove a contact from your contact list.</qt>" ), + i18n( "Gadu-Gadu Plugin" )); + } +} + +void +GaduContact::serialize( QMap<QString, QString>& serializedData, QMap<QString, QString>& ) +{ + serializedData[ "email" ] = property( GaduProtocol::protocol()->propEmail ).value().toString(); + serializedData[ "FirstName" ] = property( GaduProtocol::protocol()->propFirstName ).value().toString(); + serializedData[ "SecondName" ] = property( GaduProtocol::protocol()->propLastName ).value().toString(); + serializedData[ "telephone" ] = property( GaduProtocol::protocol()->propPhoneNr ).value().toString(); + serializedData[ "ignored" ] = ignored_ ? "true" : "false"; +} + +bool +GaduContact::setContactDetails( const GaduContactsList::ContactLine* cl ) +{ + setProperty( GaduProtocol::protocol()->propEmail, cl->email ); + setProperty( GaduProtocol::protocol()->propFirstName, cl->firstname ); + setProperty( GaduProtocol::protocol()->propLastName, cl->surname ); + setProperty( GaduProtocol::protocol()->propPhoneNr, cl->phonenr ); + //setProperty( "ignored", i18n( "ignored" ), cl->ignored ? "true" : "false" ); + ignored_ = cl->ignored; + //setProperty( "nickName", i18n( "nick name" ), cl->nickname ); + + return true; +} + +GaduContactsList::ContactLine* +GaduContact::contactDetails() +{ + Kopete::GroupList groupList; + QString groups; + + GaduContactsList::ContactLine* cl = new GaduContactsList::ContactLine; + + cl->firstname = property( GaduProtocol::protocol()->propFirstName ).value().toString(); + cl->surname = property( GaduProtocol::protocol()->propLastName ).value().toString(); + //cl->nickname = property( "nickName" ).value().toString(); + cl->email = property( GaduProtocol::protocol()->propEmail ).value().toString(); + cl->phonenr = property( GaduProtocol::protocol()->propPhoneNr ).value().toString(); + cl->ignored = ignored_; //( property( "ignored" ).value().toString() == "true" ); + + cl->uin = QString::number( uin_ ); + cl->displayname = metaContact()->displayName(); + + cl->offlineTo = false; + cl->landline = QString(""); + + groupList = metaContact()->groups(); + + Kopete::Group* gr; + for ( gr = groupList.first (); gr ; gr = groupList.next () ) { +// if present in any group, don't export to top level +// FIXME: again, probably bug in libkopete +// in case of topLevel group, Kopete::Group::displayName() returns "TopLevel" ineasted of just " " or "/" +// imo TopLevel group should be detected like i am doing that below + if ( gr!=Kopete::Group::topLevel() ) { + groups += gr->displayName()+","; + } + } + + if ( groups.length() ) { + groups.truncate( groups.length()-1 ); + } + cl->group = groups; + + return cl; +} + +QString +GaduContact::findBestContactName( const GaduContactsList::ContactLine* cl ) +{ + QString name; + + if ( cl == NULL ) { + return name; + } + + if ( cl->uin.isEmpty() ) { + return name; + } + + name = cl->uin; + + if ( cl->displayname.length() ) { + name = cl->displayname; + } + else { + // no name either + if ( cl->nickname.isEmpty() ) { + // maybe we can use fistname + surname ? + if ( cl->firstname.isEmpty() && cl->surname.isEmpty() ) { + name = cl->uin; + } + // what a shame, i have to use UIN than :/ + else { + if ( cl->firstname.isEmpty() ) { + name = cl->surname; + } + else { + if ( cl->surname.isEmpty() ) { + name = cl->firstname; + } + else { + name = cl->firstname + " " + cl->surname; + } + } + } + } + else { + name = cl->nickname; + } + } + + return name; +} + +void +GaduContact::messageAck() +{ + manager(Kopete::Contact::CanCreate)->messageSucceeded(); +} + +void +GaduContact::setIgnored( bool val ) +{ + ignored_ = val; +} + +bool +GaduContact::ignored() +{ + return ignored_; +} + +#include "gaducontact.moc" diff --git a/kopete/protocols/gadu/gaducontact.h b/kopete/protocols/gadu/gaducontact.h new file mode 100644 index 00000000..9a51838e --- /dev/null +++ b/kopete/protocols/gadu/gaducontact.h @@ -0,0 +1,119 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaducontact.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUCONTACT_H +#define GADUCONTACT_H + +#include <qpoint.h> +#include <qhostaddress.h> + +#include "gaducontactlist.h" + +#include "kopeteaccount.h" +#include "kopetecontact.h" +#include "kopetemessage.h" + +#include <libgadu.h> + +class KAction; +class GaduAccount; +namespace Kopete { class Account; } +namespace Kopete { class ChatSession; } +class KGaduNotify; +class QString; +class QStringList; + +class GaduContact : public Kopete::Contact +{ + Q_OBJECT + +public: + GaduContact( unsigned int, const QString&, Kopete::Account*, Kopete::MetaContact* ); + + virtual bool isReachable(); + virtual void serialize( QMap<QString, QString>&, QMap<QString, QString>& ); + virtual QPtrList<KAction>* customContextMenuActions(); + virtual QString identityId() const; + + GaduContactsList::ContactLine* contactDetails(); + + // this one set's only: + // email, firstname, surname, phonenr, ignored, nickname + // uin is const for GaduContact, and displayname needs to be changed through metaContact + bool setContactDetails( const GaduContactsList::ContactLine* ); + + void setParentIdentity( const QString& ); + void setIgnored( bool ); + bool ignored(); + + static QString findBestContactName( const GaduContactsList::ContactLine* ); + void changedStatus( KGaduNotify* ); + + uin_t uin() const; + + QHostAddress& contactIp(); + unsigned short contactPort(); + +public slots: + void slotUserInfo(); + void deleteContact(); + void messageReceived( Kopete::Message& ); + void messageSend( Kopete::Message&, Kopete::ChatSession* ); + void messageAck(); + void slotShowPublicProfile(); + void slotEditContact(); + virtual void sendFile( const KURL &sourceURL = KURL(), + const QString &fileName = QString::null, uint fileSize = 0L ); + + +protected: + virtual Kopete::ChatSession* manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CanCreate ); + void initActions(); + +private: + const uin_t uin_; + bool ignored_; + + Kopete::ChatSession* msgManager_; + QString description_; + QString parentIdentity_; + GaduAccount* account_; + + KAction* actionSendMessage_; + KAction* actionInfo_; + KAction* actionRemove_; + + QPtrList<Kopete::Contact> thisContact_; + + + QHostAddress remote_ip; + unsigned int remote_port; + unsigned int version; + unsigned int image_size; + + +private slots: + void slotChatSessionDestroyed(); + +}; + +#endif diff --git a/kopete/protocols/gadu/gaducontactlist.cpp b/kopete/protocols/gadu/gaducontactlist.cpp new file mode 100644 index 00000000..750f5224 --- /dev/null +++ b/kopete/protocols/gadu/gaducontactlist.cpp @@ -0,0 +1,207 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gaducontactlist.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + + +#include "gaducontactlist.h" +#include "qstringlist.h" +#include "kdebug.h" + +GaduContactsList::GaduContactsList() +{ +} + +GaduContactsList::~GaduContactsList() +{ +} + +GaduContactsList::GaduContactsList( QString sList ) +{ + QStringList::iterator stringIterator; + QStringList strList; + QString empty; + ContactLine cl; + bool email; + + if ( sList.isEmpty() || sList.isNull() ) { + return; + } + + if ( ( !sList.contains( '\n' ) && sList.contains( ';' ) ) || !sList.contains( ';' ) ) { + return; + } + + QStringList ln = QStringList::split( QChar( '\n' ), sList, true ); + QStringList::iterator lni = ln.begin( ); + + while( lni != ln.end() ) { + + QString cline = (*lni); + if ( cline.isNull() ) { + break; + } + + strList = QStringList::split( QChar( ';' ), cline, true ); + + stringIterator = strList.begin(); + + if ( strList.count() >= 12 ) { + email = true; + } + else { + email = false; + } + + +//each line ((firstname);(secondname);(nickname);(altnick);(tel);(group);(uin); +// new stuff attached at the end: +// email;aliveSoundfile;notifyType;msgSoundType;messageSound;offlineTo;homePhone; + stringIterator = strList.begin(); + + cl.firstname = (*stringIterator); + + if ( cl.firstname == QString( "i" ) ) { + kdDebug(14100) << cline << " ignored" << endl; + cl.ignored = true; + cl.uin = strList[6]; + ++lni; + cList.append( cl ); + continue; + } + else { + cl.ignored = false; + } + + cl.surname = (*++stringIterator); + cl.nickname = (*++stringIterator); + cl.displayname = (*++stringIterator); + cl.phonenr = (*++stringIterator); + cl.group = (*++stringIterator); + cl.uin = (*++stringIterator); + if ( email ) { + cl.email = (*++stringIterator); + // no use for custom sounds, at least now + ++stringIterator; + ++stringIterator; + ++stringIterator; + ++stringIterator; + + if ( stringIterator != strList.end() ) { + cl.offlineTo = (*++stringIterator) == QString("0") ? false : true; + cl.landline = (*++stringIterator); + } + } + else { + cl.email = empty; + } + + ++lni; + + if ( cl.uin.isNull() ) { + continue; + } + + cList.append( cl ); + } + + return; +} + +void +GaduContactsList::addContact( ContactLine& cl ) +{ + cList.append( cl ); +} + +void +GaduContactsList::addContact( + QString& displayname, + QString& group, + QString& uin, + QString& firstname, + QString& surname, + QString& nickname, + QString& phonenr, + QString& email, + bool ignored, + bool offlineTo, + QString& landline +) +{ + ContactLine cl; + + cl.displayname = displayname; + cl.group = group; + cl.uin = uin; + cl.firstname = firstname; + cl.surname = surname; + cl.nickname = nickname; + cl.phonenr = phonenr; + cl.email = email; + cl.ignored = ignored; + cl.offlineTo = offlineTo; + cl.landline = landline; + + cList.append( cl ); + +} + +QString +GaduContactsList::asString() +{ + QString contacts; + + for ( it = cList.begin(); it != cList.end(); ++it ) { + if ( (*it).ignored ) { + contacts += "i;;;;;;" + (*it).uin + "\n"; + } + else { +// name;surname;nick;displayname;telephone;group(s);uin;email;;0;0;;offlineTo;homePhone; + contacts += + (*it).firstname + ";"+ + (*it).surname + ";"+ + (*it).nickname + ";"+ + (*it).displayname + ";"+ + (*it).phonenr + ";"+ + (*it).group + ";"+ + (*it).uin + ";"+ + (*it).email + + ";;0;0;;" + + ((*it).offlineTo == true ? QString("1") : QString("0")) + + ";" + + (*it).landline + + ";\r\n"; + } + } + return contacts; +} + +unsigned int +GaduContactsList::size() +{ + return cList.size(); +} + +const GaduContactsList::ContactLine& +GaduContactsList::operator[]( unsigned int i ) +{ + return cList[i]; +} diff --git a/kopete/protocols/gadu/gaducontactlist.h b/kopete/protocols/gadu/gaducontactlist.h new file mode 100644 index 00000000..cfedeba4 --- /dev/null +++ b/kopete/protocols/gadu/gaducontactlist.h @@ -0,0 +1,67 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gaducontactlist.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#ifndef GADUCONTACTLIST_H +#define GADUCONTACTLIST_H + +#include <qvaluelist.h> + +class QString; + +class GaduContactsList +{ +public: + struct ContactLine { + + QString displayname; + QString group; + QString uin; + QString firstname; + QString surname; + QString nickname; + QString phonenr; + QString email; + bool ignored; + bool offlineTo; + QString landline; + }; + + GaduContactsList(); + GaduContactsList( QString ); + ~GaduContactsList(); + QString asString(); + void addContact( ContactLine &cl ); + void addContact( QString& displayname, QString& group, + QString& uin, QString& firstname, + QString& surname, QString& nickname, + QString& phonenr, QString& email, + bool ignored, bool offlineTo, + QString& landline + ); + unsigned int size(); + const GaduContactsList::ContactLine& operator[]( unsigned int i ); +private: + typedef QValueList<ContactLine> CList; + CList cList; + CList::iterator it; +}; +#endif diff --git a/kopete/protocols/gadu/gadudcc.cpp b/kopete/protocols/gadu/gadudcc.cpp new file mode 100644 index 00000000..ab6a6223 --- /dev/null +++ b/kopete/protocols/gadu/gadudcc.cpp @@ -0,0 +1,186 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadudcc.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <kdebug.h> + +#include "gadudccserver.h" +#include "gadudcc.h" +#include "gadudcctransaction.h" +#include "gaduaccount.h" + +#include "libgadu.h" + +#include <qsocketnotifier.h> +#include <qhostaddress.h> +#include <qmutex.h> +#include <qmap.h> +#include <qstring.h> + +volatile unsigned int GaduDCC::referenceCount = 0; + +GaduDCCServer* GaduDCC::dccServer = NULL; + +static QMutex initmutex; + +typedef QMap< unsigned int, GaduAccount* > gaduAccounts; +static gaduAccounts accounts; + +GaduDCC::GaduDCC( QObject* parent, const char* name ) +:QObject( parent, name ) +{ +} + +bool +GaduDCC::unregisterAccount() +{ + return unregisterAccount( accountId ); +} + +GaduAccount* +GaduDCC::account( unsigned int uin ) +{ + return accounts[ uin ]; +} + +bool +GaduDCC::unregisterAccount( unsigned int id ) +{ + initmutex.lock(); + + if ( id == 0 ) { + kdDebug(14100) << "ID nan" << endl; + initmutex.unlock(); + return false; + } + + if ( !accounts.contains( id ) ) { + kdDebug(14100) << "attempt to unregister not registered account" << endl; + initmutex.unlock(); + return false; + } + + accounts.remove( id ); + + if ( --referenceCount <= 0 ) { + kdDebug(14100) << "closing dcc socket" << endl; + referenceCount = 0; + if ( dccServer ) { + delete dccServer; + dccServer = NULL; + } + } + kdDebug(14100) << "reference count " << referenceCount << endl; + initmutex.unlock(); + + return true; +} + +bool +GaduDCC::registerAccount( GaduAccount* account ) +{ + unsigned int aid; + + if ( !account ) { + return false; + } + + if ( account->accountId().isEmpty() ) { + kdDebug(14100) << "attempt to register account with empty ID" << endl; + return false; + } + + initmutex.lock(); + + aid = account->accountId().toInt(); + + if ( accounts.contains( aid ) ) { + kdDebug(14100) << "attempt to register already registered account" << endl; + initmutex.unlock(); + return false; + } + + accountId = aid; + kdDebug( 14100 ) << " attempt to register " << accountId << endl; + + accounts[ accountId ] = account; + + referenceCount++; + + if ( !dccServer) { + dccServer = new GaduDCCServer(); + } + + connect( dccServer, SIGNAL( incoming( gg_dcc*, bool& ) ), SLOT( slotIncoming( gg_dcc*, bool& ) ) ); + + initmutex.unlock(); + + return true; +} +void +GaduDCC::slotIncoming( gg_dcc* incoming, bool& handled ) +{ + gg_dcc* newdcc; + GaduDCCTransaction* dt; + + kdDebug( 14100 ) << "slotIncomming for UIN: " << incoming->uin << endl; + + // no uin? I'm so sorry + // this screws file receiving (using kadu 0.4.x as peer) for me +// if ( !incoming->uin ) { +// return; +// } + + handled = true; + // TODO: limit number of connections per contact, or maybe even use parametr for that + newdcc = new gg_dcc; + memcpy( newdcc, incoming, sizeof( gg_dcc ) ); + dt = new GaduDCCTransaction( this ); + if ( dt->setupIncoming( newdcc ) == false ) { + // FIXME: write something to user, maybe, or not... + delete dt; + } +} + +GaduDCC::~GaduDCC() +{ + if ( accounts.contains( accountId ) ) { + kdDebug( 14100 ) << "unregister account " << accountId << " in destructor " << endl; + unregisterAccount( accountId ); + } +} + +unsigned int +GaduDCC::listeingPort() +{ + if ( dccServer ) { + return dccServer->listeingPort(); + } + return 0; +} + +#include "gadudcc.moc" diff --git a/kopete/protocols/gadu/gadudcc.h b/kopete/protocols/gadu/gadudcc.h new file mode 100644 index 00000000..37ae3e9a --- /dev/null +++ b/kopete/protocols/gadu/gadudcc.h @@ -0,0 +1,66 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadurichtextformat.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + + +#ifndef GADUDCC_H +#define GADUDCC_H + +#include <qobject.h> + +class QSocketNotifier; +class QHostAddress; +class QString; +class gg_dcc; +class GaduDCCTransaction; +class GaduAccount; +class GaduDCCServer; + +class GaduDCC: public QObject { + Q_OBJECT +public: + GaduDCC( QObject* parent, const char* name = NULL ); + ~GaduDCC(); + bool unregisterAccount(); + bool registerAccount( GaduAccount* ); + unsigned int listeingPort(); + void unset(); + void execute(); + GaduAccount* account( unsigned int ); + + QMap<unsigned int,QString> requests; +signals: + void dccConnect( GaduDCCTransaction* dccTransaction ); + +private slots: + void slotIncoming( gg_dcc*, bool& ); + +private: + void closeDCC(); + bool unregisterAccount( unsigned int ); + + unsigned int accountId; + + static GaduDCCServer* dccServer; + + static volatile unsigned int referenceCount; +}; + +#endif diff --git a/kopete/protocols/gadu/gadudccserver.cpp b/kopete/protocols/gadu/gadudccserver.cpp new file mode 100644 index 00000000..fb14277e --- /dev/null +++ b/kopete/protocols/gadu/gadudccserver.cpp @@ -0,0 +1,196 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadurichtextformat.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <kdebug.h> + +#include "gadudccserver.h" +#include "libgadu.h" +#include "gaduaccount.h" + +#include <qobject.h> +#include <qsocketnotifier.h> +#include <qhostaddress.h> + +GaduDCCServer::GaduDCCServer( QHostAddress* dccIp, unsigned int port ) +:QObject() +{ + kdDebug( 14100 ) << "dcc socket NULL, creating new liteining socket " << endl; + + // don't care about UIN at that point + dccSock = gg_dcc_socket_create( (unsigned int)-1, port ); + + if ( dccSock == NULL ){ + kdDebug(14100) << "attempt to initialize gadu-dcc listeing socket FAILED" << endl; + return; + } + + kdDebug(14100) << "attempt to initialize gadu-dcc listeing socket sucess" << endl; + + // using global variables sucks, don't have too much choice thou + if ( dccIp == NULL ) { + gg_dcc_ip = 0xffffffff; // 255.255.255.255 + } + else { + gg_dcc_ip = htonl( dccIp->ip4Addr() ); + } + gg_dcc_port = dccSock->port; + + createNotifiers( true ); + enableNotifiers( dccSock->check ); +} + +GaduDCCServer::~GaduDCCServer() +{ + kdDebug( 14100 ) << "gadu dcc server destructor " << endl; + closeDCC(); +} + +void +GaduDCCServer::closeDCC() +{ + if ( dccSock ) { + disableNotifiers(); + destroyNotifiers(); + gg_dcc_free( dccSock ); + dccSock = NULL; + gg_dcc_ip = 0; + gg_dcc_port = 0; + } + +} + +unsigned int +GaduDCCServer::listeingPort() +{ + if ( dccSock == NULL ) { + return 0; + } +// else + return dccSock->port; +} + +void +GaduDCCServer::destroyNotifiers() +{ + disableNotifiers(); + if ( read_ ) { + delete read_; + read_ = NULL; + } + if ( write_ ) { + delete write_; + write_ = NULL; + } +} + +void +GaduDCCServer::createNotifiers( bool connect ) +{ + if ( !dccSock ){ + return; + } + + read_ = new QSocketNotifier( dccSock->fd, QSocketNotifier::Read, this ); + read_->setEnabled( false ); + + write_ = new QSocketNotifier( dccSock->fd, QSocketNotifier::Write, this ); + write_->setEnabled( false ); + + if ( connect ) { + QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( watcher() ) ); + QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( watcher() ) ); + } +} + +void +GaduDCCServer::enableNotifiers( int checkWhat ) +{ + if( (checkWhat & GG_CHECK_READ) && read_ ) { + read_->setEnabled( true ); + } + if( (checkWhat & GG_CHECK_WRITE) && write_ ) { + write_->setEnabled( true ); + } +} + +void +GaduDCCServer::disableNotifiers() +{ + if ( read_ ) { + read_->setEnabled( false ); + } + if ( write_ ) { + write_->setEnabled( false ); + } +} + +void +GaduDCCServer::watcher() { + + gg_event* dccEvent; + bool handled = false; + + disableNotifiers(); + + dccEvent = gg_dcc_watch_fd( dccSock ); + if ( ! dccEvent ) { + // connection is fucked + // we should try to reenable it +// closeDCC(); + return; + } + switch ( dccEvent->type ) { + case GG_EVENT_NONE: + break; + case GG_EVENT_DCC_ERROR: + kdDebug( 14100 ) << " dcc error occured " << endl; + break; + case GG_EVENT_DCC_NEW: + // I do expect reciver to set this boolean to true if he handled signal + // if so, no other reciver should be bothered with it, and I shall not close it + // otherwise connection is closed as not handled + emit incoming( dccEvent->event.dcc_new, handled ); + if ( !handled ) { + if ( dccEvent->event.dcc_new->file_fd > 0) { + close( dccEvent->event.dcc_new->file_fd ); + } + gg_dcc_free( dccEvent->event.dcc_new ); + } + break; + default: + kdDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type << endl; + break; + } + + if ( dccEvent ) { + gg_free_event( dccEvent ); + } + + enableNotifiers( dccSock->check ); +} +#include "gadudccserver.moc" diff --git a/kopete/protocols/gadu/gadudccserver.h b/kopete/protocols/gadu/gadudccserver.h new file mode 100644 index 00000000..50916533 --- /dev/null +++ b/kopete/protocols/gadu/gadudccserver.h @@ -0,0 +1,66 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadurichtextformat.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + + +#ifndef GADUDCCSERVER_H +#define GADUDCCSERVER_H + +#include <qobject.h> +#include <qhostaddress.h> + +class QSocketNotifier; +class QString; +class gg_dcc; +class GaduDCCTransaction; +class GaduAccount; + +class GaduDCCServer: public QObject { + Q_OBJECT +public: + GaduDCCServer( QHostAddress* dccIp = NULL, unsigned int port = 1550 ); + ~GaduDCCServer(); + unsigned int listeingPort(); + +signals: + void incoming( gg_dcc*, bool& ); + +private slots: + void watcher(); + +private: + void enableNotifiers( int ); + void disableNotifiers(); + void checkDescriptor(); + + void destroyNotifiers(); + void createNotifiers( bool ); + void closeDCC(); + + QHostAddress config_dccip; + QHostAddress config_extip; + + gg_dcc* dccSock; + + QSocketNotifier* read_; + QSocketNotifier* write_; +}; + +#endif diff --git a/kopete/protocols/gadu/gadudcctransaction.cpp b/kopete/protocols/gadu/gadudcctransaction.cpp new file mode 100644 index 00000000..561852fe --- /dev/null +++ b/kopete/protocols/gadu/gadudcctransaction.cpp @@ -0,0 +1,454 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadudcctransaction.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include "kopetetransfermanager.h" +#include "kopetemetacontact.h" +#include "kopeteuiglobal.h" + +#include <qsocketnotifier.h> +#include <qfile.h> + +#include "gadudcctransaction.h" +#include "gaducontact.h" +#include "gaduaccount.h" +#include "gadudcc.h" + +#include "libgadu.h" + +GaduDCCTransaction::GaduDCCTransaction( GaduDCC* parent, const char* name ) +:QObject( parent, name ), gaduDCC_( parent ) +{ + read_ = NULL; + write_ = NULL; + contact = NULL; + transfer_ = NULL; + dccSock_ = NULL; + peer = 0; +} + +GaduDCCTransaction::~GaduDCCTransaction() +{ + closeDCC(); +} + +unsigned int +GaduDCCTransaction::recvUIN() +{ + if ( dccSock_ ) { + return dccSock_->uin; + } + return 0; +} + +unsigned int +GaduDCCTransaction::peerUIN() +{ + if ( dccSock_ ) { + return dccSock_->peer_uin; + } + return 0; +} + +bool +GaduDCCTransaction::setupOutgoing( GaduContact* peerContact, QString& filePath ) +{ + GaduContact* me; + GaduAccount* metoo; + + if ( !peerContact ) { + return false; + } + + me = static_cast<GaduContact*>( peerContact->account()->myself() ); + + QString aaa = peerContact->contactIp().toString(); + kdDebug( 14100 ) << "slotOutgoin for UIN: " << peerContact->uin() << " port " << peerContact->contactPort() << " ip " <<aaa<< endl; + kdDebug( 14100 ) << "File path is " << filePath << endl; + + if ( peerContact->contactPort() >= 10 ) { + dccSock_ = gg_dcc_send_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), me->uin(), peerContact->uin() ); + gg_dcc_fill_file_info(dccSock_,filePath.ascii()); + transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( peerContact, + filePath, dccSock_->file_info.size, peerContact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing ); + createNotifiers( true ); + enableNotifiers( dccSock_->check ); + } + else { + kdDebug( 14100 ) << "Peer " << peerContact->uin() << " is passive, requesting reverse connection" << endl; + metoo = static_cast<GaduAccount*>( me->account() ); + gaduDCC_->requests[peerContact->uin()]=filePath; + metoo->dccRequest( peerContact ); + } + + return false; +} + +bool +GaduDCCTransaction::setupIncoming( const unsigned int uin, GaduContact* peerContact ) +{ + + if ( !peerContact ) { + kdDebug( 14100 ) << "setupIncoming called with peerContact == NULL " << endl; + return false; + } + + QString aaa = peerContact->contactIp().toString(); + kdDebug( 14100 ) << "setupIncoming for UIN: " << uin << " port " << peerContact->contactPort() << " ip " <<aaa<< endl; + + peer = peerContact->uin(); + dccSock_ = gg_dcc_get_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), uin, peer ); + + contact = peerContact; + return setupIncoming( dccSock_ ); + +} + +bool +GaduDCCTransaction::setupIncoming( gg_dcc* dccS ) +{ + if ( !dccS ) { + kdDebug(14100) << "gg_dcc_get_file failed in GaduDCCTransaction::setupIncoming" << endl; + return false; + } + + dccSock_ = dccS; + + peer = dccS->uin; + + connect ( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString & ) ), + this, SLOT( slotIncomingTransferAccepted ( Kopete::Transfer *, const QString & ) ) ); + connect ( Kopete::TransferManager::transferManager(), SIGNAL( refused( const Kopete::FileTransferInfo & ) ), + this, SLOT( slotTransferRefused( const Kopete::FileTransferInfo & ) ) ); + + incoming = true; + createNotifiers( true ); + enableNotifiers( dccSock_->check ); + + return true; +} + + +void +GaduDCCTransaction::closeDCC() +{ + kdDebug(14100) << "closeDCC()" << endl; + + disableNotifiers(); + destroyNotifiers(); + gg_dcc_free( dccSock_ ); + dccSock_ = NULL; +} + +void +GaduDCCTransaction::destroyNotifiers() +{ + disableNotifiers(); + if ( read_ ) { + delete read_; + read_ = NULL; + } + if ( write_ ) { + delete write_; + write_ = NULL; + } +} + +void +GaduDCCTransaction::createNotifiers( bool connect ) +{ + if ( !dccSock_ ){ + return; + } + + read_ = new QSocketNotifier( dccSock_->fd, QSocketNotifier::Read, this ); + read_->setEnabled( false ); + + write_ = new QSocketNotifier( dccSock_->fd, QSocketNotifier::Write, this ); + write_->setEnabled( false ); + + if ( connect ) { + QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( watcher() ) ); + QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( watcher() ) ); + } +} + +void +GaduDCCTransaction::enableNotifiers( int checkWhat ) +{ + if( (checkWhat & GG_CHECK_READ) && read_ ) { + read_->setEnabled( true ); + } + if( (checkWhat & GG_CHECK_WRITE) && write_ ) { + write_->setEnabled( true ); + } +} + +void +GaduDCCTransaction::disableNotifiers() +{ + if ( read_ ) { + read_->setEnabled( false ); + } + if ( write_ ) { + write_->setEnabled( false ); + } +} +void +GaduDCCTransaction::slotIncomingTransferAccepted ( Kopete::Transfer* transfer, const QString& fileName ) +{ + + if ( (long)transfer->info().transferId () != transferId_ ) { + return; + } + + transfer_ = transfer; + localFile_.setName( fileName ); + + if ( localFile_.exists() ) { + KGuiItem resumeButton( i18n ( "&Resume" ) ); + KGuiItem overwriteButton( i18n ( "Over&write" ) ); + switch ( KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget (), + i18n( "The file %1 already exists, do you want to resume or overwrite it?" ).arg( fileName ), + i18n( "File Exists: %1" ).arg( fileName ), resumeButton, overwriteButton ) ) + { + // resume + case KMessageBox::Yes: + if ( localFile_.open( IO_WriteOnly | IO_Append ) ) { + dccSock_->offset = localFile_.size(); + dccSock_->file_fd = localFile_.handle(); + } + break; + // overwrite + case KMessageBox::No: + if ( localFile_.open( IO_ReadWrite ) ) { + dccSock_->offset = 0; + dccSock_->file_fd = localFile_.handle(); + } + break; + + // cancel + default: + closeDCC(); + deleteLater(); + return; + break; + } + if ( localFile_.handle() < 1 ) { + closeDCC(); + deleteLater(); + return; + } + } + else { + // overwrite by default + if ( localFile_.open( IO_ReadWrite ) == FALSE ) { + transfer->slotError ( KIO::ERR_COULD_NOT_WRITE, fileName ); + closeDCC(); + deleteLater (); + return; + } + dccSock_->offset = 0; + dccSock_->file_fd = localFile_.handle(); + } + + connect ( transfer, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotTransferResult() ) ); + + // reenable notifiers + enableNotifiers( dccSock_->check ); + +} + +void +GaduDCCTransaction::slotTransferResult() +{ + if ( transfer_->error() == KIO::ERR_USER_CANCELED ) { + closeDCC(); + deleteLater(); + } +} + +void +GaduDCCTransaction::slotTransferRefused ( const Kopete::FileTransferInfo& transfer ) +{ + if ( (long)transfer.transferId () != transferId_ ) + return; + closeDCC(); + deleteLater(); +} + +void +GaduDCCTransaction::askIncommingTransfer() +{ + + transferId_ = Kopete::TransferManager::transferManager()->askIncomingTransfer ( contact, + QString( (const char*)dccSock_->file_info.filename ), dccSock_->file_info.size ); + +} + +void +GaduDCCTransaction::watcher() { + + gg_event* dccEvent; + GaduAccount* account; + + disableNotifiers(); + + dccEvent = gg_dcc_watch_fd( dccSock_ ); + if ( ! dccEvent ) { + // connection is fucked + closeDCC(); + return; + } + switch ( dccEvent->type ) { + case GG_EVENT_DCC_CLIENT_ACCEPT: + kdDebug(14100) << " GG_EVENT_DCC_CLIENT_ACCEPT " << endl; + // check dccsock->peer_uin, if unknown, fuck it; + + // is it for us ? + account = gaduDCC_->account( dccSock_->uin ); + if ( !account ) { + kdDebug( 14100 ) << " this dcc transaction is for uin " << dccSock_->uin << ", which is not quite for me... closing" << endl; + // unknown 'to' ?, we're off + gg_free_event( dccEvent ); + closeDCC(); + deleteLater(); + return; + } + + if ( !peer ) { + contact = static_cast<GaduContact*> (account->contacts()[ QString::number( dccSock_->peer_uin ) ]); + } + else { + contact = static_cast<GaduContact*> (account->contacts()[ QString::number( peer ) ]); + } + + if ( contact == NULL ) { + // refusing, contact on the list + kdDebug(14100) << " dcc connection from " << dccSock_->peer_uin << " refused, UIN not on the list " <<endl; + gg_free_event( dccEvent ); + closeDCC(); + // emit error + deleteLater(); + return; + } + else { + // ask user to accept that transfer + kdDebug(14100) << " dcc accepted from " << dccSock_->peer_uin << endl; + } + + break; + case GG_EVENT_DCC_CALLBACK: + kdDebug(14100) << "GG_DCC_EVENT_CALLBACK" << endl; + break; + case GG_EVENT_NONE: + kdDebug(14100) << " GG_EVENT_NONE" << endl; + // update gui with progress + if ( transfer_ ) { + transfer_->slotProcessed( dccSock_->offset ); + } + break; + + case GG_EVENT_DCC_NEED_FILE_ACK: + kdDebug(14100) << " GG_EVENT_DCC_NEED_FILE_ACK " << endl; + gg_free_event( dccEvent ); + askIncommingTransfer(); + return; + break; + case GG_EVENT_DCC_NEED_FILE_INFO: + if (gaduDCC_->requests.contains(dccSock_->peer_uin)) { + QString filePath = gaduDCC_->requests[dccSock_->peer_uin]; + kdDebug() << "Callback request found. Sending " << filePath << endl; + gaduDCC_->requests.remove(dccSock_->peer_uin); + gg_dcc_fill_file_info(dccSock_,filePath.ascii()); + transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( contact, + filePath, dccSock_->file_info.size, contact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing ); + } else { + gg_free_event( dccEvent ); + closeDCC(); + deleteLater(); + return; + } + break; + + case GG_EVENT_DCC_ERROR: + kdDebug(14100) << " GG_EVENT_DCC_ERROR :" << dccEvent->event.dcc_error << endl; + if ( transfer_ ) { + switch( dccEvent->event.dcc_error ) { + + case GG_ERROR_DCC_REFUSED: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Connection to peer was refused; it possibly does not listen for incoming connections." ) ); + break; + + case GG_ERROR_DCC_EOF: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer transaction was not agreed by peer." ) ); + break; + + case GG_ERROR_DCC_HANDSHAKE: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File-transfer handshake failure." ) ); + break; + case GG_ERROR_DCC_FILE: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer had problems with the file." ) ); + break; + case GG_ERROR_DCC_NET: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "There was network error during file transfer." ) ); + break; + default: + transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Unknown File-Transfer error." ) ); + break; + } + } + gg_free_event( dccEvent ); + closeDCC(); + deleteLater(); + return; + + case GG_EVENT_DCC_DONE: + if ( transfer_ ) { + transfer_->slotComplete(); + } + closeDCC(); + deleteLater(); + return; + + default: + kdDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type << endl; + break; + } + + if ( dccEvent ) { + gg_free_event( dccEvent ); + } + + enableNotifiers( dccSock_->check ); +} + +#include "gadudcctransaction.moc" diff --git a/kopete/protocols/gadu/gadudcctransaction.h b/kopete/protocols/gadu/gadudcctransaction.h new file mode 100644 index 00000000..4c2edb58 --- /dev/null +++ b/kopete/protocols/gadu/gadudcctransaction.h @@ -0,0 +1,87 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadudcctransaction.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + + +#ifndef GADUDCCTRANS_H +#define GADUDCCTRANS_H + +#include <qobject.h> +#include <qfile.h> + +class QSocketNotifier; +class gg_dcc; +class GaduAccount; +class GaduContact; +namespace Kopete { class Transfer; } +namespace Kopete { class FileTransferInfo; } +class GaduDCC; + +class GaduDCCTransaction: QObject { + Q_OBJECT +public: + GaduDCCTransaction( GaduDCC*, const char* name = NULL ); + ~GaduDCCTransaction(); + + bool setupIncoming( const unsigned int, GaduContact* ); + bool setupIncoming( gg_dcc* ); + bool setupOutgoing( GaduContact*, QString& ); + unsigned int recvUIN(); + unsigned int peerUIN(); + +public slots: + +signals: + +protected: + +protected slots: + +private slots: + void watcher(); + void slotIncomingTransferAccepted ( Kopete::Transfer*, const QString& ); + void slotTransferRefused ( const Kopete::FileTransferInfo& ); + void slotTransferResult(); + +private: + void enableNotifiers( int ); + void disableNotifiers(); + void checkDescriptor(); + void closeDCC(); + void destroyNotifiers(); + void createNotifiers( bool ); + void askIncommingTransfer(); + + gg_dcc* dccSock_; + + QSocketNotifier* read_; + QSocketNotifier* write_; + + GaduContact* contact; + + Kopete::Transfer* transfer_; + long transferId_; + QFile localFile_; + int peer; + unsigned int incoming; + GaduDCC* gaduDCC_; +}; + +#endif diff --git a/kopete/protocols/gadu/gadueditaccount.cpp b/kopete/protocols/gadu/gadueditaccount.cpp new file mode 100644 index 00000000..250ae4cd --- /dev/null +++ b/kopete/protocols/gadu/gadueditaccount.cpp @@ -0,0 +1,263 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadueditaccount.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "gadueditaccount.h" +#include "gaduaccount.h" +#include "gaduprotocol.h" +#include "gadusession.h" + +#include <qradiobutton.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qstring.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qbutton.h> +#include <qregexp.h> +#include <qpushbutton.h> +#include <qgroupbox.h> + +#include <klineedit.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <klocale.h> +#include <kpassdlg.h> + +#include "kopetepasswordwidget.h" + +GaduEditAccount::GaduEditAccount( GaduProtocol* proto, Kopete::Account* ident, QWidget* parent, const char* name ) +: GaduAccountEditUI( parent, name ), KopeteEditAccountWidget( ident ), protocol_( proto ), rcmd( 0 ) +{ + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + isSsl = true; +#else + isSsl = false; +#endif + + useTls_->setDisabled( !isSsl ); + + if ( account() == NULL ) { + useTls_->setCurrentItem( GaduAccount::TLS_no ); + registerNew->setEnabled( true ); + account_ = NULL; + } + else { + account_ = static_cast<GaduAccount*>(ident); + + registerNew->setDisabled( true ); + loginEdit_->setDisabled( true ); + loginEdit_->setText( account_->accountId() ); + + passwordWidget_->load( &account_->password() ); + + QString nick = account()->myself()->property( + Kopete::Global::Properties::self()->nickName() ).value().toString(); + if ( nick.isEmpty() ) { + nick = account_->myself()->contactId(); + } + + nickName->setText( nick ); + + autoLoginCheck_->setChecked( account_->excludeConnect() ); + dccCheck_->setChecked( account_->dccEnabled() ); + useTls_->setCurrentItem( isSsl ? ( account_->useTls() ) : 2 ); + ignoreCheck_->setChecked( account_->ignoreAnons() ); + + connect( account(), SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ), + SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) ); + connectLabel->setText( i18n( "personal information being fetched from server", + "<p align=\"center\">Fetching from server</p>" ) ); + seqNr = account_->getPersonalInformation(); + } + + connect( registerNew, SIGNAL( clicked( ) ), SLOT( registerNewAccount( ) ) ); + + QWidget::setTabOrder( loginEdit_, passwordWidget_->mRemembered ); + QWidget::setTabOrder( passwordWidget_->mRemembered, passwordWidget_->mPassword ); + QWidget::setTabOrder( passwordWidget_->mPassword, autoLoginCheck_ ); +} + +void +GaduEditAccount::publishUserInfo() +{ + ResLine sr; + + enableUserInfo( false ); + + sr.firstname = uiName->text(); + sr.surname = uiSurname->text(); + sr.nickname = nickName->text(); + sr.age = uiYOB->text(); + sr.city = uiCity->text(); + sr.meiden = uiMeiden->text(); + sr.orgin = uiOrgin->text(); + + kdDebug(14100) << uiGender->currentItem() << " gender " << endl; + if ( uiGender->currentItem() == 1 ) { + kdDebug(14100) << "so you become female now" << endl; + sr.gender = QString( GG_PUBDIR50_GENDER_SET_FEMALE ); + } + if ( uiGender->currentItem() == 2 ) { + kdDebug(14100) << "so you become male now" << endl; + sr.gender = QString( GG_PUBDIR50_GENDER_SET_MALE ); + } + + if ( account_ ) { + account_->publishPersonalInformation( sr ); + } +} + +void +GaduEditAccount::slotSearchResult( const SearchResult& result, unsigned int seq ) +{ + if ( !( seq != 0 && seqNr != 0 && seq == seqNr ) ) { + return; + } + + connectLabel->setText( " " ); + + uiName->setText( result[0].firstname ); + uiSurname->setText( result[0].surname ); + nickName->setText( result[0].nickname ); + uiYOB->setText( result[0].age ); + uiCity->setText( result[0].city ); + + kdDebug( 14100 ) << "gender found: " << result[0].gender << endl; + if ( result[0].gender == QString( GG_PUBDIR50_GENDER_SET_FEMALE ) ) { + uiGender->setCurrentItem( 1 ); + kdDebug(14100) << "looks like female" << endl; + } + else { + if ( result[0].gender == QString( GG_PUBDIR50_GENDER_SET_MALE ) ) { + uiGender->setCurrentItem( 2 ); + kdDebug( 14100 ) <<" looks like male" << endl; + } + } + + uiMeiden->setText( result[0].meiden ); + uiOrgin->setText( result[0].orgin ); + + enableUserInfo( true ); + + disconnect( SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) ); +} + +void +GaduEditAccount::enableUserInfo( bool e ) +{ + uiName->setEnabled( e ); + uiSurname->setEnabled( e ); + uiYOB->setEnabled( e ); + uiCity->setEnabled( e ); + uiGender->setEnabled( e ); + uiMeiden->setEnabled( e ); + uiOrgin->setEnabled( e ); + +// connectLabel->setEnabled( !e ); +} + +void +GaduEditAccount::registerNewAccount() +{ + registerNew->setDisabled( true ); + regDialog = new GaduRegisterAccount( NULL , "Register account dialog" ); + connect( regDialog, SIGNAL( registeredNumber( unsigned int, QString ) ), SLOT( newUin( unsigned int, QString ) ) ); + if ( regDialog->exec() != QDialog::Accepted ) { + loginEdit_->setText( "" ); + return; + } + registerNew->setDisabled( false ); +} + +void +GaduEditAccount::registrationFailed() +{ + KMessageBox::sorry( this, i18n( "<b>Registration FAILED.</b>" ), i18n( "Gadu-Gadu" ) ); +} + +void +GaduEditAccount::newUin( unsigned int uin, QString password ) +{ + if ( uin ) { + loginEdit_->setText( QString::number( uin ) ); + passwordWidget_->setPassword( password ); + } + else { + // registration failed, enable button again + registerNew->setDisabled( false ); + } +} + +bool +GaduEditAccount::validateData() +{ + + if ( loginEdit_->text().isEmpty() ) { + KMessageBox::sorry( this, i18n( "<b>Enter UIN please.</b>" ), i18n( "Gadu-Gadu" ) ); + return false; + } + + if ( loginEdit_->text().toInt() < 0 || loginEdit_->text().toInt() == 0 ) { + KMessageBox::sorry( this, i18n( "<b>UIN should be a positive number.</b>" ), i18n( "Gadu-Gadu" ) ); + return false; + } + + if ( !passwordWidget_->validate() ) { + KMessageBox::sorry( this, i18n( "<b>Enter password please.</b>" ), i18n( "Gadu-Gadu" ) ); + return false; + } + + return true; +} + +Kopete::Account* +GaduEditAccount::apply() +{ + publishUserInfo(); + + if ( account() == NULL ) { + setAccount( new GaduAccount( protocol_, loginEdit_->text() ) ); + account_ = static_cast<GaduAccount*>( account() ); + } + + account_->setExcludeConnect( autoLoginCheck_->isChecked() ); + + passwordWidget_->save( &account_->password() ); + + account_->myself()->setProperty( Kopete::Global::Properties::self()->nickName(), nickName->text() ); + + // this is changed only here, so i won't add any proper handling now + account_->configGroup()->writeEntry( QString::fromAscii( "nickName" ), nickName->text() ); + + account_->setExcludeConnect( autoLoginCheck_->isChecked() ); + account_->setUseTls( (GaduAccount::tlsConnection) useTls_->currentItem() ); + account_->setIgnoreAnons( ignoreCheck_->isChecked() ); + + if ( account_->setDcc( dccCheck_->isChecked() ) == false ) { + KMessageBox::sorry( this, i18n( "<b>Starting DCC listening socket failed; dcc is not working now.</b>" ), i18n( "Gadu-Gadu" ) ); + } + + return account(); +} + +#include "gadueditaccount.moc" + diff --git a/kopete/protocols/gadu/gadueditaccount.h b/kopete/protocols/gadu/gadueditaccount.h new file mode 100644 index 00000000..87775f2a --- /dev/null +++ b/kopete/protocols/gadu/gadueditaccount.h @@ -0,0 +1,63 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadueditaccount.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUEDITACCOUNT_H +#define GADUEDITACCOUNT_H + +#include "gadueditaccountui.h" +#include "editaccountwidget.h" +#include "gaduregisteraccount.h" +#include "gadusession.h" + +class GaduAccount; +class GaduProtocol; + +namespace Kopete { class Account; } + +class GaduEditAccount : public GaduAccountEditUI, public KopeteEditAccountWidget +{ + Q_OBJECT + +public: + GaduEditAccount( GaduProtocol*, Kopete::Account*, QWidget* parent = 0, const char* name = 0 ); + virtual bool validateData(); + Kopete::Account* apply(); + +private slots: + void registerNewAccount(); + void newUin( unsigned int, QString ); + void registrationFailed(); + void slotSearchResult( const SearchResult&, unsigned int ); + +private: + void enableUserInfo( bool ); + void publishUserInfo(); + + GaduProtocol* protocol_; + bool reg_in_progress; + bool isSsl; + RegisterCommand* rcmd; + GaduRegisterAccount* regDialog; + GaduAccount* account_; + unsigned int seqNr; +}; + +#endif diff --git a/kopete/protocols/gadu/gadueditcontact.cpp b/kopete/protocols/gadu/gadueditcontact.cpp new file mode 100644 index 00000000..3844e691 --- /dev/null +++ b/kopete/protocols/gadu/gadueditcontact.cpp @@ -0,0 +1,203 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadueditcontact.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include "gaduaccount.h" +#include "gaducontact.h" +#include "gadueditcontact.h" +#include "kopeteonlinestatus.h" + +#include "gaducontactlist.h" +#include "gaduadd.h" + +#include <ktextedit.h> +#include <klocale.h> +#include <kdebug.h> +#include <kopetegroup.h> +#include <kopetecontactlist.h> +#include <kopetemetacontact.h> + +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <qlineedit.h> +#include <qlayout.h> +#include <qlistview.h> +#include <qptrlist.h> + +#include <krestrictedline.h> + +// FIXME: this and gaduadcontactpage should have one base class, with some code duplicated in both. + +GaduEditContact::GaduEditContact( GaduAccount* account, GaduContact* contact, + QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "Edit Contact's Properties" ), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true ), account_( account ), contact_( contact ) +{ + if ( contact && account ) { + cl_ = contact->contactDetails(); + } + else { + return; + } + + init(); + fillGroups(); + fillIn(); +} + +GaduEditContact::GaduEditContact( GaduAccount* account, GaduContactsList::ContactLine* clin, + QWidget* parent , const char* name ) +: KDialogBase( parent, name, true, i18n( "Edit Contact's Properties" ), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true ), account_( account ), contact_( NULL ) +{ + + if ( !account ) { + return; + } + cl_ = clin; + init(); + fillGroups(); + fillIn(); +} + +void +GaduEditContact::fillGroups() +{ + Kopete::Group *g, *cg; + QPtrList<Kopete::Group> cgl; + QPtrList<Kopete::Group> gl; + + if ( contact_ ) { + cgl = contact_->metaContact()->groups(); + } + + gl = Kopete::ContactList::self()->groups(); + + for( g = gl.first(); g; g = gl.next() ) { + if ( g->type() == Kopete::Group::Temporary ) { + continue; + } + QCheckListItem* item = new QCheckListItem( ui_->groups, g->displayName(), QCheckListItem::CheckBox ); + // FIXME: optimize this O(2) search + for( cg = cgl.first(); cg; cg = cgl.next() ) { + if ( cg->groupId() == g->groupId() ) { + item->setOn( TRUE ); + break; + } + } + kdDebug(14100) << g->displayName() << " " << g->groupId() << endl; + } +} + +void +GaduEditContact::init() +{ + ui_ = new GaduAddUI( this ); + setMainWidget( ui_ ); + ui_->addEdit_->setValidChars( "1234567890" ); + + // fill values from cl into proper fields on widget + + show(); + connect( this, SIGNAL( okClicked() ), SLOT( slotApply() ) ); + connect( ui_->groups, SIGNAL( clicked( QListViewItem * ) ), SLOT( listClicked( QListViewItem * ) ) ); +} + +void +GaduEditContact::listClicked( QListViewItem* /*item*/ ) +{ + +} + +void +GaduEditContact::fillIn() +{ +// grey it out, it shouldn't be editable + ui_->addEdit_->setReadOnly( true ); + ui_->addEdit_->setText( cl_->uin ); + + ui_->fornameEdit_->setText( cl_->firstname ); + ui_->snameEdit_->setText( cl_->surname ); + ui_->nickEdit_->setText( cl_->nickname ); + ui_->emailEdit_->setText( cl_->email ); + ui_->telephoneEdit_->setText( cl_->phonenr ); +// ui_->notAFriend_; + +} + +void +GaduEditContact::slotApply() +{ + QPtrList<Kopete::Group> gl; + Kopete::Group* group; + + cl_->firstname = ui_->fornameEdit_->text().stripWhiteSpace(); + cl_->surname = ui_->snameEdit_->text().stripWhiteSpace(); + cl_->nickname = ui_->nickEdit_->text().stripWhiteSpace(); + cl_->email = ui_->emailEdit_->text().stripWhiteSpace(); + cl_->phonenr = ui_->telephoneEdit_->text().stripWhiteSpace(); + + if ( contact_ == NULL ) { + // contact doesn't exists yet, create it and set all the details + bool s = account_->addContact( cl_->uin, GaduContact::findBestContactName( cl_ ), 0L, Kopete::Account::DontChangeKABC); + if ( s == false ) { + kdDebug(14100) << "There was a problem adding UIN "<< cl_->uin << "to users list" << endl; + return; + } + contact_ = static_cast<GaduContact*>( account_->contacts()[ cl_->uin ] ); + if ( contact_ == NULL ) { + kdDebug(14100) << "oops, no Kopete::Contact in contacts()[] for some reason, for \"" << cl_->uin << "\"" << endl; + return; + } + } + + contact_->setContactDetails( cl_ ); + + gl = Kopete::ContactList::self()->groups(); + for ( QListViewItemIterator it( ui_->groups ); it.current(); ++it ) { + QCheckListItem *check = dynamic_cast<QCheckListItem *>( it.current() ); + + if ( !check ) { + continue; + } + + if ( check->isOn() ) { + for( group = gl.first(); group; group = gl.next() ) { + if ( group->displayName() == check->text() ) { + contact_->metaContact()->addToGroup( group ); + } + } + } + else { + // check metacontact's in the group, and if so, remove it from + for( group = gl.first(); group; group = gl.next() ) { + if ( group->displayName() == check->text() ) { + contact_->metaContact()->removeFromGroup( group ); + } + } + } + } + + if( contact_->metaContact()->groups().isEmpty() == TRUE ) + contact_->metaContact()->addToGroup( Kopete::Group::topLevel() ); +} +#include "gadueditcontact.moc" diff --git a/kopete/protocols/gadu/gadueditcontact.h b/kopete/protocols/gadu/gadueditcontact.h new file mode 100644 index 00000000..6b209b79 --- /dev/null +++ b/kopete/protocols/gadu/gadueditcontact.h @@ -0,0 +1,60 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gadueditcontact.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUEDITCONTACT_H +#define GADUEDITCONTACT_H + +#include <kdialogbase.h> + +class GaduAccount; +class GaduAddUI; +class QLabel; +class QString; +class QWidget; +class GaduContact; +class GaduContactsList::ContactLine; +class QListViewItem; + +class GaduEditContact : public KDialogBase +{ + Q_OBJECT + +public: + GaduEditContact( GaduAccount*, GaduContact*, + QWidget* parent = 0, const char* name = 0 ); + GaduEditContact( GaduAccount*, GaduContactsList::ContactLine*, + QWidget* parent = 0, const char* name = 0 ); +protected slots: + void slotApply(); + void listClicked( QListViewItem* ); +private: + + void init(); + void fillIn(); + void fillGroups(); + GaduAccount* account_; + GaduContact* contact_; + GaduAddUI* ui_; + GaduContactsList::ContactLine* cl_; +}; + +#endif diff --git a/kopete/protocols/gadu/gaduprotocol.cpp b/kopete/protocols/gadu/gaduprotocol.cpp new file mode 100644 index 00000000..cab6bfe0 --- /dev/null +++ b/kopete/protocols/gadu/gaduprotocol.cpp @@ -0,0 +1,243 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaduprotocol.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <kdebug.h> +#include <kgenericfactory.h> +#include <kconfig.h> + +#include <libgadu.h> + +#include "gaduaccount.h" +#include "gaducontact.h" +#include "gaduprotocol.h" + +#include "gadueditaccount.h" +#include "gaduaddcontactpage.h" + +#include "kopeteaccountmanager.h" +#include "kopeteaccount.h" +#include "kopetemetacontact.h" +#include "kopeteglobal.h" +#include "kopeteonlinestatusmanager.h" + +typedef KGenericFactory<GaduProtocol> GaduProtocolFactory; + +K_EXPORT_COMPONENT_FACTORY( kopete_gadu, KGenericFactory<GaduProtocol>( "kopete_gadu" ) ) + +GaduProtocol* GaduProtocol::protocolStatic_ = 0L; + +GaduProtocol::GaduProtocol( QObject* parent, const char* name, const QStringList& ) +:Kopete::Protocol( GaduProtocolFactory::instance(), parent, name ), + propFirstName(Kopete::Global::Properties::self()->firstName()), + propLastName(Kopete::Global::Properties::self()->lastName()), + propEmail(Kopete::Global::Properties::self()->emailAddress()), + propAwayMessage(Kopete::Global::Properties::self()->awayMessage()), + propPhoneNr(Kopete::Global::Properties::self()->privatePhone()), + defaultAccount_( 0 ), + gaduStatusBlocked_( Kopete::OnlineStatus::Away, GG_STATUS_BLOCKED, this, GG_STATUS_BLOCKED, + "gg_ignored", i18n( "Blocked" ) ), + gaduStatusOffline_( Kopete::OnlineStatus::Offline, GG_STATUS_NOT_AVAIL, this, GG_STATUS_NOT_AVAIL, + "gg_offline", i18n( "Offline" ) , i18n( "O&ffline" ) , Kopete::OnlineStatusManager::Offline ), + gaduStatusOfflineDescr_( Kopete::OnlineStatus::Offline, GG_STATUS_NOT_AVAIL_DESCR, this, GG_STATUS_NOT_AVAIL_DESCR, + QStringList::split( '|', "contact_away_overlay|gg_description_overlay" ), i18n( "Offline" ), i18n( "A&way" ) , Kopete::OnlineStatusManager::Offline ), + gaduStatusBusy_(Kopete::OnlineStatus::Away, GG_STATUS_BUSY, this, GG_STATUS_BUSY, + "contact_away_overlay", i18n( "Busy" ) , i18n( "B&usy" ) , Kopete::OnlineStatusManager::Busy ), + gaduStatusBusyDescr_(Kopete::OnlineStatus::Away, GG_STATUS_BUSY_DESCR, this, GG_STATUS_BUSY_DESCR, + QStringList::split( '|', "contact_away_overlay|gg_description_overlay" ), i18n( "Busy" ) , i18n( "B&usy" ) , Kopete::OnlineStatusManager::Idle ), + gaduStatusInvisible_( Kopete::OnlineStatus::Invisible, GG_STATUS_INVISIBLE, this, GG_STATUS_INVISIBLE, + "contact_invisible_overlay", i18n( "Invisible" ) , i18n( "I&nvisible" ) , Kopete::OnlineStatusManager::Invisible), + gaduStatusInvisibleDescr_(Kopete::OnlineStatus::Invisible, GG_STATUS_INVISIBLE_DESCR, this, GG_STATUS_INVISIBLE_DESCR, + QStringList::split( '|', "contact_invisible_overlay|gg_description_overlay" ), i18n( "Invisible" ) , i18n( "I&nvisible" )), + gaduStatusAvail_(Kopete::OnlineStatus::Online, GG_STATUS_AVAIL, this, GG_STATUS_AVAIL, + QString::null, i18n( "Online" ) , i18n( "&Online" ) , Kopete::OnlineStatusManager::Online ), + gaduStatusAvailDescr_(Kopete::OnlineStatus::Online, GG_STATUS_AVAIL_DESCR, this, GG_STATUS_AVAIL_DESCR, + "gg_description_overlay", i18n( "Online" ) , i18n( "&Online" )), + gaduConnecting_(Kopete::OnlineStatus::Offline, GG_STATUS_CONNECTING, this, GG_STATUS_CONNECTING, + "gg_con", i18n( "Connecting" ) ) +{ + if ( protocolStatic_ ) { + kdDebug(14100)<<"####"<<"GaduProtocol already initialized"<<endl; + } + else { + protocolStatic_ = this; + } + + addAddressBookField( "messaging/gadu", Kopete::Plugin::MakeIndexField ); + + setCapabilities( Kopete::Protocol::RichFormatting | Kopete::Protocol::RichFgColor ); + +} + +GaduProtocol::~GaduProtocol() +{ + protocolStatic_ = 0L; +} + +GaduProtocol* +GaduProtocol::protocol() +{ + return protocolStatic_; +} + +AddContactPage* +GaduProtocol::createAddContactWidget( QWidget* parent, Kopete::Account* account ) +{ + return new GaduAddContactPage( static_cast<GaduAccount*>( account ), parent ); +} + +void +GaduProtocol::settingsChanged() +{ +} + +Kopete::Contact * +GaduProtocol::deserializeContact( Kopete::MetaContact* metaContact, + const QMap<QString, QString>& serializedData, + const QMap<QString, QString>& /* addressBookData */ ) +{ + + const QString aid = serializedData[ "accountId" ]; + const QString cid = serializedData[ "contactId" ]; + const QString dn = serializedData[ "displayName" ]; + + QDict<Kopete::Account> daccounts = Kopete::AccountManager::self()->accounts( this ); + + Kopete::Account* account = daccounts[ aid ]; + if (!account) { + account = createNewAccount(aid); + } + + GaduAccount* gaccount = static_cast<GaduAccount *>( account ); + + GaduContact* contact = new GaduContact( cid.toUInt(), dn, account, metaContact ); + + contact->setParentIdentity( aid ); + gaccount->addNotify( cid.toUInt() ); + + contact->setProperty( propEmail, serializedData["email"] ); + contact->setProperty( propFirstName, serializedData["FirstName"] ); + contact->setProperty( propLastName, serializedData["SecondName"] ); + contact->setProperty( propPhoneNr, serializedData["telephone"] ); + contact->setIgnored(serializedData["ignored"] == "true"); + return contact; +} + +uint +GaduProtocol::statusToWithDescription( Kopete::OnlineStatus status ) +{ + + if ( status == gaduStatusOffline_ || status == gaduStatusOfflineDescr_ ) { + return GG_STATUS_NOT_AVAIL_DESCR; + } + + if ( status == gaduStatusBusyDescr_ || status == gaduStatusBusy_ ){ + return GG_STATUS_BUSY_DESCR; + } + + if ( status == gaduStatusInvisibleDescr_ || status == gaduStatusInvisible_ ){ + return GG_STATUS_INVISIBLE_DESCR; + } + + return GG_STATUS_AVAIL_DESCR; +} + +uint +GaduProtocol::statusToWithoutDescription( Kopete::OnlineStatus status ) +{ + if ( status == gaduStatusOffline_ || status == gaduStatusOfflineDescr_ ) { + return GG_STATUS_NOT_AVAIL; + } + + if ( status == gaduStatusBusyDescr_ || status == gaduStatusBusy_ ){ + return GG_STATUS_BUSY; + } + + if ( status == gaduStatusInvisibleDescr_ || status == gaduStatusInvisible_ ){ + return GG_STATUS_INVISIBLE; + } + + return GG_STATUS_AVAIL; +} + +bool +GaduProtocol::statusWithDescription( uint status ) +{ + switch( status ) { + case GG_STATUS_NOT_AVAIL: + case GG_STATUS_BUSY: + case GG_STATUS_INVISIBLE: + case GG_STATUS_AVAIL: + case GG_STATUS_CONNECTING: + case GG_STATUS_BLOCKED: + return false; + case GG_STATUS_INVISIBLE_DESCR: + case GG_STATUS_NOT_AVAIL_DESCR: + case GG_STATUS_BUSY_DESCR: + case GG_STATUS_AVAIL_DESCR: + return true; + } + return false; +} + +Kopete::OnlineStatus +GaduProtocol::convertStatus( uint status ) const +{ + switch( status ) { + case GG_STATUS_NOT_AVAIL: + return gaduStatusOffline_; + case GG_STATUS_NOT_AVAIL_DESCR: + return gaduStatusOfflineDescr_; + case GG_STATUS_BUSY: + return gaduStatusBusy_; + case GG_STATUS_BUSY_DESCR: + return gaduStatusBusyDescr_; + case GG_STATUS_INVISIBLE: + return gaduStatusInvisible_; + case GG_STATUS_INVISIBLE_DESCR: + return gaduStatusInvisibleDescr_; + case GG_STATUS_AVAIL: + return gaduStatusAvail_; + case GG_STATUS_AVAIL_DESCR: + return gaduStatusAvailDescr_; + case GG_STATUS_CONNECTING: + return gaduConnecting_; + case GG_STATUS_BLOCKED: + return gaduStatusBlocked_; + default: + return gaduStatusOffline_; + } +} + +Kopete::Account* +GaduProtocol::createNewAccount( const QString& accountId ) +{ + defaultAccount_ = new GaduAccount( this, accountId ); + return defaultAccount_ ; +} + +KopeteEditAccountWidget* +GaduProtocol::createEditAccountWidget( Kopete::Account* account, QWidget* parent ) +{ + return( new GaduEditAccount( this, account, parent ) ); +} + +#include "gaduprotocol.moc" diff --git a/kopete/protocols/gadu/gaduprotocol.h b/kopete/protocols/gadu/gaduprotocol.h new file mode 100644 index 00000000..079763c4 --- /dev/null +++ b/kopete/protocols/gadu/gaduprotocol.h @@ -0,0 +1,108 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org> +// +// gaduprotocol.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUPROTOCOL_H +#define GADUPROTOCOL_H + +#include <qmap.h> + +#include "kopeteprotocol.h" +#include "kopeteonlinestatus.h" +#include "kopetecontactproperty.h" + +#include "gaducommands.h" + +class KAction; +class KActionMenu; + +class QWidget; +class QString; + +namespace Kopete { class Contact; } +namespace Kopete { class MetaContact; } + +class GaduSession; +class GaduContact; +class GaduAccount; +class GaduPreferences; + +#define GG_STATUS_CONNECTING 0x0100 + +class GaduProtocol : public Kopete::Protocol +{ + Q_OBJECT + +public: + GaduProtocol( QObject* parent, const char* name, const QStringList& str); + ~GaduProtocol(); + + static GaduProtocol *protocol(); + + // Plugin reimplementation + // { + AddContactPage* createAddContactWidget( QWidget* parent, Kopete::Account* account ); + Kopete::Account* createNewAccount( const QString& accountId ); + KopeteEditAccountWidget *createEditAccountWidget( Kopete::Account* account, QWidget* parent ); + bool canSendOffline() const { return true; } + + virtual Kopete::Contact *deserializeContact( Kopete::MetaContact* metaContact, + const QMap<QString, QString>& serializedData, + const QMap<QString, QString>& addressBookData ); + // } + //!Plugin reimplementation + + Kopete::OnlineStatus convertStatus( uint ) const; + bool statusWithDescription( uint status ); + + uint statusToWithDescription( Kopete::OnlineStatus status ); + uint statusToWithoutDescription( Kopete::OnlineStatus status ); + + const Kopete::ContactPropertyTmpl propFirstName; + const Kopete::ContactPropertyTmpl propLastName; + const Kopete::ContactPropertyTmpl propEmail; + const Kopete::ContactPropertyTmpl propAwayMessage; + const Kopete::ContactPropertyTmpl propPhoneNr; + //const Kopete::ContactPropertyTmpl propIgnore; + +private slots: + void settingsChanged(); + +private: + static GaduProtocol* protocolStatic_; + GaduAccount* defaultAccount_; + //GaduPreferences* prefs_; + + const Kopete::OnlineStatus gaduStatusBlocked_; + const Kopete::OnlineStatus gaduStatusOffline_; + const Kopete::OnlineStatus gaduStatusOfflineDescr_; + const Kopete::OnlineStatus gaduStatusBusy_; + const Kopete::OnlineStatus gaduStatusBusyDescr_; + const Kopete::OnlineStatus gaduStatusInvisible_; + const Kopete::OnlineStatus gaduStatusInvisibleDescr_; + const Kopete::OnlineStatus gaduStatusAvail_; + const Kopete::OnlineStatus gaduStatusAvailDescr_; + const Kopete::OnlineStatus gaduConnecting_; + +}; + + +#endif diff --git a/kopete/protocols/gadu/gadupubdir.cpp b/kopete/protocols/gadu/gadupubdir.cpp new file mode 100644 index 00000000..8b722894 --- /dev/null +++ b/kopete/protocols/gadu/gadupubdir.cpp @@ -0,0 +1,344 @@ +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadupubdir.cpp +// Gadu-Gadu Public directory contains people data, using it you can search friends +// different criteria +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include "gadupubdir.h" +#include "gadueditcontact.h" +#include "gaducontactlist.h" +#include "gaduaccount.h" +#include "gaduprotocol.h" + +#include <qwidgetstack.h> +#include <qlistview.h> +#include <qptrlist.h> +#include <qradiobutton.h> +#include <qspinbox.h> +#include <qcheckbox.h> + +#include <kcombobox.h> +#include <krestrictedline.h> +#include <klineedit.h> +#include <klistview.h> +#include <klocale.h> + +GaduPublicDir::GaduPublicDir( GaduAccount* account, QWidget* parent, const char* name ) +: KDialogBase( parent, name, false, QString::null, User1|User2|User3|Cancel, User2 ) +{ + mAccount = account; + createWidget(); + initConnections(); + + show(); +} + +GaduPublicDir::GaduPublicDir( GaduAccount* account, int searchFor, QWidget* parent, const char* name ) +: KDialogBase( parent, name, false, QString::null, User1|User2|User3|Cancel, User2 ) +{ + ResLine rs; + + mAccount = account; + createWidget(); + initConnections(); + + kdDebug( 14100 ) << "search for Uin: " << searchFor << endl; + + mMainWidget->listFound->clear(); + show(); + + if ( searchFor == 0 ) { + return; + } + + mMainWidget->pubsearch->raiseWidget( 1 ); + mMainWidget->radioByUin->setChecked( true ); + + setButtonText( User2, i18n( "Search &More..." ) ); + showButton( User3, true ); + showButton( User1, true ); + enableButton( User3, false ); + enableButton( User2, false ); + + // now it is time to switch to Right Page(tm) + rs.uin = searchFor; + + fName = fSurname = fNick = fCity = QString::null; + fUin = searchFor; + fGender = fAgeFrom = fAgeTo = 0; + fOnlyOnline = false; + + mAccount->pubDirSearch( rs, fAgeFrom, fAgeTo, fOnlyOnline ); + +} + +void +GaduPublicDir::createWidget() +{ + setCaption( i18n( "Gadu-Gadu Public Directory" ) ); + + mMainWidget = new GaduPublicDirectory( this ); + setMainWidget( mMainWidget ); + + mMainWidget->UIN->setValidChars( "1234567890" ); + + setButtonText( User1, i18n( "&New Search" ) ); + setButtonText( User2, i18n( "S&earch" ) ); + setButtonText( User3, i18n( "&Add User..." ) ); + setButtonText( Cancel, i18n( "&Close" ) ); + + showButton( User1, false ); + showButton( User3, false ); + enableButton( User2, false ); + + mMainWidget->radioByData->setChecked( true ); + + mAccount->pubDirSearchClose(); + +} + +void +GaduPublicDir::slotAddContact() +{ + GaduContactsList::ContactLine* cl = new GaduContactsList::ContactLine; + QListViewItem* item = mMainWidget->listFound->currentItem(); + + cl->ignored = false; + cl->firstname = item->text( 1 ); + cl->uin = item->text( 5 ); + cl->nickname = item->text( 2 ); + + cl->surname = fSurname; + +// GaduEditContact *ed = + new GaduEditContact( mAccount, cl, this ); +} + +void +GaduPublicDir::slotListSelected( ) +{ + QListViewItem* item = mMainWidget->listFound->currentItem(); + if ( item ) { + enableButton( User3, true ); + } + else { + enableButton( User3, false ); + } +} + +void +GaduPublicDir::initConnections() +{ + connect( this, SIGNAL( user2Clicked() ), SLOT( slotSearch() ) ); + connect( this, SIGNAL( user1Clicked() ), SLOT( slotNewSearch() ) ); + connect( this, SIGNAL( user3Clicked() ), SLOT( slotAddContact() ) ); + + connect( mAccount, SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ), + SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) ); + + connect( mMainWidget->nameS, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->surname, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->nick, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->UIN, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->cityS, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->gender, SIGNAL( activated( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->ageFrom, SIGNAL( valueChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->ageTo, SIGNAL( valueChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( mMainWidget->radioByData, SIGNAL( toggled( bool ) ), SLOT( inputChanged( bool ) ) ); + + connect( mMainWidget->listFound, SIGNAL( selectionChanged () ), SLOT( slotListSelected() ) ); + +} + +void +GaduPublicDir::inputChanged( bool ) +{ + inputChanged( QString::null ); +} + +void +GaduPublicDir::inputChanged( const QString& ) +{ + if ( validateData() == false ) { + enableButton( User2, false ); + } + else { + enableButton( User2, true ); + } +} + +void +GaduPublicDir::getData() +{ + fName = mMainWidget->nameS->text(); + fSurname = mMainWidget->surname->text(); + fNick = mMainWidget->nick->text(); + fUin = mMainWidget->UIN->text().toInt(); + fGender = mMainWidget->gender->currentItem(); + fOnlyOnline = mMainWidget->onlyOnline->isChecked(); + fAgeFrom = mMainWidget->ageFrom->value(); + fAgeTo = mMainWidget->ageTo->value(); + fCity = mMainWidget->cityS->text(); +} + +// return true if not empty +#define CHECK_STRING(A) { if ( !A.isEmpty() ) { return true; } } +#define CHECK_INT(A) { if ( A ) { return true; } } + +bool +GaduPublicDir::validateData() +{ + getData(); + + if ( mMainWidget->radioByData->isChecked() ) { + CHECK_STRING( fCity ); + CHECK_STRING( fName ); + CHECK_STRING( fSurname ); + CHECK_STRING( fNick ); + CHECK_INT( fGender ); + CHECK_INT( fAgeFrom ); + CHECK_INT( fAgeTo ); + } + else { + fSurname = QString::null; + CHECK_INT( fUin ); + } + return false; +} + +// Move to GaduProtocol someday +QPixmap +GaduPublicDir::iconForStatus( uint status ) +{ + QPixmap n; + + if ( GaduProtocol::protocol() ) { + return GaduProtocol::protocol()->convertStatus( status ).protocolIcon(); + } + return n; +} + +void +GaduPublicDir::slotSearchResult( const SearchResult& result, unsigned int ) +{ + QListView* list = mMainWidget->listFound; + + kdDebug(14100) << "searchResults(" << result.count() <<")" << endl; + + QListViewItem* sl; + + SearchResult::const_iterator r; + + for ( r = result.begin(); r != result.end() ; ++r ){ + kdDebug(14100) << "adding" << (*r).uin << endl; + sl= new QListViewItem( + list, QString::fromAscii(""), + (*r).firstname, + (*r).nickname, + (*r).age, + (*r).city, + QString::number( (*r).uin ).ascii() + ); + sl->setPixmap( 0, iconForStatus( (*r).status ) ); + } + + // if not found anything, obviously we don't want to search for more + // if we are looking just for one UIN, don't allow search more - it is pointless + + if ( result.count() && fUin==0 ) { + enableButton( User2, true ); + } + + enableButton( User1, true ); + enableButton( User3, false ); + mMainWidget->pubsearch->setDisabled( false ); + +} + +void +GaduPublicDir::slotNewSearch() +{ + mMainWidget->pubsearch->raiseWidget( 0 ); + + setButtonText( User2, i18n( "S&earch" ) ); + + showButton( User1, false ); + showButton( User3, false ); + enableButton( User2, false ); + inputChanged( QString::null ); + mAccount->pubDirSearchClose(); +} + +void +GaduPublicDir::slotSearch() +{ + + mMainWidget->listFound->clear(); + QString empty; + + // search more, or search ? + if ( mMainWidget->pubsearch->id( mMainWidget->pubsearch->visibleWidget() ) == 0 ) { + kdDebug(14100) << "start search... " << endl; + getData(); + + // validate data + if ( validateData() == false ) { + return; + } + + // go on + mMainWidget->pubsearch->raiseWidget( 1 ); + + } + else{ + kdDebug(14100) << "search more... " << endl; + // Search for more + } + mMainWidget->pubsearch->setDisabled( true ); + setButtonText( User2, i18n( "Search &More..." ) ); + showButton( User3, true ); + showButton( User1, true ); + enableButton( User3, false ); + enableButton( User2, false ); + + ResLine rs; + rs.firstname = fName; + rs.surname = fSurname; + rs.nickname = fNick; + rs.uin = fUin; + rs.city = fCity; + + if ( fGender == 1 ) { + rs.gender = GG_PUBDIR50_GENDER_MALE; + } + if ( fGender == 2 ) { + rs.gender = GG_PUBDIR50_GENDER_FEMALE; + } + + + if ( mMainWidget->radioByData->isChecked() ) { + mAccount->pubDirSearch( rs, fAgeFrom, fAgeTo, fOnlyOnline ); + } + else { + mAccount->pubDirSearch( rs, 0, 0, fOnlyOnline ); + } +} + +#include "gadupubdir.moc" diff --git a/kopete/protocols/gadu/gadupubdir.h b/kopete/protocols/gadu/gadupubdir.h new file mode 100644 index 00000000..11c03981 --- /dev/null +++ b/kopete/protocols/gadu/gadupubdir.h @@ -0,0 +1,80 @@ +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadupubdir.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#ifndef GADUPUBDIR_H +#define GADUPUBDIR_H + +#include "gadusearch.h" +#include "gadusession.h" + +#include <kdebug.h> +#include <kdialogbase.h> + +class GaduAccount; +class GaduProtocol; +class GaduContact; +class GaduAccount; +class GaduPublicDirectory; +class QListViewItem; +class GaduContact; + +class GaduPublicDir : public KDialogBase +{ +Q_OBJECT + +public: + GaduPublicDir( GaduAccount* , QWidget *parent = 0, const char* name = "GaduPublicDir" ); + GaduPublicDir( GaduAccount* , int searchFor, QWidget* parent = 0, const char* name = "GaduPublicDir" ); + QPixmap iconForStatus( uint status ); + +private slots: + void slotSearch(); + void slotNewSearch(); + void slotSearchResult( const SearchResult& result, unsigned int seq ); + void slotAddContact(); + void inputChanged( const QString& ); + void inputChanged( bool ); + void slotListSelected(); + + +private: + void getData(); + bool validateData(); + void createWidget(); + void initConnections(); + + GaduProtocol* p; + GaduAccount* mAccount; + GaduContact* mContact; + GaduPublicDirectory* mMainWidget; + +// form data + QString fName; + QString fSurname; + QString fNick; + QString fCity; + int fUin; + int fGender; + bool fOnlyOnline; + int fAgeFrom; + int fAgeTo; +}; +#endif diff --git a/kopete/protocols/gadu/gaduregisteraccount.cpp b/kopete/protocols/gadu/gaduregisteraccount.cpp new file mode 100644 index 00000000..e48ee551 --- /dev/null +++ b/kopete/protocols/gadu/gaduregisteraccount.cpp @@ -0,0 +1,212 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gaduregisteraccount.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <qstring.h> +#include <qregexp.h> +#include <qpushbutton.h> +#include <qlabel.h> + +#include <klineedit.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kglobal.h> + +#include "gaduregisteraccountui.h" +#include "gaduregisteraccount.h" +#include "gaducommands.h" + +GaduRegisterAccount::GaduRegisterAccount( QWidget* parent, const char* name ) +: KDialogBase( parent, name, true, i18n( "Register New Account" ), KDialogBase::User1 | KDialogBase::Ok, KDialogBase::User1, true ) +{ + ui = new GaduRegisterAccountUI( this ); + setMainWidget( ui ); + + ui->valueVerificationSequence->setDisabled( true ); + setButtonText( User1, i18n( "&Register" ) ); + setButtonText( Ok, i18n( "&Cancel" ) ); + enableButton( User1, false ); + + cRegister = new RegisterCommand( this ); + + emailRegexp = new QRegExp( "[\\w\\d.+_-]{1,}@[\\w\\d.-]{1,}" ); + hintPixmap = KGlobal::iconLoader()->loadIcon ( "gadu_protocol", KIcon::Small ); + + connect( this, SIGNAL( user1Clicked() ), SLOT( doRegister() ) ); + connect( this, SIGNAL( okClicked() ), SLOT( slotClose() ) ); + + connect( ui->valueEmailAddress, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) ); + connect( ui->valuePassword, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) ); + connect( ui->valuePasswordVerify, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) ); + connect( ui->valueVerificationSequence, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) ); + + connect( cRegister, SIGNAL( tokenRecieved( QPixmap, QString ) ), SLOT( displayToken( QPixmap, QString ) ) ); + connect( cRegister, SIGNAL( done( const QString&, const QString& ) ), SLOT( registrationDone( const QString&, const QString& ) ) ); + connect( cRegister, SIGNAL( error( const QString&, const QString& ) ), SLOT( registrationError( const QString&, const QString& ) ) ); + connect( cRegister, SIGNAL( operationStatus( const QString ) ), SLOT( updateStatus( const QString ) ) ); + + updateStatus( i18n( "Retrieving token" ) ); + cRegister->requestToken(); + + show(); +} + +void +GaduRegisterAccount::doRegister( ) +{ + cRegister->setUserinfo( ui->valueEmailAddress->text(), ui->valuePassword->text(), ui->valueVerificationSequence->text() ); + cRegister->execute(); + enableButton( User1, false ); +} + +void +GaduRegisterAccount::validateInput() +{ + int valid = true; + int passwordHighlight = false; + + if ( !emailRegexp->exactMatch( ui->valueEmailAddress->text() ) ) + { + updateStatus( i18n( "Please enter a valid E-Mail Address." ) ); + ui->pixmapEmailAddress->setPixmap ( hintPixmap ); + valid = false; + } + else { + ui->pixmapEmailAddress->setText ( "" ); + } + + if ( valid && ( ( ui->valuePassword->text().isEmpty() ) || ( ui->valuePasswordVerify->text().isEmpty() ) ) ) + { + updateStatus( i18n( "Please enter the same password twice." ) ); + valid = false; + passwordHighlight = true; + } + + if ( valid && ( ui->valuePassword->text() != ui->valuePasswordVerify->text() ) ) + { + updateStatus( i18n( "Password entries do not match." ) ); + valid = false; + passwordHighlight = true; + } + + if ( valid && ( ui->valueVerificationSequence->text().isEmpty() ) ) + { + updateStatus( i18n( "Please enter the verification sequence." ) ); + ui->pixmapVerificationSequence->setPixmap ( hintPixmap ); + valid = false; + } + else { + ui->pixmapVerificationSequence->setText ( "" ); + } + + if ( passwordHighlight == true ) + { + ui->pixmapPassword->setPixmap ( hintPixmap ); + ui->pixmapPasswordVerify->setPixmap ( hintPixmap ); + } + else { + ui->pixmapPassword->setText ( "" ); + ui->pixmapPasswordVerify->setText ( "" ); + } + + if ( valid ) + { + // clear status message if we have valid data + updateStatus( i18n( "" ) ); + } + + enableButton( User1, valid ); +} + +void +GaduRegisterAccount::inputChanged( const QString & ) +{ + validateInput(); +} + +void +GaduRegisterAccount::registrationDone( const QString& /*title*/, const QString& /*what */ ) +{ + ui->valueEmailAddress->setDisabled( true ); + ui->valuePassword->setDisabled( true ); + ui->valuePasswordVerify->setDisabled( true ); + ui->valueVerificationSequence->setDisabled( true ); + ui->labelEmailAddress->setDisabled( true ); + ui->labelPassword->setDisabled( true ); + ui->labelPasswordVerify->setDisabled( true ); + ui->labelVerificationSequence->setDisabled( true ); + ui->labelInstructions->setDisabled( true ); + emit registeredNumber( cRegister->newUin(), ui->valuePassword->text() ); + updateStatus( i18n( "Account created; your new UIN is %1." ).arg(QString::number( cRegister->newUin() ) ) ); + enableButton( User1, false ); + setButtonText( Ok, i18n( "&Close" ) ); +} + +void +GaduRegisterAccount::registrationError( const QString& title, const QString& what ) +{ + updateStatus( i18n( "Registration failed: %1" ).arg( what ) ); + KMessageBox::sorry( this, "Registration was unsucessful, please try again.", title ); + + disconnect( this, SLOT( displayToken( QPixmap, QString ) ) ); + disconnect( this, SLOT( registrationDone( const QString&, const QString& ) ) ); + disconnect( this, SLOT( registrationError( const QString&, const QString& ) ) ); + disconnect( this, SLOT( updateStatus( const QString ) ) ); + + ui->valueVerificationSequence->setDisabled( true ); + ui->valueVerificationSequence->setText( "" ); + enableButton( User1, false ); + updateStatus( "" ); + + // emit UIN 0, to enable 'register new account' button again in dialog below + emit registeredNumber( 0, QString( "" ) ); + + deleteLater(); +} + +void +GaduRegisterAccount::displayToken( QPixmap image, QString /*tokenId */ ) +{ + ui->valueVerificationSequence->setDisabled( false ); + ui->pixmapToken->setPixmap( image ); + validateInput(); +} + +void +GaduRegisterAccount::updateStatus( const QString status ) +{ + ui->labelStatusMessage->setAlignment( AlignCenter ); + ui->labelStatusMessage->setText( status ); +} + +void +GaduRegisterAccount::slotClose() +{ + deleteLater(); +} + +GaduRegisterAccount::~GaduRegisterAccount( ) +{ + kdDebug( 14100 ) << " register Cancel " << endl; +} + +#include "gaduregisteraccount.moc" diff --git a/kopete/protocols/gadu/gaduregisteraccount.h b/kopete/protocols/gadu/gaduregisteraccount.h new file mode 100644 index 00000000..339e4c36 --- /dev/null +++ b/kopete/protocols/gadu/gaduregisteraccount.h @@ -0,0 +1,62 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gaduregisteraccount.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUREGISTERACCOUNT_H +#define GADUREGISTERACCOUNT_H + +#include <kdialogbase.h> + +class QString; +class QPixmap; +class RegisterCommand; +class QRegExp; +class GaduRegisterAccountUI; + +class GaduRegisterAccount : public KDialogBase +{ + Q_OBJECT + +public: + GaduRegisterAccount( QWidget* , const char* ); + ~GaduRegisterAccount( ); + +signals: + void registeredNumber( unsigned int, QString ); + +protected slots: + void slotClose(); + void displayToken( QPixmap, QString ); + void registrationError( const QString&, const QString& ); + void registrationDone( const QString&, const QString& ); + void inputChanged( const QString & ); + void doRegister(); + void updateStatus( const QString status ); + +private: + void validateInput(); + + GaduRegisterAccountUI* ui; + RegisterCommand* cRegister; + QRegExp* emailRegexp; + QPixmap hintPixmap; +}; + +#endif diff --git a/kopete/protocols/gadu/gadurichtextformat.cpp b/kopete/protocols/gadu/gadurichtextformat.cpp new file mode 100644 index 00000000..a93b95fd --- /dev/null +++ b/kopete/protocols/gadu/gadurichtextformat.cpp @@ -0,0 +1,291 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadurichtextformat.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#include <knotifyclient.h> +#include <kdebug.h> +#include <kopetemessage.h> + +#include "gadurichtextformat.h" +#include "gadusession.h" + +#include <qstring.h> +#include <qregexp.h> + +GaduRichTextFormat::GaduRichTextFormat() +{ +} + +GaduRichTextFormat::~GaduRichTextFormat() +{ +} + +QString +GaduRichTextFormat::convertToHtml( const QString& msg, unsigned int formats, void* formatStructure) +{ + QString tmp, nb; + gg_msg_richtext_format *format; + char *pointer = (char*) formatStructure; + + unsigned int i,j; + int r, g, b; + r = g = b = 0; + bool opened = false; + + if ( formatStructure == NULL || formats == 0 ) { + tmp = msg; + escapeBody( tmp ); + return tmp; + } + + for ( i = 0, j = 0 ; i < formats ; ) { + format = (gg_msg_richtext_format*) pointer; + unsigned int position = format->position; + char font = format->font; + QString style; + + if ( position < j || position > msg.length() ) { + break; + } + + if ( font & GG_FONT_IMAGE ) { + i += sizeof( gg_msg_richtext_image ); + pointer += sizeof( gg_msg_richtext_image ); + tmp += "<b>[this should be a picture, not yet implemented]</b>"; + } + else { + nb = msg.mid( j, position - j ); + tmp += escapeBody( nb ); + + j = position; + + // add message bit between formating + if ( opened ) { + tmp += formatClosingTag("span"); + opened = false; + } + // set font attributes + if ( font & GG_FONT_BOLD ) { + style += (" font-weight:bold; "); + } + if ( font & GG_FONT_ITALIC ) { + style += (" font-style:italic; "); + } + if ( font & GG_FONT_UNDERLINE ) { + style += (" text-decoration:underline; "); + } + // add color + if ( font & GG_FONT_COLOR ) { + pointer += sizeof( gg_msg_richtext_format ); + i += sizeof( gg_msg_richtext_format ); + gg_msg_richtext_color *color = (gg_msg_richtext_color*)( pointer ); + r = (int)color->red; + g = (int)color->green; + b = (int)color->blue; + } + style += QString(" color: rgb( %1, %2, %3 ); ").arg( r ).arg( g ).arg( b ); + + tmp += formatOpeningTag( QString::fromLatin1("span"), QString::fromLatin1("style=\"%1\"").arg( style ) ); + opened = true; + + } + + // advance to next structure in row + pointer += sizeof( gg_msg_richtext_format ); + i += sizeof( gg_msg_richtext_format ); + } + + nb = msg.mid( j, msg.length() ); + tmp += escapeBody( nb ); + if ( opened ) { + tmp += formatClosingTag("span"); + } + + return tmp; +} + +QString +GaduRichTextFormat::formatOpeningTag( const QString& tag, const QString& attributes ) +{ + QString res = "<" + tag; + if(!attributes.isEmpty()) + res.append(" " + attributes); + return res + ">"; +} + +QString +GaduRichTextFormat::formatClosingTag( const QString& tag ) +{ + return "</" + tag + ">"; +} + +// the initial idea stolen from IRC plugin +KGaduMessage* +GaduRichTextFormat::convertToGaduMessage( const Kopete::Message& message ) +{ + QString htmlString = message.escapedBody(); + KGaduMessage* output = new KGaduMessage; + rtcs.blue = rtcs.green = rtcs.red = 0; + color = QColor(); + int position = 0; + + rtf.resize( sizeof( gg_msg_richtext) ); + output->rtf.resize(0); + + // test first if there is any HTML formating in it + if( htmlString.find( QString::fromLatin1("</span") ) > -1 ) { + QRegExp findTags( QString::fromLatin1("<span style=\"(.*)\">(.*)</span>") ); + findTags.setMinimal( true ); + int pos = 0; + int lastpos = 0; + + while ( pos >= 0 ){ + pos = findTags.search( htmlString ); + rtfs.font = 0; + if ( pos != lastpos ) { + QString tmp; + if ( pos < 0 ) { + tmp = htmlString.mid( lastpos ); + } + else { + tmp = htmlString.mid( lastpos, pos - lastpos ); + } + if ( !tmp.isEmpty() ) { + color.setRgb( 0, 0, 0 ); + if ( insertRtf( position ) == false ) { + delete output; + return NULL; + } + tmp = unescapeGaduMessage( tmp ); + output->message += tmp; + position += tmp.length(); + } + } + + if ( pos > -1 ) { + QString styleHTML = findTags.cap(1); + QString replacement = findTags.cap(2); + QStringList styleAttrs = QStringList::split( ';', styleHTML ); + rtfs.font = 0; + + lastpos = pos + replacement.length(); + + for( QStringList::Iterator attrPair = styleAttrs.begin(); attrPair != styleAttrs.end(); ++attrPair ) { + QString attribute = (*attrPair).section(':',0,0); + QString value = (*attrPair).section(':',1); + parseAttributes( attribute, value ); + } + + if ( insertRtf( position ) == false ) { + delete output; + return NULL; + } + + QString rep = QString("<span style=\"%1\">%2</span>" ).arg( styleHTML ).arg( replacement ); + htmlString.replace( findTags.pos( 0 ), rep.length(), replacement ); + + replacement = unescapeGaduMessage( replacement ); + output->message += replacement; + position += replacement.length(); + } + + } + output->rtf = rtf; + // this is sick, but that's the way libgadu is designed + // here I am adding network header !, should sit in libgadu IMO + header = (gg_msg_richtext*) output->rtf.data(); + header->length = output->rtf.size() - sizeof( gg_msg_richtext ); + header->flag = 2; + } + else { + output->message = message.escapedBody(); + output->message = unescapeGaduMessage( output->message ); + } + + return output; + +} + +void +GaduRichTextFormat::parseAttributes( const QString attribute, const QString value ) +{ + if( attribute == QString::fromLatin1("color") ) { + color.setNamedColor( value ); + } + if( attribute == QString::fromLatin1("font-weight") && value == QString::fromLatin1("600") ) { + rtfs.font |= GG_FONT_BOLD; + } + if( attribute == QString::fromLatin1("text-decoration") && value == QString::fromLatin1("underline") ) { + rtfs.font |= GG_FONT_UNDERLINE ; + } + if( attribute == QString::fromLatin1("font-style") && value == QString::fromLatin1("italic") ) { + rtfs.font |= GG_FONT_ITALIC; + } +} + +QString +GaduRichTextFormat::unescapeGaduMessage( QString& ns ) +{ + QString s; + s = Kopete::Message::unescape( ns ); + s.replace( QString::fromAscii( "\n" ), QString::fromAscii( "\r\n" ) ); + return s; +} + +bool +GaduRichTextFormat::insertRtf( uint position) +{ + if ( color != QColor( rtcs.red, rtcs.green, rtcs.blue ) ) { + rtcs.red = color.red(); + rtcs.green = color.green(); + rtcs.blue = color.blue(); + rtfs.font |= GG_FONT_COLOR; + } + + if ( rtfs.font ) { + // append font description + rtfs.position = position; + uint csize = rtf.size(); + if ( rtf.resize( csize + sizeof( gg_msg_richtext_format ) ) == FALSE ) { + return false; + }; + memcpy( rtf.data() + csize, &rtfs, sizeof( rtfs ) ); + // append color description, if color has changed + if ( rtfs.font & GG_FONT_COLOR ) { + csize = rtf.size(); + if ( rtf.resize( csize + sizeof( gg_msg_richtext_color ) ) == FALSE ) { + return false; + }; + memcpy( rtf.data() + csize, &rtcs, sizeof( rtcs ) ); + } + } + return true; +} + +QString +GaduRichTextFormat::escapeBody( QString& input ) +{ + input.replace( '<', QString::fromLatin1("<") ); + input.replace( '>', QString::fromLatin1(">") ); + input.replace( '\n', QString::fromLatin1( "<br />" ) ); + input.replace( '\t', QString::fromLatin1( " " ) ); + input.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( " " ) ); + return input; +} diff --git a/kopete/protocols/gadu/gadurichtextformat.h b/kopete/protocols/gadu/gadurichtextformat.h new file mode 100644 index 00000000..34dfcd37 --- /dev/null +++ b/kopete/protocols/gadu/gadurichtextformat.h @@ -0,0 +1,53 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// +// gadurichtextformat.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + + +#ifndef GADURTF_H +#define GADURTF_H + +#include <libgadu.h> + +class Qstring; +namespace Kopete { class Message; } +class KGaduMessage; + +class GaduRichTextFormat { +public: + GaduRichTextFormat(); + ~GaduRichTextFormat(); + QString convertToHtml( const QString&, unsigned int, void* ); + KGaduMessage* convertToGaduMessage( const Kopete::Message& ); + +private: + QString formatOpeningTag( const QString& , const QString& = QString::null ); + QString formatClosingTag( const QString& ); + bool insertRtf( uint ); + QString unescapeGaduMessage( QString& ); + void parseAttributes( const QString, const QString ); + QString escapeBody( QString& ); + QColor color; + gg_msg_richtext_format rtfs; + gg_msg_richtext_color rtcs; + gg_msg_richtext* header; + QByteArray rtf; + +}; +#endif diff --git a/kopete/protocols/gadu/gadusession.cpp b/kopete/protocols/gadu/gadusession.cpp new file mode 100644 index 00000000..92f9137d --- /dev/null +++ b/kopete/protocols/gadu/gadusession.cpp @@ -0,0 +1,811 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002 Zack Rusin <zack@kde.org> +// +// gadusession.cpp +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include "ctime" + +#include "gadusession.h" + +#include <klocale.h> +#include <kdebug.h> +#include "kopetemessage.h" + +#include <qsocketnotifier.h> +#include <qtextcodec.h> +#include <qdatetime.h> +#include "gadurichtextformat.h" + +#include <errno.h> +#include <string.h> +#include <netinet/in.h> + +GaduSession::GaduSession( QObject* parent, const char* name ) +: QObject( parent, name ), session_( 0 ), searchSeqNr_( 0 ) +{ + textcodec = QTextCodec::codecForName( "CP1250" ); + rtf = new GaduRichTextFormat; +} + +GaduSession::~GaduSession() +{ + logoff(); +} + +bool +GaduSession::isConnected() const +{ + if ( session_ ) { + return ( session_->state & GG_STATE_CONNECTED ); + } + return false; +} + +int +GaduSession::status() const +{ + kdDebug(14100)<<"Status = " << session_->status <<", initial = "<< session_->initial_status <<endl; + if ( session_ ) { + return session_->status & ( ~GG_STATUS_FRIENDS_MASK ); + } + return GG_STATUS_NOT_AVAIL; +} + +void +GaduSession::login( struct gg_login_params* p ) +{ + if ( !isConnected() ) { + +// turn on in case you have any problems, and you want +// to report it better. libgadu needs to be recompiled with debug enabled +// gg_debug_level=GG_DEBUG_MISC|GG_DEBUG_FUNCTION; + + kdDebug(14100) << "Login" << endl; + + if ( !( session_ = gg_login( p ) ) ) { + destroySession(); + kdDebug( 14100 ) << "libgadu internal error " << endl; + emit connectionFailed( GG_FAILURE_CONNECTING ); + return; + } + + createNotifiers( true ); + enableNotifiers( session_->check ); + searchSeqNr_=0; + } +} + +void +GaduSession::destroyNotifiers() +{ + disableNotifiers(); + if ( read_ ) { + delete read_; + read_ = NULL; + } + if ( write_ ) { + delete write_; + write_ = NULL; + } +} + +void +GaduSession::createNotifiers( bool connect ) +{ + if ( !session_ ){ + return; + } + + read_ = new QSocketNotifier( session_->fd, QSocketNotifier::Read, this ); + read_->setEnabled( false ); + + write_ = new QSocketNotifier( session_->fd, QSocketNotifier::Write, this ); + write_->setEnabled( false ); + + if ( connect ) { + QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( checkDescriptor() ) ); + QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( checkDescriptor() ) ); + } +} + +void +GaduSession::enableNotifiers( int checkWhat ) +{ + if( (checkWhat & GG_CHECK_READ) && read_ ) { + read_->setEnabled( true ); + } + if( (checkWhat & GG_CHECK_WRITE) && write_ ) { + write_->setEnabled( true ); + } +} + +void +GaduSession::disableNotifiers() +{ + if ( read_ ) { + read_->setEnabled( false ); + } + if ( write_ ) { + write_->setEnabled( false ); + } +} + +void +GaduSession::dccRequest( const unsigned int uin ) +{ + if ( session_ ) { + gg_dcc_request( session_, uin ); + } +} + +void +GaduSession::login( KGaduLoginParams* loginp ) +{ + QCString desc = textcodec->fromUnicode( loginp->statusDescr ); + + memset( ¶ms_, 0, sizeof(params_) ); + + params_.status_descr = (char*)desc.data(); + + params_.uin = loginp->uin; + params_.password = (char *)( loginp->password.ascii() ); + params_.status = loginp->status | ( loginp->forFriends ? GG_STATUS_FRIENDS_MASK : 0 ); + params_.async = 1; + params_.tls = loginp->useTls; + params_ .server_addr = loginp->server; + params_.client_addr = loginp->client_addr; + params_.client_port = loginp->client_port; + + kdDebug(14100) << "LOGIN IP: " << loginp->client_addr << endl; + + if ( loginp->useTls ) { + params_.server_port = GG_HTTPS_PORT; + } + else { + if ( loginp->server ) { + params_.server_port = GG_DEFAULT_PORT; + } + } + + kdDebug(14100)<<"gadusession::login, server ( " << loginp->server << " ), tls(" << loginp->useTls << ") " <<endl; + login( ¶ms_ ); + +} + +void +GaduSession::destroySession() +{ + if ( session_ ) { + destroyNotifiers(); + gg_free_session( session_ ); + session_ = 0; + } +} + +void +GaduSession::logoff( Kopete::Account::DisconnectReason reason ) +{ + destroySession(); + emit disconnect( reason ); +} + +int +GaduSession::notify( uin_t* userlist, int count ) +{ + if ( isConnected() ) { + return gg_notify( session_, userlist, count ); + } + else { + emit error( i18n("Not Connected"), i18n("You are not connected to the server.") ); + } + + return 1; +} + +int +GaduSession::addNotify( uin_t uin ) +{ + if ( isConnected() ) { + return gg_add_notify( session_, uin ); + } + else { + emit error( i18n("Not Connected"), i18n("You are not connected to the server.") ); + } + return 1; +} + +int +GaduSession::removeNotify( uin_t uin ) +{ + if ( isConnected() ) { + gg_remove_notify( session_, uin ); + } + else { + emit error( i18n("Not Connected"), i18n("You are not connected to the server.") ); + } + + return 1; +} + +int +GaduSession::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass ) +{ + QString sendMsg; + QCString cpMsg; + KGaduMessage* gadumessage; + + if ( isConnected() ) { + gadumessage = rtf->convertToGaduMessage( msg ); + if ( gadumessage ) { + const void* data = (const void*)gadumessage->rtf.data(); + cpMsg = textcodec->fromUnicode( gadumessage->message ); + int o; + o = gg_send_message_richtext( session_, msgClass, recipient, (const unsigned char *)cpMsg.data(), (const unsigned char*) data, gadumessage->rtf.size() ); + gadumessage->rtf.resize(0); + delete gadumessage; + return o; + } + else { + sendMsg = msg.plainBody(); + sendMsg.replace( QString::fromAscii( "\n" ), QString::fromAscii( "\r\n" ) ); + cpMsg = textcodec->fromUnicode( sendMsg ); + + return gg_send_message( session_, msgClass, recipient, (const unsigned char *)cpMsg.data() ); + } + } + else { + emit error( i18n("Not Connected"), i18n("You are not connected to the server.") ); + } + + return 1; +} + +int +GaduSession::changeStatus( int status, bool forFriends ) +{ + kdDebug(14101)<<"## Changing to "<<status<<endl; + if ( isConnected() ) { + return gg_change_status( session_, status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0) ); + } + else { + emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") ); + } + + return 1; +} + +int +GaduSession::changeStatusDescription( int status, const QString& descr, bool forFriends ) +{ + QCString ndescr; + + ndescr= textcodec->fromUnicode(descr); + + if ( isConnected() ) { + return gg_change_status_descr( session_, + status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0), ndescr.data() ); + } + else { + emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") ); + } + + return 1; +} + +int +GaduSession::ping() +{ + if ( isConnected() ) { + return gg_ping( session_ ); + } + + return 1; +} + +void +GaduSession::pubDirSearchClose() +{ + searchSeqNr_=0; +} + +unsigned int +GaduSession::getPersonalInformation() +{ + gg_pubdir50_t searchRequest; + unsigned int seqNr; + + if ( isConnected() == false ) { + return 0; + } + + searchRequest = gg_pubdir50_new( GG_PUBDIR50_READ ); + if ( !searchRequest ) { + return 0; + } + + seqNr = gg_pubdir50( session_, searchRequest ); + gg_pubdir50_free( searchRequest ); + + return seqNr; +} + +bool +GaduSession::publishPersonalInformation( ResLine& d ) +{ + gg_pubdir50_t r; + + if ( !session_ ) { + return 0; + } + + r = gg_pubdir50_new( GG_PUBDIR50_WRITE ); + + if ( d.firstname.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_FIRSTNAME, + (const char *)((const char*)textcodec->fromUnicode( d.firstname ) ) ); + if ( d.surname.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_LASTNAME, + (const char *)((const char*)textcodec->fromUnicode( d.surname ) ) ); + if ( d.nickname.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_NICKNAME, + (const char *)((const char*)textcodec->fromUnicode( d.nickname ) ) ); + if ( d.age.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_BIRTHYEAR, + (const char *)((const char*)textcodec->fromUnicode( d.age ) ) ); + if ( d.city.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_CITY, + (const char *)((const char*)textcodec->fromUnicode( d.city ) ) ); + if ( d.meiden.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_FAMILYNAME, + (const char *)((const char*)textcodec->fromUnicode( d.meiden ) ) ); + if ( d.orgin.length() ) + gg_pubdir50_add( r, GG_PUBDIR50_FAMILYCITY, + (const char *)((const char*)textcodec->fromUnicode( d.orgin ) ) ); + if ( d.gender.length() == 1 ) + gg_pubdir50_add( r, GG_PUBDIR50_GENDER, + (const char *)((const char*)textcodec->fromUnicode( d.gender ) ) ); + + gg_pubdir50( session_, r ); + + gg_pubdir50_free( r ); + + return true; +} + +unsigned int +GaduSession::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive ) +{ + QString bufYear; + unsigned int reqNr; + gg_pubdir50_t searchRequest; + + if ( !session_ ) { + return 0; + } + + searchRequest = gg_pubdir50_new( GG_PUBDIR50_SEARCH_REQUEST ); + if ( !searchRequest ) { + return 0; + } + + if ( query.uin == 0 ) { + if (query.firstname.length()) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_FIRSTNAME, + (const char*)textcodec->fromUnicode( query.firstname ) ); + } + if ( query.surname.length() ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_LASTNAME, + (const char*)textcodec->fromUnicode( query.surname ) ); + } + if ( query.nickname.length() ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_NICKNAME, + (const char*)textcodec->fromUnicode( query.nickname ) ); + } + if ( query.city.length() ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_CITY, + (const char*)textcodec->fromUnicode( query.city ) ); + } + if ( ageFrom || ageTo ) { + QString yearFrom = QString::number( QDate::currentDate().year() - ageFrom ); + QString yearTo = QString::number( QDate::currentDate().year() - ageTo ); + + if ( ageFrom && ageTo ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR, + (const char*)textcodec->fromUnicode( yearFrom + " " + yearTo ) ); + } + if ( ageFrom ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR, + (const char*)textcodec->fromUnicode( yearFrom ) ); + } + else { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR, + (const char*)textcodec->fromUnicode( yearTo ) ); + } + } + + if ( query.gender.length() == 1 ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_GENDER, + (const char *)((const char*)textcodec->fromUnicode( query.gender ) ) ); + } + + if ( onlyAlive ) { + gg_pubdir50_add( searchRequest, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE ); + } + } + // otherwise we are looking only for one fellow with this nice UIN + else{ + gg_pubdir50_add( searchRequest, GG_PUBDIR50_UIN, QString::number( query.uin ).ascii() ); + } + + gg_pubdir50_add( searchRequest, GG_PUBDIR50_START, QString::number( searchSeqNr_ ).ascii() ); + reqNr = gg_pubdir50( session_, searchRequest ); + gg_pubdir50_free( searchRequest ); + + return reqNr; +} + +void +GaduSession::sendResult( gg_pubdir50_t result ) +{ + int i, count, age; + ResLine resultLine; + SearchResult sres; + + count = gg_pubdir50_count( result ); + + if ( !count ) { + kdDebug(14100) << "there was nothing found in public directory for requested details" << endl; + } + + for ( i = 0; i < count; i++ ) { + resultLine.uin = QString( gg_pubdir50_get( result, i, GG_PUBDIR50_UIN ) ).toInt(); + resultLine.firstname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FIRSTNAME ) ); + resultLine.surname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_LASTNAME ) ); + resultLine.nickname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_NICKNAME ) ); + resultLine.age = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_BIRTHYEAR ) ); + resultLine.city = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_CITY ) ); + QString stat = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_STATUS ) ); + resultLine.orgin = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYCITY ) ); + resultLine.meiden = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYNAME ) ); + resultLine.gender = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_GENDER ) ); + + resultLine.status = stat.toInt(); + age = resultLine.age.toInt(); + if ( age ) { + resultLine.age = QString::number( QDate::currentDate().year() - age ); + } + else { + resultLine.age.truncate( 0 ); + } + sres.append( resultLine ); + kdDebug(14100) << "found line "<< resultLine.uin << " " << resultLine.firstname << endl; + } + + searchSeqNr_ = gg_pubdir50_next( result ); + emit pubDirSearchResult( sres, gg_pubdir50_seq( result ) ); +} + +void +GaduSession::requestContacts() +{ + if ( !session_ || session_->state != GG_STATE_CONNECTED ) { + kdDebug(14100) <<" you need to be connected to send " << endl; + return; + } + + if ( gg_userlist_request( session_, GG_USERLIST_GET, NULL ) == -1 ) { + kdDebug(14100) <<" userlist export ERROR " << endl; + return; + } + kdDebug( 14100 ) << "Contacts list import..started " << endl; +} + + +void +GaduSession::exportContactsOnServer( GaduContactsList* contactsList ) +{ + QCString plist; + + if ( !session_ || session_->state != GG_STATE_CONNECTED ) { + kdDebug( 14100 ) << "you need to connect to export Contacts list " << endl; + return; + } + + plist = textcodec->fromUnicode( contactsList->asString() ); + kdDebug(14100) <<"--------------------userlists\n" << plist << endl; + kdDebug(14100) << "----------------------------" << endl; + + if ( gg_userlist_request( session_, GG_USERLIST_PUT, plist.data() ) == -1 ) { + kdDebug( 14100 ) << "export contact list failed " << endl; + return; + } + kdDebug( 14100 ) << "Contacts list export..started " << endl; +} + + +void +GaduSession::handleUserlist( gg_event* event ) +{ + QString ul; + switch( event->event.userlist.type ) { + case GG_USERLIST_GET_REPLY: + if ( event->event.userlist.reply ) { + ul = event->event.userlist.reply; + kdDebug( 14100 ) << "Got Contacts list OK " << endl; + } + else { + kdDebug( 14100 ) << "Got Contacts list FAILED/EMPTY " << endl; + // FIXME: send failed? + } + emit userListRecieved( ul ); + break; + + case GG_USERLIST_PUT_REPLY: + kdDebug( 14100 ) << "Contacts list exported OK " << endl; + emit userListExported(); + break; + + } +} + +QString +GaduSession::stateDescription( int state ) +{ + switch( state ) { + case GG_STATE_IDLE: + return i18n( "idle" ); + case GG_STATE_RESOLVING: + return i18n( "resolving host" ); + case GG_STATE_CONNECTING: + return i18n( "connecting" ); + case GG_STATE_READING_DATA: + return i18n( "reading data" ); + case GG_STATE_ERROR: + return i18n( "error" ); + case GG_STATE_CONNECTING_HUB: + return i18n( "connecting to hub" ); + case GG_STATE_CONNECTING_GG: + return i18n( "connecting to server" ); + case GG_STATE_READING_KEY: + return i18n( "retrieving key" ); + case GG_STATE_READING_REPLY: + return i18n( "waiting for reply" ); + case GG_STATE_CONNECTED: + return i18n( "connected" ); + case GG_STATE_SENDING_QUERY: + return i18n( "sending query" ); + case GG_STATE_READING_HEADER: + return i18n( "reading header" ); + case GG_STATE_PARSING: + return i18n( "parse data" ); + case GG_STATE_DONE: + return i18n( "done" ); + case GG_STATE_TLS_NEGOTIATION: + return i18n( "Tls connection negotiation" ); + default: + return i18n( "unknown" ); + } +} +QString +GaduSession::errorDescription( int err ) +{ + switch( err ){ + case GG_ERROR_RESOLVING: + return i18n( "Resolving error." ); + case GG_ERROR_CONNECTING: + return i18n( "Connecting error." ); + case GG_ERROR_READING: + return i18n( "Reading error." ); + case GG_ERROR_WRITING: + return i18n( "Writing error." ); + default: + return i18n( "Unknown error number %1." ).arg( QString::number( (unsigned int)err ) ); + } +} + +QString +GaduSession::failureDescription( gg_failure_t f ) +{ + switch( f ) { + case GG_FAILURE_RESOLVING: + return i18n( "Unable to resolve server address. DNS failure." ); + case GG_FAILURE_CONNECTING: + return i18n( "Unable to connect to server." ); + case GG_FAILURE_INVALID: + return i18n( "Server send incorrect data. Protocol error." ); + case GG_FAILURE_READING: + return i18n( "Problem reading data from server." ); + case GG_FAILURE_WRITING: + return i18n( "Problem sending data to server." ); + case GG_FAILURE_PASSWORD: + return i18n( "Incorrect password." ); + case GG_FAILURE_404: + return QString::fromAscii( "404." ); + case GG_FAILURE_TLS: + return i18n( "Unable to connect over encrypted channel.\nTry to turn off encryption support in Gadu account settings and reconnect." ); + default: + return i18n( "Unknown error number %1." ).arg( QString::number( (unsigned int)f ) ); + } +} + +void +GaduSession::notify60( gg_event* event ) +{ + KGaduNotify* gn = NULL; + unsigned int n; + + if ( event->event.notify60[0].uin ) { + gn = new KGaduNotify; + } + else { + return; + } + + for( n=0 ; event->event.notify60[n].uin ; n++ ) { + gn->contact_id = event->event.notify60[n].uin; + gn->status = event->event.notify60[n].status; + gn->remote_ip.setAddress( ntohl( event->event.notify60[n].remote_ip ) ); + gn->remote_port = event->event.notify60[n].remote_port; + if ( event->event.notify60[n].remote_ip && gn->remote_port > 10 ) { + gn->fileCap = true; + } + else { + gn->fileCap = false; + } + gn->version = event->event.notify60[n].version; + gn->image_size = event->event.notify60[n].image_size; + gn->description = textcodec->toUnicode( event->event.notify60[n].descr ); + emit contactStatusChanged( gn ); + } + delete gn; +} + +void +GaduSession::checkDescriptor() +{ + disableNotifiers(); + + struct gg_event* event; +// struct gg_dcc* dccSock; + KGaduMessage gaduMessage; + KGaduNotify gaduNotify; + + if ( !( event = gg_watch_fd( session_ ) ) ) { + kdDebug(14100)<<"Connection was broken for some reason"<<endl; + destroyNotifiers(); + logoff( Kopete::Account::ConnectionReset ); + return; + } + + // FD changed, recreate socket notifiers + if ( session_->state == GG_STATE_CONNECTING_HUB || session_->state == GG_STATE_CONNECTING_GG ) { + kdDebug(14100)<<"recreating notifiers"<<endl; + destroyNotifiers(); + createNotifiers( true ); + } + + switch( event->type ) { + case GG_EVENT_MSG: + kdDebug(14100) << "incoming message:class:" << event->event.msg.msgclass << endl; + if ( event->event.msg.msgclass & GG_CLASS_CTCP ) { + kdDebug( 14100 ) << "incomming ctcp " << endl; + // TODO: DCC CONNECTION + emit incomingCtcp( event->event.msg.sender ); + } + + if ( (event->event.msg.msgclass & GG_CLASS_MSG) || (event->event.msg.msgclass & GG_CLASS_CHAT) ) { + gaduMessage.message = + textcodec->toUnicode((const char*)event->event.msg.message); + gaduMessage.sender_id = event->event.msg.sender; + gaduMessage.sendTime.setTime_t( event->event.msg.time, Qt::LocalTime ); + gaduMessage.message = rtf->convertToHtml( gaduMessage.message, event->event.msg.formats_length, event->event.msg.formats ); + emit messageReceived( &gaduMessage ); + } + break; + case GG_EVENT_ACK: + emit ackReceived( event->event.ack.recipient ); + break; + case GG_EVENT_STATUS: + gaduNotify.status = event->event.status.status; + gaduNotify.contact_id = event->event.status.uin; + if ( event->event.status.descr ) { + gaduNotify.description = textcodec->toUnicode( event->event.status.descr ); + } + else { + gaduNotify.description = QString::null; + } + gaduNotify.remote_port = 0; + gaduNotify.version = 0; + gaduNotify.image_size = 0; + gaduNotify.time = 0; + gaduNotify.fileCap = false; + + emit contactStatusChanged( &gaduNotify ); + break; + case GG_EVENT_STATUS60: + gaduNotify.status = event->event.status60.status; + gaduNotify.contact_id = event->event.status60.uin; + if ( event->event.status60.descr ) { + gaduNotify.description = textcodec->toUnicode( event->event.status60.descr ); + } + else { + gaduNotify.description = QString::null; + } + gaduNotify.remote_ip.setAddress( ntohl( event->event.status60.remote_ip ) ); + gaduNotify.remote_port = event->event.status60.remote_port; + gaduNotify.version = event->event.status60.version; + gaduNotify.image_size = event->event.status60.image_size; + gaduNotify.time = event->event.status60.time; + if ( event->event.status60.remote_ip && gaduNotify.remote_port > 10 ) { + gaduNotify.fileCap = true; + } + else { + gaduNotify.fileCap = false; + } + + emit contactStatusChanged( &gaduNotify ); + break; + case GG_EVENT_NOTIFY60: + notify60( event ); + break; + case GG_EVENT_CONN_SUCCESS: + kdDebug(14100) << "success server: " << session_->server_addr << endl; + emit connectionSucceed(); + break; + case GG_EVENT_CONN_FAILED: + kdDebug(14100) << "failed server: " << session_->server_addr << endl; + destroySession(); + kdDebug(14100) << "emit connection failed(" << event->event.failure << ") signal" << endl; + emit connectionFailed( (gg_failure_t)event->event.failure ); + break; + case GG_EVENT_DISCONNECT: + kdDebug(14100)<<"event Disconnected"<<endl; + // it should be called either when we requested disconnect, or when other client connects with our UID + logoff( Kopete::Account::Manual ); + break; + case GG_EVENT_PONG: + emit pong(); + break; + case GG_EVENT_NONE: + break; + case GG_EVENT_PUBDIR50_SEARCH_REPLY: + case GG_EVENT_PUBDIR50_WRITE: + case GG_EVENT_PUBDIR50_READ: + sendResult( event->event.pubdir50 ); + break; + case GG_EVENT_USERLIST: + handleUserlist( event ); + break; + default: + kdDebug(14100)<<"Unprocessed GaduGadu Event = "<<event->type<<endl; + break; + } + + if ( event ) { + gg_free_event( event ); + } + + if ( session_ ) { + enableNotifiers( session_->check ); + } +} + +#include "gadusession.moc" diff --git a/kopete/protocols/gadu/gadusession.h b/kopete/protocols/gadu/gadusession.h new file mode 100644 index 00000000..79626d7f --- /dev/null +++ b/kopete/protocols/gadu/gadusession.h @@ -0,0 +1,178 @@ +// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*- +// +// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl> +// Copyright (C) 2002 Zack Rusin <zack@kde.org> +// +// gadusession.h +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. + +#ifndef GADUSESSION_H +#define GADUSESSION_H + +#include "kopeteaccount.h" + +#include <qvaluelist.h> +#include <qptrlist.h> +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdatetime.h> +#include <qcstring.h> +#include <qhostaddress.h> + +#include "gaducontactlist.h" + +#include <libgadu.h> + +struct KGaduMessage { + QString message; // Unicode + unsigned int sender_id; // sender's UIN + QDateTime sendTime; + QByteArray rtf; +}; + +struct KGaduLoginParams { + uin_t uin; + QString password; + bool useTls; + int status; + QString statusDescr; + unsigned int server; + bool forFriends; + unsigned int client_addr; + unsigned int client_port; +}; + +struct KGaduNotify { + int status; + QHostAddress remote_ip; + unsigned short remote_port; + bool fileCap; + int version; + int image_size; + int time; + QString description; + unsigned int contact_id; +}; + +struct ResLine{ + unsigned int uin; + QString firstname; + QString surname; + QString nickname; + QString age; + QString city; + QString orgin; + QString meiden; + QString gender; + int status; +}; + +typedef QValueList<ResLine> SearchResult; + +class QSocketNotifier; +class QStringList; +namespace Kopete { class Message; } +class GaduRichTextFormat; + +class GaduSession : public QObject +{ + Q_OBJECT + +public: + GaduSession( QObject* parent = 0, const char* name = 0 ); + virtual ~GaduSession(); + bool isConnected() const; + int status() const; + QString contactsToString( GaduContactsList* contactsList ); + bool stringToContacts( GaduContactsList& , const QString& ); + static QString failureDescription( gg_failure_t ); + static QString errorDescription( int err ); + static QString stateDescription( int state ); + void dccRequest( const unsigned int ); + unsigned int getPersonalInformation(); + /* + * Initiates search in public directory, we need to be logged on to perform search ! + * This returns 0, if you are unable to search (fe you are not logged on, you don't have memory) + * This does not checks parametrs ! + * Calling this function more times with the same params, will continue this search as long as + * @ref pubDirSearchClose() will not be called + * You must set @ref pubDirSearchResult() signal before calling this function, otherwise no result + * will be returned + */ + unsigned int pubDirSearch( ResLine&, int, int, bool ); + +public slots: + void login( KGaduLoginParams* login ); + void logoff( Kopete::Account::DisconnectReason reason = Kopete::Account::Manual ); + int notify( uin_t*, int ); + int addNotify( uin_t ); + int removeNotify( uin_t ); + int sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass ); + int changeStatus( int, bool forFriends = false ); + int changeStatusDescription( int, const QString&, bool forFriends = false ); + int ping(); + + void requestContacts(); + + /* + * Releases all allocated memory needed to perform search. + * This will be done on each @ref pubDirNewSearch(), if previuos is not released + */ + void pubDirSearchClose(); + void exportContactsOnServer( GaduContactsList* ); + bool publishPersonalInformation( ResLine& ); + +signals: + void error( const QString&, const QString& ); + void messageReceived( KGaduMessage* ); + void ackReceived( unsigned int ); + void contactStatusChanged( KGaduNotify* ); + void pong(); + void connectionFailed( gg_failure_t failure ); + void connectionSucceed( ); + void disconnect( Kopete::Account::DisconnectReason ); + void pubDirSearchResult( const SearchResult&, unsigned int ); + void userListRecieved( const QString& ); + void userListExported(); + void incomingCtcp( unsigned int ); + +protected slots: + void enableNotifiers( int ); + void disableNotifiers(); + void checkDescriptor(); + void login( struct gg_login_params* ); + +private: + + void sendResult( gg_pubdir50_t ); + void handleUserlist( gg_event* ); + void notify60( gg_event* ); + void destroySession(); + void destroyNotifiers(); + void createNotifiers( bool connect ); + + gg_session* session_; + QSocketNotifier* read_; + QSocketNotifier* write_; + gg_login_params params_; + QTextCodec* textcodec; + int searchSeqNr_; + GaduRichTextFormat* rtf; +}; + +#endif diff --git a/kopete/protocols/gadu/icons/Makefile.am b/kopete/protocols/gadu/icons/Makefile.am new file mode 100644 index 00000000..9143c6b4 --- /dev/null +++ b/kopete/protocols/gadu/icons/Makefile.am @@ -0,0 +1,2 @@ +kopeteicondir = $(kde_datadir)/kopete/icons +kopeteicon_ICON = AUTO diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_away.png b/kopete/protocols/gadu/icons/cr16-action-gg_away.png Binary files differnew file mode 100644 index 00000000..a3ecf056 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_away.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png Binary files differnew file mode 100644 index 00000000..18d1640c --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png Binary files differnew file mode 100644 index 00000000..d9790b54 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_con.mng b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng Binary files differnew file mode 100644 index 00000000..64eea9ea --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png Binary files differnew file mode 100644 index 00000000..23d80cb4 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png Binary files differnew file mode 100644 index 00000000..128e27eb --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png Binary files differnew file mode 100644 index 00000000..98674ef5 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png Binary files differnew file mode 100644 index 00000000..ab994042 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png Binary files differnew file mode 100644 index 00000000..e2f44f62 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png Binary files differnew file mode 100644 index 00000000..65569e12 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png Binary files differnew file mode 100644 index 00000000..5b5740b3 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online.png b/kopete/protocols/gadu/icons/cr16-action-gg_online.png Binary files differnew file mode 100644 index 00000000..652c127a --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_online.png diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png Binary files differnew file mode 100644 index 00000000..26b28d49 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png diff --git a/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png Binary files differnew file mode 100644 index 00000000..46f062c3 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png diff --git a/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png Binary files differnew file mode 100644 index 00000000..2ff51736 --- /dev/null +++ b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png diff --git a/kopete/protocols/gadu/kopete_gadu.desktop b/kopete/protocols/gadu/kopete_gadu.desktop new file mode 100644 index 00000000..5c2750d5 --- /dev/null +++ b/kopete/protocols/gadu/kopete_gadu.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +Type=Service +X-Kopete-Version=1000900 +Icon=gadu_protocol +ServiceTypes=Kopete/Protocol +X-Kopete-Messaging-Protocol=messaging/gadu +X-KDE-Library=kopete_gadu +X-KDE-PluginInfo-Author=Grzegorz Jaskiewicz +X-KDE-PluginInfo-Email=gj@pointblue.com.pl +X-KDE-PluginInfo-Name=kopete_gadu +X-KDE-PluginInfo-Version=0.8.0 +X-KDE-PluginInfo-Website=http://kopete.kde.org +X-KDE-PluginInfo-Category=Protocols +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +Name=Gadu-Gadu +Name[fa]=Gadu-Gadu‌ +Name[hi]=गडू-गडू +Name[ne]=गादà¥-गादॠ+Name[tr]=Gadu Gadu +Comment=Protocol to connect to Gadu-Gadu +Comment[ar]=البرتوكول سيتصل بـ Gadu-Gadu +Comment[be]=Пратакол Gadu-Gadu +Comment[bg]=Протокол за връзка Ñ Gadu-Gadu +Comment[bn]=Gadu-Gadu-তে সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦² +Comment[br]=Komenad kevreañ ouzh Gadu-Gadu +Comment[bs]=Gadu-Gadu protokol +Comment[ca]=Protocol per a connectar-se a Gadu-Gadu +Comment[cs]=Protokol k pÅ™ipojenà ke Gadu-Gadu +Comment[cy]=Protocol i gysylltu â Gadu-Gadu +Comment[da]=Protokol til at forbinde til Gadu-Gadu +Comment[de]=Protokoll zur Verbindung mit Gadu-Gadu +Comment[el]=Î Ïωτόκολλο για σÏνδεση στο Gadu-Gadu +Comment[es]=Protocolo de conexión a Gadu-Gadu +Comment[et]=Protokoll ühendumiseks Gadu-Gaduga +Comment[eu]=Gadu-Gadu-ra konektatzeko protokoloa +Comment[fa]=قرارداد برای اتصال Gadu-Gadu‌ +Comment[fi]=Yhteyskäytäntö Gadu-Gadu-verkkoon kytkeytymiseen +Comment[fr]=Protocole pour se connecter sur Gadu-Gadu +Comment[ga]=Prótacal chun ceangal le Gadu-Gadu +Comment[gl]=Protocolo para conectarse a Gadu-Gadu +Comment[he]=פרוטוקול התחברות ל- Gadu-Gadu +Comment[hi]=गडू-गडू से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤² +Comment[hr]=Protokol za povezivanje na Gadu-Gadu +Comment[hu]=Protokoll a Gadu-Gadu használatához +Comment[is]=Samskiptamáti til að tengjast Gadu-Gadu +Comment[it]=Protocollo per connessione a Gadu-Gadu +Comment[ja]=Gadu-Gadu ã«æŽ¥ç¶šã™ã‚‹ãƒ—ãƒãƒˆã‚³ãƒ« +Comment[ka]=Gadu-Gadu-სთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ ების áƒáƒ¥áƒ›áƒ˜ +Comment[kk]=Gadu-Gadu-ға қоÑылу протоколы +Comment[km]=ពិធីការ​​ភ្ជាប់​ទៅ Gadu-Gadu​ +Comment[lt]=Protokolas prisijungimui prie Gadu-Gadu +Comment[mk]=Протокол за поврзување на Gadu-Gadu +Comment[nb]=Protokoll for Ã¥ koble til Gadu-Gadu +Comment[nds]=Protokoll för't Tokoppeln na Gadu-Gadu +Comment[ne]=गादà¥-गादà¥à¤®à¤¾ जडान गरà¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤² +Comment[nl]=Protocol voor Gadu-Gadu +Comment[nn]=Protokoll for Ã¥ kopla til Gadu-Gadu +Comment[pl]=Protokół poÅ‚Ä…czenia z serwerem Gadu-Gadu +Comment[pt]=Um protocolo para se ligar ao Gadu-Gadu +Comment[pt_BR]=Protocolo para conexão ao Gadu-Gadu +Comment[ro]=Protocol de conectare la Gadu-Gadu +Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Gadu-Gadu +Comment[sk]=Protokol pre pripojenie k Gadu-Gadu +Comment[sl]=Protokol za povezavo na Gadu-Gadu +Comment[sr]=Протокол за повезивање на Gadu-Gadu +Comment[sr@Latn]=Protokol za povezivanje na Gadu-Gadu +Comment[sv]=Protokoll för att ansluta till Gadu-Gadu +Comment[ta]=Gadu-Gadu உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ +Comment[tg]=Қарордоди пайваÑтшавӣ ба Gadu-Gadu +Comment[tr]=Gadu Gadu'ya baÄŸlantı iletiÅŸim kuralı +Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Gadu-Gadu +Comment[uz]=Gadu-Gadu bilan aloqa oÊ»rnatish uchun protokol +Comment[uz@cyrillic]=Gadu-Gadu билан алоқа ўрнатиш учун протокол +Comment[zh_CN]=连接到 Gadu-Gadu åè®® +Comment[zh_HK]=用來連接至 Gadu-Gadu 的通訊å”定 +Comment[zh_TW]=連線到 Gadu-Gadu çš„å”定 diff --git a/kopete/protocols/gadu/libgadu/COPYING b/kopete/protocols/gadu/libgadu/COPYING new file mode 100644 index 00000000..512ede36 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/kopete/protocols/gadu/libgadu/Makefile.am b/kopete/protocols/gadu/libgadu/Makefile.am new file mode 100644 index 00000000..94693d38 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO +noinst_LTLIBRARIES = libgadu_copy.la +INCLUDES = $(SSL_INCLUDES) $(all_includes) +libgadu_copy_la_LDFLAGS = $(SSL_LDFLAGS) $(all_libraries) +libgadu_copy_la_LIBADD = $(LIBSSL) $(LIBPTHREAD) +libgadu_copy_la_SOURCES = common.c \ + dcc.c \ + events.c \ + http.c \ + libgadu.c \ + pubdir50.c \ + pubdir.c diff --git a/kopete/protocols/gadu/libgadu/common.c b/kopete/protocols/gadu/libgadu/common.c new file mode 100644 index 00000000..2e835fca --- /dev/null +++ b/kopete/protocols/gadu/libgadu/common.c @@ -0,0 +1,822 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo¼ny <speedy@ziew.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifdef sun +# include <sys/filio.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libgadu.h" + +FILE *gg_debug_file = NULL; + +#ifndef GG_DEBUG_DISABLE + +/* + * gg_debug() // funkcja wewnêtrzna + * + * wy¶wietla komunikat o danym poziomie, o ile u¿ytkownik sobie tego ¿yczy. + * + * - level - poziom wiadomo¶ci + * - format... - tre¶æ wiadomo¶ci (kompatybilna z printf()) + */ +void gg_debug(int level, const char *format, ...) +{ + va_list ap; + int old_errno = errno; + + if (gg_debug_handler) { + va_start(ap, format); + (*gg_debug_handler)(level, format, ap); + va_end(ap); + + goto cleanup; + } + + if ((gg_debug_level & level)) { + va_start(ap, format); + vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); + va_end(ap); + } + +cleanup: + errno = old_errno; +} + +#endif + +/* + * gg_vsaprintf() // funkcja pomocnicza + * + * robi dok³adnie to samo, co vsprintf(), tyle ¿e alokuje sobie wcze¶niej + * miejsce na dane. powinno dzia³aæ na tych maszynach, które maj± funkcjê + * vsnprintf() zgodn± z C99, jak i na wcze¶niejszych. + * + * - format - opis wy¶wietlanego tekstu jak dla printf() + * - ap - lista argumentów dla printf() + * + * zaalokowany bufor, który nale¿y pó¼niej zwolniæ, lub NULL + * je¶li nie uda³o siê wykonaæ zadania. + */ +char *gg_vsaprintf(const char *format, va_list ap) +{ + int size = 0; + const char *start; + char *buf = NULL; + +#ifdef __GG_LIBGADU_HAVE_VA_COPY + va_list aq; + + va_copy(aq, ap); +#else +# ifdef __GG_LIBGADU_HAVE___VA_COPY + va_list aq; + + __va_copy(aq, ap); +# endif +#endif + + start = format; + +#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF + { + int res; + char *tmp; + + size = 128; + do { + size *= 2; + if (!(tmp = realloc(buf, size))) { + free(buf); + return NULL; + } + buf = tmp; + res = vsnprintf(buf, size, format, ap); + } while (res == size - 1 || res == -1); + } +#else + { + char tmp[2]; + + /* libce Solarisa przy buforze NULL zawsze zwracaj± -1, wiêc + * musimy podaæ co¶ istniej±cego jako cel printf()owania. */ + size = vsnprintf(tmp, sizeof(tmp), format, ap); + if (!(buf = malloc(size + 1))) + return NULL; + } +#endif + + format = start; + +#ifdef __GG_LIBGADU_HAVE_VA_COPY + vsnprintf(buf, size + 1, format, aq); + va_end(aq); +#else +# ifdef __GG_LIBGADU_HAVE___VA_COPY + vsnprintf(buf, size + 1, format, aq); + va_end(aq); +# else + vsnprintf(buf, size + 1, format, ap); +# endif +#endif + + return buf; +} + +/* + * gg_saprintf() // funkcja pomocnicza + * + * robi dok³adnie to samo, co sprintf(), tyle ¿e alokuje sobie wcze¶niej + * miejsce na dane. powinno dzia³aæ na tych maszynach, które maj± funkcjê + * vsnprintf() zgodn± z C99, jak i na wcze¶niejszych. + * + * - format... - tre¶æ taka sama jak w funkcji printf() + * + * zaalokowany bufor, który nale¿y pó¼niej zwolniæ, lub NULL + * je¶li nie uda³o siê wykonaæ zadania. + */ +char *gg_saprintf(const char *format, ...) +{ + va_list ap; + char *res; + + va_start(ap, format); + res = gg_vsaprintf(format, ap); + va_end(ap); + + return res; +} + +/* + * gg_get_line() // funkcja pomocnicza + * + * podaje kolejn± liniê z bufora tekstowego. niszczy go bezpowrotnie, dziel±c + * na kolejne stringi. zdarza siê, nie ma potrzeby pisania funkcji dubluj±cej + * bufor ¿eby tylko mieæ nieruszone dane wej¶ciowe, skoro i tak nie bêd± nam + * po¼niej potrzebne. obcina `\r\n'. + * + * - ptr - wska¼nik do zmiennej, która przechowuje aktualn± pozycjê + * w przemiatanym buforze + * + * wska¼nik do kolejnej linii tekstu lub NULL, je¶li to ju¿ koniec bufora. + */ +char *gg_get_line(char **ptr) +{ + char *foo, *res; + + if (!ptr || !*ptr || !strcmp(*ptr, "")) + return NULL; + + res = *ptr; + + if (!(foo = strchr(*ptr, '\n'))) + *ptr += strlen(*ptr); + else { + *ptr = foo + 1; + *foo = 0; + if (strlen(res) > 1 && res[strlen(res) - 1] == '\r') + res[strlen(res) - 1] = 0; + } + + return res; +} + +/* + * gg_connect() // funkcja pomocnicza + * + * ³±czy siê z serwerem. pierwszy argument jest typu (void *), ¿eby nie + * musieæ niczego inkludowaæ w libgadu.h i nie psuæ jaki¶ g³upich zale¿no¶ci + * na dziwnych systemach. + * + * - addr - adres serwera (struct in_addr *) + * - port - port serwera + * - async - asynchroniczne po³±czenie + * + * deskryptor gniazda lub -1 w przypadku b³êdu (kod b³êdu w zmiennej errno). + */ +int gg_connect(void *addr, int port, int async) +{ + int sock, one = 1, errno2; + struct sockaddr_in sin; + struct in_addr *a = addr; + struct sockaddr_in myaddr; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async); + + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + memset(&myaddr, 0, sizeof(myaddr)); + myaddr.sin_family = AF_INET; + + myaddr.sin_addr.s_addr = gg_local_ip; + + if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + +#ifdef ASSIGN_SOCKETS_TO_THREADS + gg_win32_thread_socket(0, sock); +#endif + + if (async) { +#ifdef FIONBIO + if (ioctl(sock, FIONBIO, &one) == -1) { +#else + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + } + + sin.sin_port = htons(port); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->s_addr; + + if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) { + if (errno && (!async || errno != EINPROGRESS)) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n"); + } + + return sock; +} + +/* + * gg_read_line() // funkcja pomocnicza + * + * czyta jedn± liniê tekstu z gniazda. + * + * - sock - deskryptor gniazda + * - buf - wska¼nik do bufora + * - length - d³ugo¶æ bufora + * + * je¶li trafi na b³±d odczytu lub podano nieprawid³owe parametry, zwraca NULL. + * inaczej zwraca buf. + */ +char *gg_read_line(int sock, char *buf, int length) +{ + int ret; + + if (!buf || length < 0) + return NULL; + + for (; length > 1; buf++, length--) { + do { + if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) { + gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno)); + *buf = 0; + return NULL; + } else if (ret == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n"); + *buf = 0; + return NULL; + } + } while (ret == -1 && errno == EINTR); + + if (*buf == '\n') { + buf++; + break; + } + } + + *buf = 0; + return buf; +} + +/* + * gg_chomp() // funkcja pomocnicza + * + * ucina "\r\n" lub "\n" z koñca linii. + * + * - line - linia do przyciêcia + */ +void gg_chomp(char *line) +{ + int len; + + if (!line) + return; + + len = strlen(line); + + if (len > 0 && line[len - 1] == '\n') + line[--len] = 0; + if (len > 0 && line[len - 1] == '\r') + line[--len] = 0; +} + +/* + * gg_urlencode() // funkcja wewnêtrzna + * + * zamienia podany tekst na ci±g znaków do formularza http. przydaje siê + * przy ró¿nych us³ugach katalogu publicznego. + * + * - str - ci±g znaków do zakodowania + * + * zaalokowany bufor, który nale¿y pó¼niej zwolniæ albo NULL + * w przypadku b³êdu. + */ +char *gg_urlencode(const char *str) +{ + char *q, *buf, hex[] = "0123456789abcdef"; + const char *p; + unsigned int size = 0; + + if (!str) + str = ""; + + for (p = str; *p; p++, size++) { + if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-')) + size += 2; + } + + if (!(buf = malloc(size + 1))) + return NULL; + + for (p = str, q = buf; *p; p++, q++) { + if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-')) + *q = *p; + else { + if (*p == ' ') + *q = '+'; + else { + *q++ = '%'; + *q++ = hex[*p >> 4 & 15]; + *q = hex[*p & 15]; + } + } + } + + *q = 0; + + return buf; +} + +/* + * gg_http_hash() // funkcja wewnêtrzna + * + * funkcja licz±ca hash dla adresu e-mail, has³a i paru innych. + * + * - format... - format kolejnych parametrów ('s' je¶li dany parametr jest + * ci±giem znaków lub 'u' je¶li numerem GG) + * + * hash wykorzystywany przy rejestracji i wszelkich manipulacjach w³asnego + * wpisu w katalogu publicznym. + */ +int gg_http_hash(const char *format, ...) +{ + unsigned int a, c, i, j; + va_list ap; + int b = -1; + + va_start(ap, format); + + for (j = 0; j < strlen(format); j++) { + char *arg, buf[16]; + + if (format[j] == 'u') { + snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t)); + arg = buf; + } else { + if (!(arg = va_arg(ap, char*))) + arg = ""; + } + + i = 0; + while ((c = (unsigned char) arg[i++]) != 0) { + a = (c ^ b) + (c << 8); + b = (a >> 24) | (a << 8); + } + } + + va_end(ap); + + return (b < 0 ? -b : b); +} + +/* + * gg_gethostbyname() // funkcja pomocnicza + * + * odpowiednik gethostbyname() troszcz±cy siê o wspó³bie¿no¶æ, gdy mamy do + * dyspozycji funkcjê gethostbyname_r(). + * + * - hostname - nazwa serwera + * + * zwraca wska¼nik na strukturê in_addr, któr± nale¿y zwolniæ. + */ +struct in_addr *gg_gethostbyname(const char *hostname) +{ + struct in_addr *addr = NULL; + +#ifdef HAVE_GETHOSTBYNAME_R + char *tmpbuf = NULL, *buf = NULL; + struct hostent *hp = NULL, *hp2 = NULL; + int h_errnop, ret; + size_t buflen = 1024; + int new_errno; + + new_errno = ENOMEM; + + if (!(addr = malloc(sizeof(struct in_addr)))) + goto cleanup; + + if (!(hp = calloc(1, sizeof(*hp)))) + goto cleanup; + + if (!(buf = malloc(buflen))) + goto cleanup; + + tmpbuf = buf; + + while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) { + buflen *= 2; + + if (!(tmpbuf = realloc(buf, buflen))) + break; + + buf = tmpbuf; + } + + if (ret) + new_errno = h_errnop; + + if (ret || !hp2 || !tmpbuf) + goto cleanup; + + memcpy(addr, hp->h_addr, sizeof(struct in_addr)); + + free(buf); + free(hp); + + return addr; + +cleanup: + errno = new_errno; + + if (addr) + free(addr); + if (hp) + free(hp); + if (buf) + free(buf); + + return NULL; +#else + struct hostent *hp; + + if (!(addr = malloc(sizeof(struct in_addr)))) { + goto cleanup; + } + + if (!(hp = gethostbyname(hostname))) + goto cleanup; + + memcpy(addr, hp->h_addr, sizeof(struct in_addr)); + + return addr; + +cleanup: + if (addr) + free(addr); + + return NULL; +#endif +} + +#ifdef ASSIGN_SOCKETS_TO_THREADS + +typedef struct gg_win32_thread { + int id; + int socket; + struct gg_win32_thread *next; +} gg_win32_thread; + +struct gg_win32_thread *gg_win32_threads = 0; + +/* + * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32 + * + * zwraca deskryptor gniazda, które by³o ostatnio tworzone dla w±tku + * o podanym identyfikatorze. + * + * je¶li na win32 przy po³±czeniach synchronicznych zapamiêtamy w jakim + * w±tku uruchomili¶my funkcjê, która siê z czymkolwiek ³±czy, to z osobnego + * w±tku mo¿emy anulowaæ po³±czenie poprzez gg_win32_thread_socket(watek, -1); + * + * - thread_id - id w±tku. je¶li jest równe 0, brany jest aktualny w±tek, + * je¶li równe -1, usuwa wpis o podanym sockecie. + * - socket - deskryptor gniazda. je¶li równe 0, zwraca deskryptor gniazda + * dla podanego w±tku, je¶li równe -1, usuwa wpis, je¶li co¶ + * innego, ustawia dla podanego w±tku dany numer deskryptora. + * + * je¶li socket jest równe 0, zwraca deskryptor gniazda dla podanego w±tku. + */ +int gg_win32_thread_socket(int thread_id, int socket) +{ + char close = (thread_id == -1) || socket == -1; + gg_win32_thread *wsk = gg_win32_threads; + gg_win32_thread **p_wsk = &gg_win32_threads; + + if (!thread_id) + thread_id = GetCurrentThreadId(); + + while (wsk) { + if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) { + if (close) { + /* socket zostaje usuniety */ + closesocket(wsk->socket); + *p_wsk = wsk->next; + free(wsk); + return 1; + } else if (!socket) { + /* socket zostaje zwrocony */ + return wsk->socket; + } else { + /* socket zostaje ustawiony */ + wsk->socket = socket; + return socket; + } + } + p_wsk = &(wsk->next); + wsk = wsk->next; + } + + if (close && socket != -1) + closesocket(socket); + if (close || !socket) + return 0; + + /* Dodaje nowy element */ + wsk = malloc(sizeof(gg_win32_thread)); + wsk->id = thread_id; + wsk->socket = socket; + wsk->next = 0; + *p_wsk = wsk; + + return socket; +} + +#endif /* ASSIGN_SOCKETS_TO_THREADS */ + +static char gg_base64_charset[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* + * gg_base64_encode() + * + * zapisuje ci±g znaków w base64. + * + * - buf - ci±g znaków. + * + * zaalokowany bufor. + */ +char *gg_base64_encode(const char *buf) +{ + char *out, *res; + unsigned int i = 0, j = 0, k = 0, len = strlen(buf); + + res = out = malloc((len / 3 + 1) * 4 + 2); + + if (!res) + return NULL; + + while (j <= len) { + switch (i % 4) { + case 0: + k = (buf[j] & 252) >> 2; + break; + case 1: + if (j < len) + k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4); + else + k = (buf[j] & 3) << 4; + + j++; + break; + case 2: + if (j < len) + k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6); + else + k = (buf[j] & 15) << 2; + + j++; + break; + case 3: + k = buf[j++] & 63; + break; + } + *out++ = gg_base64_charset[k]; + i++; + } + + if (i % 4) + for (j = 0; j < 4 - (i % 4); j++, out++) + *out = '='; + + *out = 0; + + return res; +} + +/* + * gg_base64_decode() + * + * dekoduje ci±g znaków z base64. + * + * - buf - ci±g znaków. + * + * zaalokowany bufor. + */ +char *gg_base64_decode(const char *buf) +{ + char *res, *save, *foo, val; + const char *end; + unsigned int index = 0; + + if (!buf) + return NULL; + + save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2); + + if (!save) + return NULL; + + end = buf + strlen(buf); + + while (*buf && buf < end) { + if (*buf == '\r' || *buf == '\n') { + buf++; + continue; + } + if (!(foo = strchr(gg_base64_charset, *buf))) + foo = gg_base64_charset; + val = (int)(foo - gg_base64_charset); + buf++; + switch (index) { + case 0: + *res |= val << 2; + break; + case 1: + *res++ |= val >> 4; + *res |= val << 4; + break; + case 2: + *res++ |= val >> 2; + *res |= val << 6; + break; + case 3: + *res++ |= val; + break; + } + index++; + index %= 4; + } + *res = 0; + + return save; +} + +/* + * gg_proxy_auth() // funkcja wewnêtrzna + * + * tworzy nag³ówek autoryzacji dla proxy. + * + * zaalokowany tekst lub NULL, je¶li proxy nie jest w³±czone lub nie wymaga + * autoryzacji. + */ +char *gg_proxy_auth() +{ + char *tmp, *enc, *out; + unsigned int tmp_size; + + if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password) + return NULL; + + if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2)))) + return NULL; + + snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password); + + if (!(enc = gg_base64_encode(tmp))) { + free(tmp); + return NULL; + } + + free(tmp); + + if (!(out = malloc(strlen(enc) + 40))) { + free(enc); + return NULL; + } + + snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc); + + free(enc); + + return out; +} + +static uint32_t gg_crc32_table[256]; +static int gg_crc32_initialized = 0; + +/* + * gg_crc32_make_table() // funkcja wewnêtrzna + */ +static void gg_crc32_make_table() +{ + uint32_t h = 1; + unsigned int i, j; + + memset(gg_crc32_table, 0, sizeof(gg_crc32_table)); + + for (i = 128; i; i >>= 1) { + h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0); + + for (j = 0; j < 256; j += 2 * i) + gg_crc32_table[i + j] = gg_crc32_table[j] ^ h; + } + + gg_crc32_initialized = 1; +} + +/* + * gg_crc32() + * + * wyznacza sumê kontroln± CRC32 danego bloku danych. + * + * - crc - suma kontrola poprzedniego bloku danych lub 0 je¶li pierwszy + * - buf - bufor danych + * - size - ilo¶æ danych + * + * suma kontrolna CRC32. + */ +uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len) +{ + if (!gg_crc32_initialized) + gg_crc32_make_table(); + + if (!buf || len < 0) + return crc; + + crc ^= 0xffffffffL; + + while (len--) + crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff]; + + return crc ^ 0xffffffffL; +} + + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/compat.h b/kopete/protocols/gadu/libgadu/compat.h new file mode 100644 index 00000000..715fcb3a --- /dev/null +++ b/kopete/protocols/gadu/libgadu/compat.h @@ -0,0 +1,29 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo¼ny <speedy@ziew.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __COMPAT_H +#define __COMPAT_H + +#ifdef sun +# define INADDR_NONE ((in_addr_t) 0xffffffff) +#endif + +#endif diff --git a/kopete/protocols/gadu/libgadu/dcc.c b/kopete/protocols/gadu/libgadu/dcc.c new file mode 100644 index 00000000..085d9d01 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/dcc.c @@ -0,0 +1,1298 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Tomasz Chiliñski <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifdef sun +# include <sys/filio.h> +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "compat.h" +#include "libgadu.h" + +#ifndef GG_DEBUG_DISABLE +/* + * gg_dcc_debug_data() // funkcja wewnêtrzna + * + * wy¶wietla zrzut pakietu w hexie. + * + * - prefix - prefiks zrzutu pakietu + * - fd - deskryptor gniazda + * - buf - bufor z danymi + * - size - rozmiar danych + */ +static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) +{ + unsigned int i; + + gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); + + for (i = 0; i < size; i++) + gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]); + + gg_debug(GG_DEBUG_MISC, "\n"); +} +#else +#define gg_dcc_debug_data(a,b,c,d) do { } while (0) +#endif + +/* + * gg_dcc_request() + * + * wysy³a informacjê o tym, ¿e dany klient powinien siê z nami po³±czyæ. + * wykorzystywane, kiedy druga strona, której chcemy co¶ wys³aæ jest za + * maskarad±. + * + * - sess - struktura opisuj±ca sesjê GG + * - uin - numerek odbiorcy + * + * patrz gg_send_message_ctcp(). + */ +int gg_dcc_request(struct gg_session *sess, uin_t uin) +{ + return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, "\002", 1); +} + +/* + * gg_dcc_fill_filetime() // funkcja wewnêtrzna + * + * zamienia czas w postaci unixowej na windowsowy. + * + * - unix - czas w postaci unixowej + * - filetime - czas w postaci windowsowej + */ +static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft) +{ +#ifdef __GG_LIBGADU_HAVE_LONG_LONG + unsigned long long tmp; + + tmp = ut; + tmp += 11644473600LL; + tmp *= 10000000LL; + +#ifndef __GG_LIBGADU_BIGENDIAN + ft[0] = (uint32_t) tmp; + ft[1] = (uint32_t) (tmp >> 32); +#else + ft[0] = gg_fix32((uint32_t) (tmp >> 32)); + ft[1] = gg_fix32((uint32_t) tmp); +#endif + +#endif +} + +/* + * gg_dcc_fill_file_info() + * + * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku. + * + * - d - struktura opisuj±ca po³±czenie DCC + * - filename - nazwa pliku + * + * 0, -1. + */ +int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) +{ + return gg_dcc_fill_file_info2(d, filename, filename); +} + +/* + * gg_dcc_fill_file_info2() + * + * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku. + * + * - d - struktura opisuj±ca po³±czenie DCC + * - filename - nazwa pliku + * - local_filename - nazwa na lokalnym systemie plików + * + * 0, -1. + */ +int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) +{ + struct stat st; + const char *name, *ext, *p; + unsigned char *q; + int i, j; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename); + + if (!d || d->type != GG_SESSION_DCC_SEND) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n"); + errno = EINVAL; + return -1; + } + + if (stat(local_filename, &st) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno)); + return -1; + } + + if ((st.st_mode & S_IFDIR)) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n"); + errno = EINVAL; + return -1; + } + + if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno)); + return -1; + } + + memset(&d->file_info, 0, sizeof(d->file_info)); + + if (!(st.st_mode & S_IWUSR)) + d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY); + + gg_dcc_fill_filetime(st.st_atime, d->file_info.atime); + gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime); + gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime); + + d->file_info.size = gg_fix32(st.st_size); + d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */ + + if (!(name = strrchr(filename, '/'))) + name = filename; + else + name++; + + if (!(ext = strrchr(name, '.'))) + ext = name + strlen(name); + + for (i = 0, p = name; i < 8 && p < ext; i++, p++) + d->file_info.short_filename[i] = toupper(name[i]); + + if (i == 8 && p < ext) { + d->file_info.short_filename[6] = '~'; + d->file_info.short_filename[7] = '1'; + } + + if (strlen(ext) > 0) { + for (j = 0; *ext && j < 4; j++, p++) + d->file_info.short_filename[i + j] = toupper(ext[j]); + } + + for (q = d->file_info.short_filename; *q; q++) { + if (*q == 185) { + *q = 165; + } else if (*q == 230) { + *q = 198; + } else if (*q == 234) { + *q = 202; + } else if (*q == 179) { + *q = 163; + } else if (*q == 241) { + *q = 209; + } else if (*q == 243) { + *q = 211; + } else if (*q == 156) { + *q = 140; + } else if (*q == 159) { + *q = 143; + } else if (*q == 191) { + *q = 175; + } + } + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); + strncpy(d->file_info.filename, name, sizeof(d->file_info.filename) - 1); + + return 0; +} + +/* + * gg_dcc_transfer() // funkcja wewnêtrzna + * + * inicjuje proces wymiany pliku z danym klientem. + * + * - ip - adres ip odbiorcy + * - port - port odbiorcy + * - my_uin - w³asny numer + * - peer_uin - numer obiorcy + * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET) + * + * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. + */ +static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) +{ + struct gg_dcc *d = NULL; + struct in_addr addr; + + addr.s_addr = ip; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); + + if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); + errno = EINVAL; + return NULL; + } + + if (!(d = (void*) calloc(1, sizeof(*d)))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n"); + return NULL; + } + + d->check = GG_CHECK_WRITE; + d->state = GG_STATE_CONNECTING; + d->type = type; + d->timeout = GG_DEFAULT_TIMEOUT; + d->file_fd = -1; + d->active = 1; + d->fd = -1; + d->uin = my_uin; + d->peer_uin = peer_uin; + + if ((d->fd = gg_connect(&addr, port, 1)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n"); + free(d); + return NULL; + } + + return d; +} + +/* + * gg_dcc_get_file() + * + * inicjuje proces odbierania pliku od danego klienta, gdy ten wys³a³ do + * nas ¿±danie po³±czenia. + * + * - ip - adres ip odbiorcy + * - port - port odbiorcy + * - my_uin - w³asny numer + * - peer_uin - numer obiorcy + * + * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. + */ +struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) +{ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n"); + + return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); +} + +/* + * gg_dcc_send_file() + * + * inicjuje proces wysy³ania pliku do danego klienta. + * + * - ip - adres ip odbiorcy + * - port - port odbiorcy + * - my_uin - w³asny numer + * - peer_uin - numer obiorcy + * + * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. + */ +struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) +{ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n"); + + return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); +} + +/* + * gg_dcc_voice_chat() + * + * próbuje nawi±zaæ po³±czenie g³osowe. + * + * - ip - adres ip odbiorcy + * - port - port odbiorcy + * - my_uin - w³asny numer + * - peer_uin - numer obiorcy + * + * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d. + */ +struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) +{ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n"); + + return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); +} + +/* + * gg_dcc_set_type() + * + * po zdarzeniu GG_EVENT_DCC_CALLBACK nale¿y ustawiæ typ po³±czenia za + * pomoc± tej funkcji. + * + * - d - struktura opisuj±ca po³±czenie + * - type - typ po³±czenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE) + */ +void gg_dcc_set_type(struct gg_dcc *d, int type) +{ + d->type = type; + d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST; +} + +/* + * gg_dcc_callback() // funkcja wewnêtrzna + * + * wywo³ywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza + * rezultat w struct gg_dcc->event. + * + * - d - structura opisuj±ca po³±czenie + * + * 0, -1. + */ +static int gg_dcc_callback(struct gg_dcc *d) +{ + struct gg_event *e = gg_dcc_watch_fd(d); + + d->event = e; + + return (e != NULL) ? 0 : -1; +} + +/* + * gg_dcc_socket_create() + * + * tworzy gniazdo dla bezpo¶redniej komunikacji miêdzy klientami. + * + * - uin - w³asny numer + * - port - preferowany port, je¶li równy 0 lub -1, próbuje domy¶lnego + * + * zaalokowana struct gg_dcc, któr± po¼niej nale¿y zwolniæ funkcj± + * gg_dcc_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) +{ + struct gg_dcc *c; + struct sockaddr_in sin; + int sock, bound = 0, errno2; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); + + if (!uin) { + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); + errno = EINVAL; + return NULL; + } + + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno)); + return NULL; + } + + if (!port) + port = GG_DEFAULT_DCC_PORT; + + while (!bound) { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); + if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) + bound = 1; + else { + if (++port == 65535) { + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n"); + close(sock); + return NULL; + } + } + } + + if (listen(sock, 10)) { + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); + + if (!(c = malloc(sizeof(*c)))) { + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n"); + close(sock); + return NULL; + } + memset(c, 0, sizeof(*c)); + + c->port = c->id = port; + c->fd = sock; + c->type = GG_SESSION_DCC_SOCKET; + c->uin = uin; + c->timeout = -1; + c->state = GG_STATE_LISTENING; + c->check = GG_CHECK_READ; + c->callback = gg_dcc_callback; + c->destroy = gg_dcc_free; + + return c; +} + +/* + * gg_dcc_voice_send() + * + * wysy³a ramkê danych dla rozmowy g³osowej. + * + * - d - struktura opisuj±ca po³±czenie dcc + * - buf - bufor z danymi + * - length - rozmiar ramki + * + * 0, -1. + */ +int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) +{ + struct packet_s { + uint8_t type; + uint32_t length; + } GG_PACKED; + struct packet_s packet; + + gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length); + if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n"); + errno = EINVAL; + return -1; + } + + packet.type = 0x03; /* XXX */ + packet.length = gg_fix32(length); + + if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); + return -1; + } + gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet)); + + if (write(d->fd, buf, length) < length) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n"); + return -1; + } + gg_dcc_debug_data("write", d->fd, buf, length); + + return 0; +} + +#define gg_read(fd, buf, size) \ +{ \ + int tmp = read(fd, buf, size); \ + \ + if (tmp < (int) size) { \ + if (tmp == -1) { \ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \ + } else if (tmp == 0) { \ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \ + } else { \ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \ + } \ + e->type = GG_EVENT_DCC_ERROR; \ + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ + return e; \ + } \ + gg_dcc_debug_data("read", fd, buf, size); \ +} + +#define gg_write(fd, buf, size) \ +{ \ + int tmp; \ + gg_dcc_debug_data("write", fd, buf, size); \ + tmp = write(fd, buf, size); \ + if (tmp < (int) size) { \ + if (tmp == -1) { \ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \ + } else { \ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \ + } \ + e->type = GG_EVENT_DCC_ERROR; \ + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \ + return e; \ + } \ +} + +/* + * gg_dcc_watch_fd() + * + * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê zmieni na gg_dcc->fd. + * + * - h - struktura zwrócona przez gg_create_dcc_socket() + * + * zaalokowana struct gg_event lub NULL, je¶li zabrak³o pamiêci na ni±. + */ +struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) +{ + struct gg_event *e; + int foo; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); + + if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); + errno = EINVAL; + return NULL; + } + + if (!(e = (void*) calloc(1, sizeof(*e)))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n"); + return NULL; + } + + e->type = GG_EVENT_NONE; + + if (h->type == GG_SESSION_DCC_SOCKET) { + struct sockaddr_in sin; + struct gg_dcc *c; + int fd, sin_len = sizeof(sin), one = 1; + + if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); + return e; + } + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); + +#ifdef FIONBIO + if (ioctl(fd, FIONBIO, &one) == -1) { +#else + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno)); + close(fd); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + return e; + } + + if (!(c = (void*) calloc(1, sizeof(*c)))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n"); + + free(e); + close(fd); + return NULL; + } + + c->fd = fd; + c->check = GG_CHECK_READ; + c->state = GG_STATE_READING_UIN_1; + c->type = GG_SESSION_DCC; + c->timeout = GG_DEFAULT_TIMEOUT; + c->file_fd = -1; + c->remote_addr = sin.sin_addr.s_addr; + c->remote_port = ntohs(sin.sin_port); + + e->type = GG_EVENT_DCC_NEW; + e->event.dcc_new = c; + + return e; + } else { + struct gg_dcc_tiny_packet tiny; + struct gg_dcc_small_packet small; + struct gg_dcc_big_packet big; + int size, tmp, res, res_size = sizeof(res); + unsigned int utmp; + char buf[1024], ack[] = "UDAG"; + + struct gg_dcc_file_info_packet { + struct gg_dcc_big_packet big; + struct gg_file_info file_info; + } GG_PACKED; + struct gg_dcc_file_info_packet file_info_packet; + + switch (h->state) { + case GG_STATE_READING_UIN_1: + case GG_STATE_READING_UIN_2: + { + uin_t uin; + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); + + gg_read(h->fd, &uin, sizeof(uin)); + + if (h->state == GG_STATE_READING_UIN_1) { + h->state = GG_STATE_READING_UIN_2; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + h->peer_uin = gg_fix32(uin); + } else { + h->state = GG_STATE_SENDING_ACK; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + h->uin = gg_fix32(uin); + e->type = GG_EVENT_DCC_CLIENT_ACCEPT; + } + + return e; + } + + case GG_STATE_SENDING_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); + + gg_write(h->fd, ack, 4); + + h->state = GG_STATE_READING_TYPE; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + + case GG_STATE_READING_TYPE: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); + + gg_read(h->fd, &small, sizeof(small)); + + small.type = gg_fix32(small.type); + + switch (small.type) { + case 0x0003: /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n"); + h->type = GG_SESSION_DCC_SEND; + h->state = GG_STATE_SENDING_FILE_INFO; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + + e->type = GG_EVENT_DCC_CALLBACK; + + break; + + case 0x0002: /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n"); + h->type = GG_SESSION_DCC_GET; + h->state = GG_STATE_READING_REQUEST; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + h->incoming = 1; + + break; + + default: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + } + + return e; + + case GG_STATE_READING_REQUEST: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); + + gg_read(h->fd, &small, sizeof(small)); + + small.type = gg_fix32(small.type); + + switch (small.type) { + case 0x0001: /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n"); + h->state = GG_STATE_READING_FILE_INFO; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + break; + + case 0x0003: /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); + h->state = GG_STATE_SENDING_VOICE_ACK; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DCC_TIMEOUT_VOICE_ACK; + h->type = GG_SESSION_DCC_VOICE; + e->type = GG_EVENT_DCC_NEED_VOICE_ACK; + + break; + + default: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + } + + return e; + + case GG_STATE_READING_FILE_INFO: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); + + gg_read(h->fd, &file_info_packet, sizeof(file_info_packet)); + + memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); + + h->file_info.mode = gg_fix32(h->file_info.mode); + h->file_info.size = gg_fix32(h->file_info.size); + + h->state = GG_STATE_SENDING_FILE_ACK; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DCC_TIMEOUT_FILE_ACK; + + e->type = GG_EVENT_DCC_NEED_FILE_ACK; + + return e; + + case GG_STATE_SENDING_FILE_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); + + big.type = gg_fix32(0x0006); /* XXX */ + big.dunno1 = gg_fix32(h->offset); + big.dunno2 = 0; + + gg_write(h->fd, &big, sizeof(big)); + + h->state = GG_STATE_READING_FILE_HEADER; + h->chunk_size = sizeof(big); + h->chunk_offset = 0; + if (!(h->chunk_buf = malloc(sizeof(big)))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); + free(e); + return NULL; + } + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + + case GG_STATE_SENDING_VOICE_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); + + tiny.type = 0x01; /* XXX */ + + gg_write(h->fd, &tiny, sizeof(tiny)); + + h->state = GG_STATE_READING_VOICE_HEADER; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + h->offset = 0; + + return e; + + case GG_STATE_READING_FILE_HEADER: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); + + tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); + + if (tmp == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + return e; + } + + gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); + + h->chunk_offset += tmp; + + if (h->chunk_offset < h->chunk_size) + return e; + + memcpy(&big, h->chunk_buf, sizeof(big)); + free(h->chunk_buf); + h->chunk_buf = NULL; + + big.type = gg_fix32(big.type); + h->chunk_size = gg_fix32(big.dunno1); + h->chunk_offset = 0; + + if (big.type == 0x0005) { /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n"); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_REFUSED; + return e; + } + + if (h->chunk_size == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); + e->type = GG_EVENT_DCC_DONE; + return e; + } + + h->state = GG_STATE_GETTING_FILE; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + h->established = 1; + + return e; + + case GG_STATE_READING_VOICE_HEADER: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); + + gg_read(h->fd, &tiny, sizeof(tiny)); + + switch (tiny.type) { + case 0x03: /* XXX */ + h->state = GG_STATE_READING_VOICE_SIZE; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + h->established = 1; + break; + case 0x04: /* XXX */ + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); + /* XXX zwracaæ odpowiedni event */ + default: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + } + + return e; + + case GG_STATE_READING_VOICE_SIZE: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); + + gg_read(h->fd, &small, sizeof(small)); + + small.type = gg_fix32(small.type); + + if (small.type < 16 || small.type > sizeof(buf)) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + + return e; + } + + h->chunk_size = small.type; + h->chunk_offset = 0; + + if (!(h->voice_buf = malloc(h->chunk_size))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); + free(e); + return NULL; + } + + h->state = GG_STATE_READING_VOICE_DATA; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + + case GG_STATE_READING_VOICE_DATA: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); + + tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); + if (tmp < 1) { + if (tmp == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); + } else { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); + } + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + return e; + } + + gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp); + + h->chunk_offset += tmp; + + if (h->chunk_offset >= h->chunk_size) { + e->type = GG_EVENT_DCC_VOICE_DATA; + e->event.dcc_voice_data.data = h->voice_buf; + e->event.dcc_voice_data.length = h->chunk_size; + h->state = GG_STATE_READING_VOICE_HEADER; + h->voice_buf = NULL; + } + + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + + case GG_STATE_CONNECTING: + { + uin_t uins[2]; + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); + + res = 0; + if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + return e; + } + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); + + uins[0] = gg_fix32(h->uin); + uins[1] = gg_fix32(h->peer_uin); + + gg_write(h->fd, uins, sizeof(uins)); + + h->state = GG_STATE_READING_ACK; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + } + + case GG_STATE_READING_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); + + gg_read(h->fd, buf, 4); + + if (strncmp(buf, ack, 4)) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); + + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + return e; + } + + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + h->state = GG_STATE_SENDING_REQUEST; + + return e; + + case GG_STATE_SENDING_VOICE_REQUEST: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); + + small.type = gg_fix32(0x0003); + + gg_write(h->fd, &small, sizeof(small)); + + h->state = GG_STATE_READING_VOICE_ACK; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + return e; + + case GG_STATE_SENDING_REQUEST: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); + + small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ + + gg_write(h->fd, &small, sizeof(small)); + + switch (h->type) { + case GG_SESSION_DCC_GET: + h->state = GG_STATE_READING_REQUEST; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + break; + + case GG_SESSION_DCC_SEND: + h->state = GG_STATE_SENDING_FILE_INFO; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + + if (h->file_fd == -1) + e->type = GG_EVENT_DCC_NEED_FILE_INFO; + break; + + case GG_SESSION_DCC_VOICE: + h->state = GG_STATE_SENDING_VOICE_REQUEST; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + break; + } + + return e; + + case GG_STATE_SENDING_FILE_INFO: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); + + if (h->file_fd == -1) { + e->type = GG_EVENT_DCC_NEED_FILE_INFO; + return e; + } + + small.type = gg_fix32(0x0001); /* XXX */ + + gg_write(h->fd, &small, sizeof(small)); + + file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ + file_info_packet.big.dunno1 = 0; + file_info_packet.big.dunno2 = 0; + + memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); + + /* zostaj± teraz u nas, wiêc odwracamy z powrotem */ + h->file_info.size = gg_fix32(h->file_info.size); + h->file_info.mode = gg_fix32(h->file_info.mode); + + gg_write(h->fd, &file_info_packet, sizeof(file_info_packet)); + + h->state = GG_STATE_READING_FILE_ACK; + h->check = GG_CHECK_READ; + h->timeout = GG_DCC_TIMEOUT_FILE_ACK; + + return e; + + case GG_STATE_READING_FILE_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); + + gg_read(h->fd, &big, sizeof(big)); + + /* XXX sprawdzaæ wynik */ + h->offset = gg_fix32(big.dunno1); + + h->state = GG_STATE_SENDING_FILE_HEADER; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + + e->type = GG_EVENT_DCC_ACK; + + return e; + + case GG_STATE_READING_VOICE_ACK: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); + + gg_read(h->fd, &tiny, sizeof(tiny)); + + if (tiny.type != 0x01) { + gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_REFUSED; + return e; + } + + h->state = GG_STATE_READING_VOICE_HEADER; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + + e->type = GG_EVENT_DCC_ACK; + + return e; + + case GG_STATE_SENDING_FILE_HEADER: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); + + h->chunk_offset = 0; + + if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { + h->chunk_size = 4096; + big.type = gg_fix32(0x0003); /* XXX */ + } else + big.type = gg_fix32(0x0002); /* XXX */ + + big.dunno1 = gg_fix32(h->chunk_size); + big.dunno2 = 0; + + gg_write(h->fd, &big, sizeof(big)); + + h->state = GG_STATE_SENDING_FILE; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + h->established = 1; + + return e; + + case GG_STATE_SENDING_FILE: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); + + if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) + utmp = sizeof(buf); + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); + + /* koniec pliku? */ + if (h->file_info.size == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n"); + e->type = GG_EVENT_DCC_DONE; + + return e; + } + + lseek(h->file_fd, h->offset, SEEK_SET); + + size = read(h->file_fd, buf, utmp); + + /* b³±d */ + if (size == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); + + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_FILE; + + return e; + } + + /* koniec pliku? */ + if (size == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_EOF; + + return e; + } + + /* je¶li wczytali¶my wiêcej, utnijmy. */ + if (h->offset + size > h->file_info.size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); + size = h->file_info.size - h->offset; + + if (size < 1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n"); + e->type = GG_EVENT_DCC_DONE; + return e; + } + } + + tmp = write(h->fd, buf, size); + + if (tmp == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno)); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + return e; + } + + h->offset += size; + + if (h->offset >= h->file_info.size) { + e->type = GG_EVENT_DCC_DONE; + return e; + } + + h->chunk_offset += size; + + if (h->chunk_offset >= h->chunk_size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); + h->state = GG_STATE_SENDING_FILE_HEADER; + h->timeout = GG_DEFAULT_TIMEOUT; + } else { + h->state = GG_STATE_SENDING_FILE; + h->timeout = GG_DCC_TIMEOUT_SEND; + } + + h->check = GG_CHECK_WRITE; + + return e; + + case GG_STATE_GETTING_FILE: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); + + if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) + utmp = sizeof(buf); + + size = read(h->fd, buf, utmp); + + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size); + + /* b³±d */ + if (size == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); + + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + + return e; + } + + /* koniec? */ + if (size == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n"); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_EOF; + + return e; + } + + tmp = write(h->file_fd, buf, size); + + if (tmp == -1 || tmp < size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + return e; + } + + h->offset += size; + + if (h->offset >= h->file_info.size) { + e->type = GG_EVENT_DCC_DONE; + return e; + } + + h->chunk_offset += size; + + if (h->chunk_offset >= h->chunk_size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); + h->state = GG_STATE_READING_FILE_HEADER; + h->timeout = GG_DEFAULT_TIMEOUT; + h->chunk_offset = 0; + h->chunk_size = sizeof(big); + if (!(h->chunk_buf = malloc(sizeof(big)))) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n"); + free(e); + return NULL; + } + } else { + h->state = GG_STATE_GETTING_FILE; + h->timeout = GG_DCC_TIMEOUT_GET; + } + + h->check = GG_CHECK_READ; + + return e; + + default: + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; + + return e; + } + } + + return e; +} + +#undef gg_read +#undef gg_write + +/* + * gg_dcc_free() + * + * zwalnia pamiêæ po strukturze po³±czenia dcc. + * + * - d - zwalniana struktura + */ +void gg_dcc_free(struct gg_dcc *d) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); + + if (!d) + return; + + if (d->fd != -1) + close(d->fd); + + if (d->chunk_buf) { + free(d->chunk_buf); + d->chunk_buf = NULL; + } + + free(d); +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/events.c b/kopete/protocols/gadu/libgadu/events.c new file mode 100644 index 00000000..97b84912 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/events.c @@ -0,0 +1,1580 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo¼ny <speedy@ziew.org> + * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "libgadu-config.h" + +#include <errno.h> +#ifdef __GG_LIBGADU_HAVE_PTHREAD +# include <pthread.h> +#endif +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#ifdef __GG_LIBGADU_HAVE_OPENSSL +# include <openssl/err.h> +# include <openssl/x509.h> +#endif + +#include "compat.h" +#include "libgadu.h" + +/* + * gg_event_free() + * + * zwalnia pamiêæ zajmowan± przez informacjê o zdarzeniu. + * + * - e - wska¼nik do informacji o zdarzeniu + */ +void gg_event_free(struct gg_event *e) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); + + if (!e) + return; + + switch (e->type) { + case GG_EVENT_MSG: + free(e->event.msg.message); + free(e->event.msg.formats); + free(e->event.msg.recipients); + break; + + case GG_EVENT_NOTIFY: + free(e->event.notify); + break; + + case GG_EVENT_NOTIFY60: + { + int i; + + for (i = 0; e->event.notify60[i].uin; i++) + free(e->event.notify60[i].descr); + + free(e->event.notify60); + + break; + } + + case GG_EVENT_STATUS60: + free(e->event.status60.descr); + break; + + case GG_EVENT_STATUS: + free(e->event.status.descr); + break; + + case GG_EVENT_NOTIFY_DESCR: + free(e->event.notify_descr.notify); + free(e->event.notify_descr.descr); + break; + + case GG_EVENT_DCC_VOICE_DATA: + free(e->event.dcc_voice_data.data); + break; + + case GG_EVENT_PUBDIR50_SEARCH_REPLY: + case GG_EVENT_PUBDIR50_READ: + case GG_EVENT_PUBDIR50_WRITE: + gg_pubdir50_free(e->event.pubdir50); + break; + + case GG_EVENT_USERLIST: + free(e->event.userlist.reply); + break; + + case GG_EVENT_IMAGE_REPLY: + free(e->event.image_reply.filename); + free(e->event.image_reply.image); + break; + } + + free(e); +} + +/* + * gg_image_queue_remove() + * + * usuwa z kolejki dany wpis. + * + * - s - sesja + * - q - kolejka + * - freeq - czy zwolniæ kolejkê + * + * 0/-1 + */ +int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) +{ + if (!s || !q) { + errno = EFAULT; + return -1; + } + + if (s->images == q) + s->images = q->next; + else { + struct gg_image_queue *qq; + + for (qq = s->images; qq; qq = qq->next) { + if (qq->next == q) { + qq->next = q->next; + break; + } + } + } + + if (freeq) { + free(q->image); + free(q->filename); + free(q); + } + + return 0; +} + +/* + * gg_image_queue_parse() // funkcja wewnêtrzna + * + * parsuje przychodz±cy pakiet z obrazkiem. + * + * - e - opis zdarzenia + * - + */ +static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) +{ + struct gg_msg_image_reply *i = (void*) p; + struct gg_image_queue *q, *qq; + + if (!p || !sess || !e) { + errno = EFAULT; + return; + } + + /* znajd¼ dany obrazek w kolejce danej sesji */ + + for (qq = sess->images, q = NULL; qq; qq = qq->next) { + if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { + q = qq; + break; + } + } + + if (!q) { + gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); + return; + } + + if (p[0] == 0x05) { + int i, ok = 0; + + q->done = 0; + + len -= sizeof(struct gg_msg_image_reply); + p += sizeof(struct gg_msg_image_reply); + + /* sprawd¼, czy mamy tekst zakoñczony \0 */ + + for (i = 0; i < len; i++) { + if (!p[i]) { + ok = 1; + break; + } + } + + if (!ok) { + gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); + return; + } + + if (!(q->filename = strdup(p))) { + gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); + return; + } + + len -= strlen(p) + 1; + p += strlen(p) + 1; + } else { + len -= sizeof(struct gg_msg_image_reply); + p += sizeof(struct gg_msg_image_reply); + } + + if (q->done + len > q->size) + len = q->size - q->done; + + memcpy(q->image + q->done, p, len); + q->done += len; + + /* je¶li skoñczono odbieraæ obrazek, wygeneruj zdarzenie */ + + if (q->done >= q->size) { + e->type = GG_EVENT_IMAGE_REPLY; + e->event.image_reply.sender = sender; + e->event.image_reply.size = q->size; + e->event.image_reply.crc32 = q->crc32; + e->event.image_reply.filename = q->filename; + e->event.image_reply.image = q->image; + + gg_image_queue_remove(sess, q, 0); + + free(q); + } +} + +/* + * gg_handle_recv_msg() // funkcja wewnêtrzna + * + * obs³uguje pakiet z przychodz±c± wiadomo¶ci±, rozbijaj±c go na dodatkowe + * struktury (konferencje, kolorki) w razie potrzeby. + * + * - h - nag³ówek pakietu + * - e - opis zdarzenia + * + * 0, -1. + */ +static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); + char *p, *packet_end = (char*) r + h->length; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); + e->type = GG_EVENT_NONE; + return 0; + } + + for (p = (char*) r + sizeof(*r); *p; p++) { + if (*p == 0x02 && p == packet_end - 1) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); + break; + } + if (p >= packet_end) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + } + + p++; + + /* przeanalizuj dodatkowe opcje */ + while (p < packet_end) { + switch (*p) { + case 0x01: /* konferencja */ + { + struct gg_msg_recipients *m = (void*) p; + uint32_t i, count; + + p += sizeof(*m); + + if (p > packet_end) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); + goto malformed; + } + + count = gg_fix32(m->count); + + if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); + goto malformed; + } + + if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); + goto fail; + } + + for (i = 0; i < count; i++, p += sizeof(uint32_t)) { + uint32_t u; + memcpy(&u, p, sizeof(uint32_t)); + e->event.msg.recipients[i] = gg_fix32(u); + } + + e->event.msg.recipients_count = count; + + break; + } + + case 0x02: /* richtext */ + { + uint16_t len; + char *buf; + + if (p + 3 > packet_end) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); + goto malformed; + } + + memcpy(&len, p + 1, sizeof(uint16_t)); + len = gg_fix16(len); + + if (!(buf = malloc(len))) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); + goto fail; + } + + p += 3; + + if (p + len > packet_end) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + free(buf); + goto malformed; + } + + memcpy(buf, p, len); + + e->event.msg.formats = buf; + e->event.msg.formats_length = len; + + p += len; + + break; + } + + case 0x04: /* image_request */ + { + struct gg_msg_image_request *i = (void*) p; + + if (p + sizeof(*i) > packet_end) { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + goto malformed; + } + + e->event.image_request.sender = gg_fix32(r->sender); + e->event.image_request.size = gg_fix32(i->size); + e->event.image_request.crc32 = gg_fix32(i->crc32); + + e->type = GG_EVENT_IMAGE_REQUEST; + + return 0; + } + + case 0x05: /* image_reply */ + case 0x06: + { + struct gg_msg_image_reply *rep = (void*) p; + + if (p + sizeof(struct gg_msg_image_reply) == packet_end) { + + /* pusta odpowied¼ - klient po drugiej stronie nie ma ¿±danego obrazka */ + + e->type = GG_EVENT_IMAGE_REPLY; + e->event.image_reply.sender = gg_fix32(r->sender); + e->event.image_reply.size = 0; + e->event.image_reply.crc32 = gg_fix32(rep->crc32); + e->event.image_reply.filename = NULL; + e->event.image_reply.image = NULL; + return 0; + + } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { + + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); + goto malformed; + } + + rep->size = gg_fix32(rep->size); + rep->crc32 = gg_fix32(rep->crc32); + gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); + + return 0; + } + + default: + { + gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); + p = packet_end; + } + } + } + + e->type = GG_EVENT_MSG; + e->event.msg.msgclass = gg_fix32(r->msgclass); + e->event.msg.sender = gg_fix32(r->sender); + e->event.msg.time = gg_fix32(r->time); + e->event.msg.message = strdup((char*) r + sizeof(*r)); + + return 0; + +malformed: + e->type = GG_EVENT_NONE; + + free(e->event.msg.recipients); + free(e->event.msg.formats); + + return 0; + +fail: + free(e->event.msg.recipients); + free(e->event.msg.formats); + return -1; +} + +/* + * gg_watch_fd_connected() // funkcja wewnêtrzna + * + * patrzy na gniazdo, odbiera pakiet i wype³nia strukturê zdarzenia. + * + * - sess - struktura opisuj±ca sesjê + * - e - opis zdarzenia + * + * 0, -1. + */ +static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) +{ + struct gg_header *h = NULL; + char *p; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (!(h = gg_recv_packet(sess))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + + p = (char*) h + sizeof(struct gg_header); + + switch (h->type) { + case GG_RECV_MSG: + { + if (h->length >= sizeof(struct gg_recv_msg)) + if (gg_handle_recv_msg(h, e, sess)) + goto fail; + + break; + } + + case GG_NOTIFY_REPLY: + { + struct gg_notify_reply *n = (void*) p; + unsigned int count, i; + char *tmp; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + if (h->length < sizeof(*n)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); + errno = EINVAL; + goto fail; + } + + if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { + e->type = GG_EVENT_NOTIFY_DESCR; + + if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + e->event.notify_descr.notify[1].uin = 0; + memcpy(e->event.notify_descr.notify, p, sizeof(*n)); + e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); + e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); + e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); + + count = h->length - sizeof(*n); + if (!(tmp = malloc(count + 1))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + memcpy(tmp, p + sizeof(*n), count); + tmp[count] = 0; + e->event.notify_descr.descr = tmp; + + } else { + e->type = GG_EVENT_NOTIFY; + + if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(e->event.notify, p, h->length); + count = h->length / sizeof(*n); + e->event.notify[count].uin = 0; + + for (i = 0; i < count; i++) { + e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); + e->event.notify[i].status = gg_fix32(e->event.notify[i].status); + e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); + } + } + + break; + } + + case GG_STATUS: + { + struct gg_status *s = (void*) p; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length >= sizeof(*s)) { + e->type = GG_EVENT_STATUS; + memcpy(&e->event.status, p, sizeof(*s)); + e->event.status.uin = gg_fix32(e->event.status.uin); + e->event.status.status = gg_fix32(e->event.status.status); + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + if (buf) { + memcpy(buf, p + sizeof(*s), len); + buf[len] = 0; + } + e->event.status.descr = buf; + } else + e->event.status.descr = NULL; + } + + break; + } + + case GG_NOTIFY_REPLY60: + { + struct gg_notify_reply60 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + + if (!e->event.notify60) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + + while (length >= sizeof(struct gg_notify_reply60)) { + uin_t uin = gg_fix32(n->uin); + char *tmp; + + e->event.notify60[i].uin = uin & 0x00ffffff; + e->event.notify60[i].status = n->status; + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port = gg_fix16(n->remote_port); + e->event.notify60[i].version = n->version; + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].time = 0; + + if (uin & 0x40000000) + e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; + if (uin & 0x08000000) + e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + + if (GG_S_D(n->status)) { + unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); + + if (descr_len < length) { + if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); + e->event.notify60[i].descr[descr_len] = 0; + + /* XXX czas */ + } + + length -= sizeof(struct gg_notify_reply60) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { + length -= sizeof(struct gg_notify_reply60); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + + break; + } + + case GG_STATUS60: + { + struct gg_status60 *s = (void*) p; + uint32_t uin; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + uin = gg_fix32(s->uin); + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = uin & 0x00ffffff; + e->event.status60.status = s->status; + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.version = s->version; + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.time = 0; + + if (uin & 0x40000000) + e->event.status60.version |= GG_HAS_AUDIO_MASK; + if (uin & 0x08000000) + e->event.status60.version |= GG_ERA_OMNIX_MASK; + + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), len); + buf[len] = 0; + } + + e->event.status60.descr = buf; + + if (len > 4 && p[h->length - 5] == 0) { + uint32_t t; + memcpy(&t, p + h->length - 4, sizeof(uint32_t)); + e->event.status60.time = gg_fix32(t); + } + } + + break; + } + + case GG_SEND_MSG_ACK: + { + struct gg_send_msg_ack *s = (void*) p; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); + + if (h->length < sizeof(*s)) + break; + + e->type = GG_EVENT_ACK; + e->event.ack.status = gg_fix32(s->status); + e->event.ack.recipient = gg_fix32(s->recipient); + e->event.ack.seq = gg_fix32(s->seq); + + break; + } + + case GG_PONG: + { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); + + e->type = GG_EVENT_PONG; + sess->last_pong = time(NULL); + + break; + } + + case GG_DISCONNECTING: + { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); + e->type = GG_EVENT_DISCONNECT; + break; + } + + case GG_PUBDIR50_REPLY: + { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); + if (gg_pubdir50_handle_reply(e, p, h->length) == -1) + goto fail; + break; + } + + case GG_USERLIST_REPLY: + { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); + + if (h->length < 1) + break; + + /* je¶li odpowied¼ na eksport, wywo³aj zdarzenie tylko + * gdy otrzymano wszystkie odpowiedzi */ + if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { + if (--sess->userlist_blocks) + break; + + p[0] = GG_USERLIST_PUT_REPLY; + } + + if (h->length > 1) { + char *tmp; + unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; + + gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); + + if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); + free(sess->userlist_reply); + sess->userlist_reply = NULL; + goto fail; + } + + sess->userlist_reply = tmp; + sess->userlist_reply[len + h->length - 1] = 0; + memcpy(sess->userlist_reply + len, p + 1, h->length - 1); + } + + if (p[0] == GG_USERLIST_GET_MORE_REPLY) + break; + + e->type = GG_EVENT_USERLIST; + e->event.userlist.type = p[0]; + e->event.userlist.reply = sess->userlist_reply; + sess->userlist_reply = NULL; + + break; + } + + default: + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); + } + + free(h); + return 0; + +fail: + free(h); + return -1; +} + +/* + * gg_watch_fd() + * + * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê stanie z obserwowanym + * deskryptorem. zwraca klientowi informacjê o tym, co siê dzieje. + * + * - sess - opis sesji + * + * wska¼nik do struktury gg_event, któr± trzeba zwolniæ pó¼niej + * za pomoc± gg_event_free(). jesli rodzaj zdarzenia jest równy + * GG_EVENT_NONE, nale¿y je zignorowaæ. je¶li zwróci³o NULL, + * sta³o siê co¶ niedobrego -- albo zabrak³o pamiêci albo zerwa³o + * po³±czenie. + */ +struct gg_event *gg_watch_fd(struct gg_session *sess) +{ + struct gg_event *e; + int res = 0; + int port = 0; + int errno2 = 0; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); + + if (!sess) { + errno = EFAULT; + return NULL; + } + + if (!(e = (void*) calloc(1, sizeof(*e)))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); + return NULL; + } + + e->type = GG_EVENT_NONE; + + switch (sess->state) { + case GG_STATE_RESOLVING: + { + struct in_addr addr; + int failed = 0; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); + + if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); + failed = 1; + errno2 = errno; + } + + close(sess->fd); + sess->fd = -1; + +#ifndef __GG_LIBGADU_HAVE_PTHREAD + waitpid(sess->pid, NULL, 0); + sess->pid = -1; +#else + if (sess->resolver) { + pthread_cancel(*((pthread_t*) sess->resolver)); + free(sess->resolver); + sess->resolver = NULL; + } +#endif + + if (failed) { + errno = errno2; + goto fail_resolving; + } + + /* je¶li jeste¶my w resolverze i mamy ustawiony port + * proxy, znaczy, ¿e resolvowali¶my proxy. zatem + * wpiszmy jego adres. */ + if (sess->proxy_port) + sess->proxy_addr = addr.s_addr; + + /* zapiszmy sobie adres huba i adres serwera (do + * bezpo¶redniego po³±czenia, je¶li hub le¿y) + * z resolvera. */ + if (sess->proxy_addr && sess->proxy_port) + port = sess->proxy_port; + else { + sess->server_addr = sess->hub_addr = addr.s_addr; + port = GG_APPMSG_PORT; + } + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); + + /* ³±czymy siê albo z hubem, albo z proxy, zale¿nie + * od tego, co resolvowali¶my. */ + if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { + /* je¶li w trybie asynchronicznym gg_connect() + * zwróci b³±d, nie ma sensu próbowaæ dalej. */ + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + goto fail_connecting; + } + + /* je¶li podano serwer i ³±czmy siê przez proxy, + * jest to bezpo¶rednie po³±czenie, inaczej jest + * do huba. */ + sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } + + case GG_STATE_CONNECTING_HUB: + { + char buf[1024], *client, *auth; + int res = 0, res_size = sizeof(res); + const char *host, *appmsg; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); + + /* je¶li asynchroniczne, sprawdzamy, czy nie wyst±pi³ + * przypadkiem jaki¶ b³±d. */ + if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { + /* no tak, nie uda³o siê po³±czyæ z proxy. nawet + * nie próbujemy dalej. */ + if (sess->proxy_addr && sess->proxy_port) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + goto fail_connecting; + } + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); + close(sess->fd); + + if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { + /* przy asynchronicznych, gg_connect() + * zwraca -1 przy b³êdach socket(), + * ioctl(), braku routingu itd. dlatego + * nawet nie próbujemy dalej. */ + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + goto fail_connecting; + } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + break; + } + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); + + if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); + goto fail_connecting; + } + + if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) + host = "http://" GG_APPMSG_HOST; + else + host = ""; + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) + appmsg = "appmsg3.asp"; + else +#endif + appmsg = "appmsg2.asp"; + + auth = gg_proxy_auth(); + + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Pragma: no-cache\r\n" + "%s" + "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); + + if (auth) + free(auth); + + free(client); + + /* zwolnij pamiêæ po wersji klienta. */ + if (sess->client_version) { + free(sess->client_version); + sess->client_version = NULL; + } + + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); + + /* zapytanie jest krótkie, wiêc zawsze zmie¶ci siê + * do bufora gniazda. je¶li write() zwróci mniej, + * sta³o siê co¶ z³ego. */ + if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); + + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_WRITING; + sess->state = GG_STATE_IDLE; + close(sess->fd); + sess->fd = -1; + break; + } + + sess->state = GG_STATE_READING_DATA; + sess->check = GG_CHECK_READ; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } + + case GG_STATE_READING_DATA: + { + char buf[1024], *tmp, *host; + int port = GG_DEFAULT_PORT; + struct in_addr addr; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); + + /* czytamy liniê z gniazda i obcinamy \r\n. */ + gg_read_line(sess->fd, buf, sizeof(buf) - 1); + gg_chomp(buf); + gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); + + /* sprawdzamy, czy wszystko w porz±dku. */ + if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); + + close(sess->fd); + + /* je¶li otrzymali¶my jakie¶ dziwne informacje, + * próbujemy siê ³±czyæ z pominiêciem huba. */ + if (sess->proxy_addr && sess->proxy_port) { + if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { + /* trudno. nie wysz³o. */ + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail_connecting; + } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + break; + } + + sess->port = GG_DEFAULT_PORT; + + /* ³±czymy siê na port 8074 huba. */ + if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); + + sess->port = GG_HTTPS_PORT; + + /* ³±czymy siê na port 443. */ + if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail_connecting; + } + } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + break; + } + + /* ignorujemy resztê nag³ówka. */ + while (strcmp(buf, "\r\n") && strcmp(buf, "")) + gg_read_line(sess->fd, buf, sizeof(buf) - 1); + + /* czytamy pierwsz± liniê danych. */ + gg_read_line(sess->fd, buf, sizeof(buf) - 1); + gg_chomp(buf); + + /* je¶li pierwsza liczba w linii nie jest równa zeru, + * oznacza to, ¿e mamy wiadomo¶æ systemow±. */ + if (atoi(buf)) { + char tmp[1024], *foo, *sysmsg_buf = NULL; + int len = 0; + + while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { + if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); + break; + } + + sysmsg_buf = foo; + + if (!len) + strcpy(sysmsg_buf, tmp); + else + strcat(sysmsg_buf, tmp); + + len += strlen(tmp); + } + + e->type = GG_EVENT_MSG; + e->event.msg.msgclass = atoi(buf); + e->event.msg.sender = 0; + e->event.msg.message = sysmsg_buf; + } + + close(sess->fd); + + gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); + + /* analizujemy otrzymane dane. */ + tmp = buf; + + while (*tmp && *tmp != ' ') + tmp++; + while (*tmp && *tmp == ' ') + tmp++; + host = tmp; + while (*tmp && *tmp != ' ') + tmp++; + *tmp = 0; + + if ((tmp = strchr(host, ':'))) { + *tmp = 0; + port = atoi(tmp + 1); + } + + if (!strcmp(host, "notoperating")) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); + sess->fd = -1; + goto fail_unavailable; + } + + addr.s_addr = inet_addr(host); + sess->server_addr = addr.s_addr; + + if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { + /* je¶li mamy proxy, ³±czymy siê z nim. */ + if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { + /* nie wysz³o? trudno. */ + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail_connecting; + } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + break; + } + + sess->port = port; + + /* ³±czymy siê z w³a¶ciwym serwerem. */ + if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); + + sess->port = GG_HTTPS_PORT; + + /* nie wysz³o? próbujemy portu 443. */ + if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { + /* ostatnia deska ratunku zawiod³a? + * w takim razie zwijamy manatki. */ + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail_connecting; + } + } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } + + case GG_STATE_CONNECTING_GG: + { + int res = 0, res_size = sizeof(res); + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); + + /* je¶li wyst±pi³ b³±d podczas ³±czenia siê... */ + if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { + /* je¶li nie uda³o siê po³±czenie z proxy, + * nie mamy czego próbowaæ wiêcej. */ + if (sess->proxy_addr && sess->proxy_port) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + goto fail_connecting; + } + + close(sess->fd); + sess->fd = -1; + +#ifdef ETIMEDOUT + if (sess->timeout == 0) + errno = ETIMEDOUT; +#endif + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + /* je¶li logujemy siê po TLS, nie próbujemy + * siê ³±czyæ ju¿ z niczym innym w przypadku + * b³êdu. nie do¶æ, ¿e nie ma sensu, to i + * trzeba by siê bawiæ w tworzenie na nowo + * SSL i SSL_CTX. */ + + if (sess->ssl) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); + goto fail_connecting; + } +#endif + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + + sess->port = GG_HTTPS_PORT; + + /* próbujemy na port 443. */ + if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail_connecting; + } + } + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); + + if (gg_proxy_http_only) + sess->proxy_port = 0; + + /* je¶li mamy proxy, wy¶lijmy zapytanie. */ + if (sess->proxy_addr && sess->proxy_port) { + char buf[100], *auth = gg_proxy_auth(); + struct in_addr addr; + + if (sess->server_addr) + addr.s_addr = sess->server_addr; + else + addr.s_addr = sess->hub_addr; + + snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); + + /* wysy³amy zapytanie. jest ono na tyle krótkie, + * ¿e musi siê zmie¶ciæ w buforze gniazda. je¶li + * write() zawiedzie, sta³o siê co¶ z³ego. */ + if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + if (auth) + free(auth); + goto fail_connecting; + } + + if (auth) { + gg_debug(GG_DEBUG_MISC, "// %s", auth); + if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); + goto fail_connecting; + } + + free(auth); + } + + if (write(sess->fd, "\r\n", 2) < 2) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + goto fail_connecting; + } + } + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) { + SSL_set_fd(sess->ssl, sess->fd); + + sess->state = GG_STATE_TLS_NEGOTIATION; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } +#endif + + sess->state = GG_STATE_READING_KEY; + sess->check = GG_CHECK_READ; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + case GG_STATE_TLS_NEGOTIATION: + { + int res; + X509 *peer; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); + + if ((res = SSL_connect(sess->ssl)) <= 0) { + int err = SSL_get_error(sess->ssl, res); + + if (res == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); + + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_TLS; + sess->state = GG_STATE_IDLE; + close(sess->fd); + sess->fd = -1; + break; + } + + if (err == SSL_ERROR_WANT_READ) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); + + sess->state = GG_STATE_TLS_NEGOTIATION; + sess->check = GG_CHECK_READ; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } else if (err == SSL_ERROR_WANT_WRITE) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); + + sess->state = GG_STATE_TLS_NEGOTIATION; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } else { + char buf[1024]; + + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); + + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_TLS; + sess->state = GG_STATE_IDLE; + close(sess->fd); + sess->fd = -1; + break; + } + } + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); + + peer = SSL_get_peer_certificate(sess->ssl); + + if (!peer) + gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); + else { + char buf[1024]; + + X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); + gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); + + X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); + gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); + } + + sess->state = GG_STATE_READING_KEY; + sess->check = GG_CHECK_READ; + sess->timeout = GG_DEFAULT_TIMEOUT; + + break; + } +#endif + + case GG_STATE_READING_KEY: + { + struct gg_header *h; + struct gg_welcome *w; + struct gg_login60 l; + unsigned int hash; + unsigned char *password = sess->password; + int ret; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); + + memset(&l, 0, sizeof(l)); + l.dunno2 = 0xbe; + + /* XXX bardzo, bardzo, bardzo g³upi pomys³ na pozbycie + * siê tekstu wrzucanego przez proxy. */ + if (sess->proxy_addr && sess->proxy_port) { + char buf[100]; + + strcpy(buf, ""); + gg_read_line(sess->fd, buf, sizeof(buf) - 1); + gg_chomp(buf); + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); + + while (strcmp(buf, "")) { + gg_read_line(sess->fd, buf, sizeof(buf) - 1); + gg_chomp(buf); + if (strcmp(buf, "")) + gg_debug(GG_DEBUG_MISC, "// %s\n", buf); + } + + /* XXX niech czeka jeszcze raz w tej samej + * fazie. g³upio, ale dzia³a. */ + sess->proxy_port = 0; + + break; + } + + /* czytaj pierwszy pakiet. */ + if (!(h = gg_recv_packet(sess))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_READING; + sess->state = GG_STATE_IDLE; + errno2 = errno; + close(sess->fd); + errno = errno2; + sess->fd = -1; + break; + } + + if (h->type != GG_WELCOME) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); + free(h); + close(sess->fd); + sess->fd = -1; + errno = EINVAL; + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_INVALID; + sess->state = GG_STATE_IDLE; + break; + } + + w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); + w->key = gg_fix32(w->key); + + hash = gg_login_hash(password, w->key); + + gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); + + free(h); + + free(sess->password); + sess->password = NULL; + + { + struct in_addr dcc_ip; + dcc_ip.s_addr = gg_dcc_ip; + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); + } + + if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { + struct sockaddr_in sin; + int sin_len = sizeof(sin); + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); + + if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); + l.local_ip = sin.sin_addr.s_addr; + } else { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); + l.local_ip = 0; + } + } else + l.local_ip = gg_dcc_ip; + + l.uin = gg_fix32(sess->uin); + l.hash = gg_fix32(hash); + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version); + l.local_port = gg_fix16(gg_dcc_port); + l.image_size = sess->image_size; + + if (sess->external_addr && sess->external_port > 1023) { + l.external_ip = sess->external_addr; + l.external_port = gg_fix16(sess->external_port); + } + + gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); + + free(sess->initial_descr); + sess->initial_descr = NULL; + + if (ret == -1) { + gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sess->fd); + errno = errno2; + sess->fd = -1; + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_WRITING; + sess->state = GG_STATE_IDLE; + break; + } + + sess->state = GG_STATE_READING_REPLY; + + break; + } + + case GG_STATE_READING_REPLY: + { + struct gg_header *h; + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); + + if (!(h = gg_recv_packet(sess))) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_READING; + sess->state = GG_STATE_IDLE; + errno2 = errno; + close(sess->fd); + errno = errno2; + sess->fd = -1; + break; + } + + if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); + e->type = GG_EVENT_CONN_SUCCESS; + sess->state = GG_STATE_CONNECTED; + sess->timeout = -1; + sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; + free(h); + break; + } + + if (h->type == GG_LOGIN_FAILED) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); + e->event.failure = GG_FAILURE_PASSWORD; + errno = EACCES; + } else if (h->type == GG_DISCONNECTING) { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); + e->event.failure = GG_FAILURE_INTRUDER; + errno = EACCES; + } else { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); + e->event.failure = GG_FAILURE_INVALID; + errno = EINVAL; + } + + e->type = GG_EVENT_CONN_FAILED; + sess->state = GG_STATE_IDLE; + errno2 = errno; + close(sess->fd); + errno = errno2; + sess->fd = -1; + free(h); + + break; + } + + case GG_STATE_CONNECTED: + { + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); + + sess->last_event = time(NULL); + + if ((res = gg_watch_fd_connected(sess, e)) == -1) { + + gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); + + if (errno == EAGAIN) { + e->type = GG_EVENT_NONE; + res = 0; + } else + res = -1; + } + break; + } + } + +done: + if (res == -1) { + free(e); + e = NULL; + } + + return e; + +fail_connecting: + if (sess->fd != -1) { + errno2 = errno; + close(sess->fd); + errno = errno2; + sess->fd = -1; + } + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_CONNECTING; + sess->state = GG_STATE_IDLE; + goto done; + +fail_resolving: + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_RESOLVING; + sess->state = GG_STATE_IDLE; + goto done; + +fail_unavailable: + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_UNAVAILABLE; + sess->state = GG_STATE_IDLE; + goto done; +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/http.c b/kopete/protocols/gadu/libgadu/http.c new file mode 100644 index 00000000..77ebb319 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/http.c @@ -0,0 +1,522 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "libgadu-config.h" + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#ifdef __GG_LIBGADU_HAVE_PTHREAD +# include <pthread.h> +#endif +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "compat.h" +#include "libgadu.h" + +/* + * gg_http_connect() // funkcja pomocnicza + * + * rozpoczyna po³±czenie po http. + * + * - hostname - adres serwera + * - port - port serwera + * - async - asynchroniczne po³±czenie + * - method - metoda http (GET, POST, cokolwiek) + * - path - ¶cie¿ka do zasobu (musi byæ poprzedzona ,,/'') + * - header - nag³ówek zapytania plus ewentualne dane dla POST + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y + * zwolniæ funkcj± gg_http_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header) +{ + struct gg_http *h; + + if (!hostname || !port || !method || !path || !header) { + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n"); + errno = EFAULT; + return NULL; + } + + if (!(h = malloc(sizeof(*h)))) + return NULL; + memset(h, 0, sizeof(*h)); + + h->async = async; + h->port = port; + h->fd = -1; + h->type = GG_SESSION_HTTP; + + if (gg_proxy_enabled) { + char *auth = gg_proxy_auth(); + + h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s", + method, hostname, port, path, (auth) ? auth : + "", header); + hostname = gg_proxy_host; + h->port = port = gg_proxy_port; + + if (auth) + free(auth); + } else { + h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s", + method, path, header); + } + + if (!h->query) { + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n"); + free(h); + errno = ENOMEM; + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query); + + if (async) { +#ifndef __GG_LIBGADU_HAVE_PTHREAD + if (gg_resolve(&h->fd, &h->pid, hostname)) { +#else + if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n"); + gg_http_free(h); + errno = ENOENT; + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver); + + h->state = GG_STATE_RESOLVING; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + } else { + struct in_addr *hn, a; + + if (!(hn = gg_gethostbyname(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n"); + gg_http_free(h); + errno = ENOENT; + return NULL; + } else { + a.s_addr = hn->s_addr; + free(hn); + } + + if (!(h->fd = gg_connect(&a, port, 0)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_http_free(h); + return NULL; + } + + h->state = GG_STATE_CONNECTING; + + while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) { + if (gg_http_watch_fd(h) == -1) + break; + } + + if (h->state != GG_STATE_PARSING) { + gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n"); + gg_http_free(h); + return NULL; + } + } + + h->callback = gg_http_watch_fd; + h->destroy = gg_http_free; + + return h; +} + +#define gg_http_error(x) \ + close(h->fd); \ + h->fd = -1; \ + h->state = GG_STATE_ERROR; \ + h->error = x; \ + return 0; + +/* + * gg_http_watch_fd() + * + * przy asynchronicznej obs³udze HTTP funkcjê t± nale¿y wywo³aæ, je¶li + * zmieni³o siê co¶ na obserwowanym deskryptorze. + * + * - h - struktura opisuj±ca po³±czenie + * + * je¶li wszystko posz³o dobrze to 0, inaczej -1. po³±czenie bêdzie + * zakoñczone, je¶li h->state == GG_STATE_PARSING. je¶li wyst±pi jaki¶ + * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error. + */ +int gg_http_watch_fd(struct gg_http *h) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h); + + if (!h) { + gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (h->state == GG_STATE_RESOLVING) { + struct in_addr a; + + gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n"); + + if (read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) { + gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n"); + gg_http_error(GG_ERROR_RESOLVING); + } + + close(h->fd); + h->fd = -1; + +#ifndef __GG_LIBGADU_HAVE_PTHREAD + waitpid(h->pid, NULL, 0); +#else + if (h->resolver) { + pthread_cancel(*((pthread_t *) h->resolver)); + free(h->resolver); + h->resolver = NULL; + } +#endif + + gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port); + + if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_http_error(GG_ERROR_CONNECTING); + } + + h->state = GG_STATE_CONNECTING; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + + return 0; + } + + if (h->state == GG_STATE_CONNECTING) { + int res = 0; + unsigned int res_size = sizeof(res); + + if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { + gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno)); + close(h->fd); + h->fd = -1; + h->state = GG_STATE_ERROR; + h->error = GG_ERROR_CONNECTING; + if (res) + errno = res; + return 0; + } + + gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n"); + + h->state = GG_STATE_SENDING_QUERY; + } + + if (h->state == GG_STATE_SENDING_QUERY) { + int res; + + if ((res = write(h->fd, h->query, strlen(h->query))) < 1) { + gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno); + gg_http_error(GG_ERROR_WRITING); + } + + if (res < strlen(h->query)) { + gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res); + + memmove(h->query, h->query + res, strlen(h->query) - res + 1); + h->state = GG_STATE_SENDING_QUERY; + h->check = GG_CHECK_WRITE; + h->timeout = GG_DEFAULT_TIMEOUT; + } else { + gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query)); + free(h->query); + h->query = NULL; + + h->state = GG_STATE_READING_HEADER; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + } + + return 0; + } + + if (h->state == GG_STATE_READING_HEADER) { + char buf[1024], *tmp; + int res; + + if ((res = read(h->fd, buf, sizeof(buf))) == -1) { + gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno); + if (h->header) { + free(h->header); + h->header = NULL; + } + gg_http_error(GG_ERROR_READING); + } + + if (!res) { + gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n"); + if (h->header) { + free(h->header); + h->header = NULL; + } + gg_http_error(GG_ERROR_READING); + } + + gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res); + + if (!(tmp = realloc(h->header, h->header_size + res + 1))) { + gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n"); + free(h->header); + h->header = NULL; + gg_http_error(GG_ERROR_READING); + } + + h->header = tmp; + + memcpy(h->header + h->header_size, buf, res); + h->header_size += res; + + gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size); + + h->header[h->header_size] = 0; + + if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) { + int sep_len = (*tmp == '\r') ? 4 : 2; + unsigned int left; + char *line; + + left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len); + + gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left); + + /* HTTP/1.1 200 OK */ + if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) { + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); + + gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n"); + free(h->header); + h->header = NULL; + gg_http_error(GG_ERROR_CONNECTING); + } + + h->body_size = 0; + line = h->header; + *tmp = 0; + + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); + + while (line) { + if (!strncasecmp(line, "Content-length: ", 16)) { + h->body_size = atoi(line + 16); + } + line = strchr(line, '\n'); + if (line) + line++; + } + + if (h->body_size <= 0) { + gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n"); + h->body_size = left; + } + + if (left > h->body_size) { + gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left); + h->body_size = left; + } + + gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size); + + if (!(h->body = malloc(h->body_size + 1))) { + gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1); + free(h->header); + h->header = NULL; + gg_http_error(GG_ERROR_READING); + } + + if (left) { + memcpy(h->body, tmp + sep_len, left); + h->body_done = left; + } + + h->body[left] = 0; + + h->state = GG_STATE_READING_DATA; + h->check = GG_CHECK_READ; + h->timeout = GG_DEFAULT_TIMEOUT; + } + + return 0; + } + + if (h->state == GG_STATE_READING_DATA) { + char buf[1024]; + int res; + + if ((res = read(h->fd, buf, sizeof(buf))) == -1) { + gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno); + if (h->body) { + free(h->body); + h->body = NULL; + } + gg_http_error(GG_ERROR_READING); + } + + if (!res) { + if (h->body_done >= h->body_size) { + gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n"); + h->state = GG_STATE_PARSING; + close(h->fd); + h->fd = -1; + } else { + gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size); + if (h->body) { + free(h->body); + h->body = NULL; + } + gg_http_error(GG_ERROR_READING); + } + + return 0; + } + + gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res); + + if (h->body_done + res > h->body_size) { + char *tmp; + + gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size); + + if (!(tmp = realloc(h->body, h->body_done + res + 1))) { + gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1); + free(h->body); + h->body = NULL; + gg_http_error(GG_ERROR_READING); + } + + h->body = tmp; + h->body_size = h->body_done + res; + } + + h->body[h->body_done + res] = 0; + memcpy(h->body + h->body_done, buf, res); + h->body_done += res; + + gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size); + + return 0; + } + + if (h->fd != -1) + close(h->fd); + + h->fd = -1; + h->state = GG_STATE_ERROR; + h->error = 0; + + return -1; +} + +#undef gg_http_error + +/* + * gg_http_stop() + * + * je¶li po³±czenie jest w trakcie, przerywa je. nie zwalnia h->data. + * + * - h - struktura opisuj±ca po³±czenie + */ +void gg_http_stop(struct gg_http *h) +{ + if (!h) + return; + + if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE) + return; + + if (h->fd != -1) + close(h->fd); + h->fd = -1; +} + +/* + * gg_http_free_fields() // funkcja wewnêtrzna + * + * zwalnia pola struct gg_http, ale nie zwalnia samej struktury. + */ +void gg_http_free_fields(struct gg_http *h) +{ + if (!h) + return; + + if (h->body) { + free(h->body); + h->body = NULL; + } + + if (h->query) { + free(h->query); + h->query = NULL; + } + + if (h->header) { + free(h->header); + h->header = NULL; + } +} + +/* + * gg_http_free() + * + * próbuje zamkn±æ po³±czenie i zwalnia pamiêæ po nim. + * + * - h - struktura, któr± nale¿y zlikwidowaæ + */ +void gg_http_free(struct gg_http *h) +{ + if (!h) + return; + + gg_http_stop(h); + gg_http_free_fields(h); + free(h); +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/libgadu-config.h.in b/kopete/protocols/gadu/libgadu/libgadu-config.h.in new file mode 100644 index 00000000..dc4fb435 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/libgadu-config.h.in @@ -0,0 +1,30 @@ +/* Local libgadu configuration. */ + +#ifndef __GG_LIBGADU_CONFIG_H +#define __GG_LIBGADU_CONFIG_H + +/* Defined if libgadu was compiled for bigendian machine. */ +#undef __GG_LIBGADU_BIGENDIAN + +/* Defined if libgadu was compiled and linked with pthread support. */ +#define __GG_LIBGADU_HAVE_PTHREAD + +/* Defined if this machine has C99-compiliant vsnprintf(). */ +#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF + +/* Defined if this machine has va_copy(). */ +#undef __GG_LIBGADU_HAVE_VA_COPY + +/* Defined if this machine has __va_copy(). */ +#undef __GG_LIBGADU_HAVE___VA_COPY + +/* Defined if this machine supports long long. */ +#undef __GG_LIBGADU_HAVE_LONG_LONG + +/* Defined if libgadu was compiled and linked with TLS support. */ +#undef __GG_LIBGADU_HAVE_OPENSSL + +/* Include file containing uintXX_t declarations. */ +#include <inttypes.h> + +#endif /* __GG_LIBGADU_CONFIG_H */ diff --git a/kopete/protocols/gadu/libgadu/libgadu.c b/kopete/protocols/gadu/libgadu/libgadu.c new file mode 100644 index 00000000..47b687f0 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/libgadu.c @@ -0,0 +1,1818 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo¼ny <speedy@ziew.org> + * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> + * Tomasz Chiliñski <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifdef sun +# include <sys/filio.h> +#endif + +#include "libgadu-config.h" + +#include <errno.h> +#include <netdb.h> +#ifdef __GG_LIBGADU_HAVE_PTHREAD +# include <pthread.h> +#endif +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#ifdef __GG_LIBGADU_HAVE_OPENSSL +# include <openssl/err.h> +# include <openssl/rand.h> +#endif + +#include "compat.h" +#include "libgadu.h" + +int gg_debug_level = 0; +void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; + +int gg_dcc_port = 0; +unsigned long gg_dcc_ip = 0; + +unsigned long gg_local_ip = 0; +/* + * zmienne opisuj±ce parametry proxy http. + */ +char *gg_proxy_host = NULL; +int gg_proxy_port = 0; +int gg_proxy_enabled = 0; +int gg_proxy_http_only = 0; +char *gg_proxy_username = NULL; +char *gg_proxy_password = NULL; + +#ifndef lint +static char rcsid[] +#ifdef __GNUC__ +__attribute__ ((unused)) +#endif += "$Id$"; +#endif + +/* + * gg_libgadu_version() + * + * zwraca wersjê libgadu. + * + * - brak + * + * wersja libgadu. + */ +const char *gg_libgadu_version() +{ + return GG_LIBGADU_VERSION; +} + +/* + * gg_fix32() + * + * zamienia kolejno¶æ bajtów w liczbie 32-bitowej tak, by odpowiada³a + * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera, + * zamienia tylko na maszynach BE-wych. + * + * - x - liczba do zamiany + * + * liczba z odpowiedni± kolejno¶ci± bajtów. + */ +uint32_t gg_fix32(uint32_t x) +{ +#ifndef __GG_LIBGADU_BIGENDIAN + return x; +#else + return (uint32_t) + (((x & (uint32_t) 0x000000ffU) << 24) | + ((x & (uint32_t) 0x0000ff00U) << 8) | + ((x & (uint32_t) 0x00ff0000U) >> 8) | + ((x & (uint32_t) 0xff000000U) >> 24)); +#endif +} + +/* + * gg_fix16() + * + * zamienia kolejno¶æ bajtów w liczbie 16-bitowej tak, by odpowiada³a + * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera, + * zamienia tylko na maszynach BE-wych. + * + * - x - liczba do zamiany + * + * liczba z odpowiedni± kolejno¶ci± bajtów. + */ +uint16_t gg_fix16(uint16_t x) +{ +#ifndef __GG_LIBGADU_BIGENDIAN + return x; +#else + return (uint16_t) + (((x & (uint16_t) 0x00ffU) << 8) | + ((x & (uint16_t) 0xff00U) >> 8)); +#endif +} + +/* + * gg_login_hash() // funkcja wewnêtrzna + * + * liczy hash z has³a i danego seeda. + * + * - password - has³o do hashowania + * - seed - warto¶æ podana przez serwer + * + * hash. + */ +unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) +{ + unsigned int x, y, z; + + y = seed; + + for (x = 0; *password; password++) { + x = (x & 0xffffff00) | *password; + y ^= x; + y += x; + x <<= 8; + y ^= x; + x <<= 8; + y -= x; + x <<= 8; + y ^= x; + + z = y & 0x1F; + y = (y << z) | (y >> (32 - z)); + } + + return y; +} + +/* + * gg_resolve() // funkcja wewnêtrzna + * + * tworzy potok, forkuje siê i w drugim procesie zaczyna resolvowaæ + * podanego hosta. zapisuje w sesji deskryptor potoku. je¶li co¶ tam + * bêdzie gotowego, znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li + * nie znajdzie, zwraca INADDR_NONE. + * + * - fd - wska¼nik gdzie wrzuciæ deskryptor + * - pid - gdzie wrzuciæ pid procesu potomnego + * - hostname - nazwa hosta do zresolvowania + * + * 0, -1. + */ +int gg_resolve(int *fd, int *pid, const char *hostname) +{ + int pipes[2], res; + struct in_addr a; + int errno2; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); + + if (!fd || !pid) { + errno = EFAULT; + return -1; + } + + if (pipe(pipes) == -1) + return -1; + + if ((res = fork()) == -1) { + errno2 = errno; + close(pipes[0]); + close(pipes[1]); + errno = errno2; + return -1; + } + + if (!res) { + close(pipes[0]); + + if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { + struct in_addr *hn; + + if (!(hn = gg_gethostbyname(hostname))) + a.s_addr = INADDR_NONE; + else { + a.s_addr = hn->s_addr; + free(hn); + } + } + + write(pipes[1], &a, sizeof(a)); + + exit(0); + } + + close(pipes[1]); + + *fd = pipes[0]; + *pid = res; + + return 0; +} + +#ifdef __GG_LIBGADU_HAVE_PTHREAD + +struct gg_resolve_pthread_data { + char *hostname; + int fd; +}; + +static void *gg_resolve_pthread_thread(void *arg) +{ + struct gg_resolve_pthread_data *d = arg; + struct in_addr a; + + pthread_detach(pthread_self()); + + if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + struct in_addr *hn; + + if (!(hn = gg_gethostbyname(d->hostname))) + a.s_addr = INADDR_NONE; + else { + a.s_addr = hn->s_addr; + free(hn); + } + } + + write(d->fd, &a, sizeof(a)); + close(d->fd); + + free(d->hostname); + d->hostname = NULL; + + free(d); + + pthread_exit(NULL); + + return NULL; /* ¿eby kompilator nie marudzi³ */ +} + +/* + * gg_resolve_pthread() // funkcja wewnêtrzna + * + * tworzy potok, nowy w±tek i w nim zaczyna resolvowaæ podanego hosta. + * zapisuje w sesji deskryptor potoku. je¶li co¶ tam bêdzie gotowego, + * znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li nie znajdzie, zwraca + * INADDR_NONE. + * + * - fd - wska¼nik do zmiennej przechowuj±cej desktyptor resolvera + * - resolver - wska¼nik do wska¼nika resolvera + * - hostname - nazwa hosta do zresolvowania + * + * 0, -1. + */ +int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) +{ + struct gg_resolve_pthread_data *d = NULL; + pthread_t *tmp; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); + + if (!resolver || !fd || !hostname) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (!(tmp = malloc(sizeof(pthread_t)))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); + return -1; + } + + if (pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + free(tmp); + return -1; + } + + if (!(d = malloc(sizeof(*d)))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->hostname = NULL; + + if (!(d->hostname = strdup(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->fd = pipes[1]; + + if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); + + *resolver = tmp; + + *fd = pipes[0]; + + return 0; + +cleanup: + if (d) { + free(d->hostname); + free(d); + } + + close(pipes[0]); + close(pipes[1]); + + free(tmp); + + errno = new_errno; + + return -1; +} + +#endif + +/* + * gg_read() // funkcja pomocnicza + * + * czyta z gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy + * po³±czenie zwyk³e czy TLS. + * + * - sess - sesja, + * - buf - bufor, + * - length - ilo¶æ bajtów, + * + * takie same warto¶ci jak read(). + */ +int gg_read(struct gg_session *sess, char *buf, int length) +{ + int res; + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) { + int err; + + res = SSL_read(sess->ssl, buf, length); + + if (res < 0) { + err = SSL_get_error(sess->ssl, res); + + if (err == SSL_ERROR_WANT_READ) + errno = EAGAIN; + + return -1; + } + } else +#endif + res = read(sess->fd, buf, length); + + return res; +} + +/* + * gg_write() // funkcja pomocnicza + * + * zapisuje do gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy + * po³±czenie zwyk³e czy TLS. + * + * - sess - sesja, + * - buf - bufor, + * - length - ilo¶æ bajtów, + * + * takie same warto¶ci jak write(). + */ +int gg_write(struct gg_session *sess, const char *buf, int length) +{ + int res = 0; + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) { + int err; + + res = SSL_write(sess->ssl, buf, length); + + if (res < 0) { + err = SSL_get_error(sess->ssl, res); + + if (err == SSL_ERROR_WANT_WRITE) + errno = EAGAIN; + + return -1; + } + } else +#endif + { + int written = 0; + + while (written < length) { + res = write(sess->fd, buf + written, length - written); + + if (res == -1) { + if (errno == EAGAIN) + continue; + else + break; + } else { + written += res; + res = written; + } + } + } + + return res; +} + +/* + * gg_recv_packet() // funkcja wewnêtrzna + * + * odbiera jeden pakiet i zwraca wska¼nik do niego. pamiêæ po nim + * nale¿y zwolniæ za pomoc± free(). + * + * - sess - opis sesji + * + * w przypadku b³êdu NULL, kod b³êdu w errno. nale¿y zwróciæ uwagê, ¿e gdy + * po³±czenie jest nieblokuj±ce, a kod b³êdu wynosi EAGAIN, nie uda³o siê + * odczytaæ ca³ego pakietu i nie nale¿y tego traktowaæ jako b³±d. + */ +void *gg_recv_packet(struct gg_session *sess) +{ + struct gg_header h; + char *buf = NULL; + int ret = 0; + unsigned int offset, size = 0; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); + + if (!sess) { + errno = EFAULT; + return NULL; + } + + if (sess->recv_left < 1) { + if (sess->header_buf) { + memcpy(&h, sess->header_buf, sess->header_done); + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); + free(sess->header_buf); + sess->header_buf = NULL; + } else + sess->header_done = 0; + + while (sess->header_done < sizeof(h)) { + ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); + + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); + + if (!ret) { + errno = ECONNRESET; + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); + return NULL; + } + + if (ret == -1) { + if (errno == EINTR) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); + continue; + } + + if (errno == EAGAIN) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); + + if (!(sess->header_buf = malloc(sess->header_done))) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); + return NULL; + } + + memcpy(sess->header_buf, &h, sess->header_done); + + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); + + return NULL; + } + + sess->header_done += ret; + + } + + h.type = gg_fix32(h.type); + h.length = gg_fix32(h.length); + } else + memcpy(&h, sess->recv_buf, sizeof(h)); + + /* jakie¶ sensowne limity na rozmiar pakietu */ + if (h.length > 65535) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); + errno = ERANGE; + return NULL; + } + + if (sess->recv_left > 0) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); + size = sess->recv_left; + offset = sess->recv_done; + buf = sess->recv_buf; + } else { + if (!(buf = malloc(sizeof(h) + h.length + 1))) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); + return NULL; + } + + memcpy(buf, &h, sizeof(h)); + + offset = 0; + size = h.length; + } + + while (size > 0) { + ret = gg_read(sess, buf + sizeof(h) + offset, size); + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); + if (!ret) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); + errno = ECONNRESET; + return NULL; + } + if (ret > -1 && ret <= size) { + offset += ret; + size -= ret; + } else if (ret == -1) { + int errno2 = errno; + + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno = errno2; + + if (errno == EAGAIN) { + gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); + sess->recv_buf = buf; + sess->recv_left = size; + sess->recv_done = offset; + return NULL; + } + if (errno != EINTR) { + free(buf); + return NULL; + } + } + } + + sess->recv_left = 0; + + if ((gg_debug_level & GG_DEBUG_DUMP)) { + unsigned int i; + + gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); + for (i = 0; i < sizeof(h) + h.length; i++) + gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); + gg_debug(GG_DEBUG_DUMP, "\n"); + } + + return buf; +} + +/* + * gg_send_packet() // funkcja wewnêtrzna + * + * konstruuje pakiet i wysy³a go do serwera. + * + * - sock - deskryptor gniazda + * - type - typ pakietu + * - payload_1 - pierwsza czê¶æ pakietu + * - payload_length_1 - d³ugo¶æ pierwszej czê¶ci + * - payload_2 - druga czê¶æ pakietu + * - payload_length_2 - d³ugo¶æ drugiej czê¶ci + * - ... - kolejne czê¶ci pakietu i ich d³ugo¶ci + * - NULL - koñcowym parametr (konieczny!) + * + * je¶li siê powiod³o, zwraca 0, w przypadku b³êdu -1. je¶li errno == ENOMEM, + * zabrak³o pamiêci. inaczej by³ b³±d przy wysy³aniu pakietu. dla errno == 0 + * nie wys³ano ca³ego pakietu. + */ +int gg_send_packet(struct gg_session *sess, int type, ...) +{ + struct gg_header *h; + char *tmp; + unsigned int tmp_length; + void *payload; + unsigned int payload_length; + va_list ap; + int res; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); + + tmp_length = sizeof(struct gg_header); + + if (!(tmp = malloc(tmp_length))) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); + return -1; + } + + va_start(ap, type); + + payload = va_arg(ap, void *); + + while (payload) { + char *tmp2; + + payload_length = va_arg(ap, unsigned int); + + if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); + free(tmp); + va_end(ap); + return -1; + } + + tmp = tmp2; + + memcpy(tmp + tmp_length, payload, payload_length); + tmp_length += payload_length; + + payload = va_arg(ap, void *); + } + + va_end(ap); + + h = (struct gg_header*) tmp; + h->type = gg_fix32(type); + h->length = gg_fix32(tmp_length - sizeof(struct gg_header)); + + if ((gg_debug_level & GG_DEBUG_DUMP)) { + unsigned int i; + + gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); + for (i = 0; i < tmp_length; ++i) + gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); + gg_debug(GG_DEBUG_DUMP, "\n"); + } + + if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { + gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); + free(tmp); + return -1; + } + + free(tmp); + return 0; +} + +/* + * gg_session_callback() // funkcja wewnêtrzna + * + * wywo³ywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje + * do gg_session->event jego wynik. + */ +static int gg_session_callback(struct gg_session *s) +{ + if (!s) { + errno = EFAULT; + return -1; + } + + return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; +} + +/* + * gg_login() + * + * rozpoczyna procedurê ³±czenia siê z serwerem. resztê obs³uguje siê przez + * gg_watch_fd(). + * + * UWAGA! program musi obs³u¿yæ SIGCHLD, je¶li ³±czy siê asynchronicznie, + * ¿eby poprawnie zamkn±æ proces resolvera. + * + * - p - struktura opisuj±ca pocz±tkowy stan. wymagane pola: uin, + * password + * + * w przypadku b³êdu NULL, je¶li idzie dobrze (async) albo posz³o + * dobrze (sync), zwróci wska¼nik do zaalokowanej struct gg_session. + */ +struct gg_session *gg_login(const struct gg_login_params *p) +{ + struct gg_session *sess = NULL; + char *hostname; + int port; + + if (!p) { + gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p); + errno = EFAULT; + return NULL; + } + + gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async); + + if (!(sess = malloc(sizeof(struct gg_session)))) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n"); + goto fail; + } + + memset(sess, 0, sizeof(struct gg_session)); + + if (!p->password || !p->uin) { + gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n"); + errno = EFAULT; + goto fail; + } + + if (!(sess->password = strdup(p->password))) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n"); + goto fail; + } + + if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + goto fail; + } + + sess->uin = p->uin; + sess->state = GG_STATE_RESOLVING; + sess->check = GG_CHECK_READ; + sess->timeout = GG_DEFAULT_TIMEOUT; + sess->async = p->async; + sess->type = GG_SESSION_GG; + sess->initial_status = p->status; + sess->callback = gg_session_callback; + sess->destroy = gg_free_session; + sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT); + sess->server_addr = p->server_addr; + sess->external_port = p->external_port; + sess->external_addr = p->external_addr; + sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; + if (p->era_omnix) + sess->protocol_version |= GG_ERA_OMNIX_MASK; + if (p->has_audio) + sess->protocol_version |= GG_HAS_AUDIO_MASK; + sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; + sess->last_sysmsg = p->last_sysmsg; + sess->image_size = p->image_size; + sess->pid = -1; + + if (p->tls == 1) { +#ifdef __GG_LIBGADU_HAVE_OPENSSL + char buf[1024]; + + OpenSSL_add_ssl_algorithms(); + + if (!RAND_status()) { + char rdata[1024]; + struct { + time_t time; + void *ptr; + } rstruct; + + time(&rstruct.time); + rstruct.ptr = (void *) &rstruct; + + RAND_seed((void *) rdata, sizeof(rdata)); + RAND_seed((void *) &rstruct, sizeof(rstruct)); + } + + sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); + + if (!sess->ssl_ctx) { + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); + gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf); + goto fail; + } + + SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL); + + sess->ssl = SSL_new(sess->ssl_ctx); + + if (!sess->ssl) { + ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); + gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf); + goto fail; + } +#else + gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); +#endif + } + + if (gg_proxy_enabled) { + hostname = gg_proxy_host; + sess->proxy_port = port = gg_proxy_port; + } else { + hostname = GG_APPMSG_HOST; + port = GG_APPMSG_PORT; + } + + if (!p->async) { + struct in_addr a; + + if (!p->server_addr || !p->server_port) { + if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { + struct in_addr *hn; + + if (!(hn = gg_gethostbyname(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); + goto fail; + } else { + a.s_addr = hn->s_addr; + free(hn); + } + } + } else { + a.s_addr = p->server_addr; + port = p->server_port; + } + + sess->hub_addr = a.s_addr; + + if (gg_proxy_enabled) + sess->proxy_addr = a.s_addr; + + if ((sess->fd = gg_connect(&a, port, 0)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + + if (p->server_addr && p->server_port) + sess->state = GG_STATE_CONNECTING_GG; + else + sess->state = GG_STATE_CONNECTING_HUB; + + while (sess->state != GG_STATE_CONNECTED) { + struct gg_event *e; + + if (!(e = gg_watch_fd(sess))) { + gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n"); + goto fail; + } + + if (e->type == GG_EVENT_CONN_FAILED) { + errno = EACCES; + gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n"); + gg_event_free(e); + goto fail; + } + + gg_event_free(e); + } + + return sess; + } + + if (!sess->server_addr || gg_proxy_enabled) { +#ifndef __GG_LIBGADU_HAVE_PTHREAD + if (gg_resolve(&sess->fd, &sess->pid, hostname)) { +#else + if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + } else { + if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + } + + return sess; + +fail: + if (sess) { + if (sess->password) + free(sess->password); + if (sess->initial_descr) + free(sess->initial_descr); + free(sess); + } + + return NULL; +} + +/* + * gg_free_session() + * + * próbuje zamkn±æ po³±czenia i zwalnia pamiêæ zajmowan± przez sesjê. + * + * - sess - opis sesji + */ +void gg_free_session(struct gg_session *sess) +{ + if (!sess) + return; + + /* XXX dopisaæ zwalnianie i zamykanie wszystkiego, co mog³o zostaæ */ + + if (sess->password) + free(sess->password); + + if (sess->initial_descr) + free(sess->initial_descr); + + if (sess->client_version) + free(sess->client_version); + + if (sess->header_buf) + free(sess->header_buf); + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) + SSL_free(sess->ssl); + + if (sess->ssl_ctx) + SSL_CTX_free(sess->ssl_ctx); +#endif + +#ifdef __GG_LIBGADU_HAVE_PTHREAD + if (sess->resolver) { + pthread_cancel(*((pthread_t*) sess->resolver)); + free(sess->resolver); + sess->resolver = NULL; + } +#else + if (sess->pid != -1) { + kill(sess->pid, SIGTERM); + waitpid(sess->pid, NULL, WNOHANG); + } +#endif + + if (sess->fd != -1) + close(sess->fd); + + while (sess->images) + gg_image_queue_remove(sess, sess->images, 1); + + free(sess); +} + +/* + * gg_change_status() + * + * zmienia status u¿ytkownika. przydatne do /away i /busy oraz /quit. + * + * - sess - opis sesji + * - status - nowy status u¿ytkownika + * + * 0, -1. + */ +int gg_change_status(struct gg_session *sess, int status) +{ + struct gg_new_status p; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + p.status = gg_fix32(status); + + sess->status = status; + + return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); +} + +/* + * gg_change_status_descr() + * + * zmienia status u¿ytkownika na opisowy. + * + * - sess - opis sesji + * - status - nowy status u¿ytkownika + * - descr - opis statusu + * + * 0, -1. + */ +int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) +{ + struct gg_new_status p; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); + + if (!sess || !descr) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + p.status = gg_fix32(status); + + sess->status = status; + + return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); +} + +/* + * gg_change_status_descr_time() + * + * zmienia status u¿ytkownika na opisowy z godzin± powrotu. + * + * - sess - opis sesji + * - status - nowy status u¿ytkownika + * - descr - opis statusu + * - time - czas w formacie uniksowym + * + * 0, -1. + */ +int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) +{ + struct gg_new_status p; + uint32_t newtime; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + + if (!sess || !descr || !time) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + p.status = gg_fix32(status); + + sess->status = status; + + newtime = gg_fix32(time); + + return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); +} + +/* + * gg_logoff() + * + * wylogowuje u¿ytkownika i zamyka po³±czenie, ale nie zwalnia pamiêci. + * + * - sess - opis sesji + */ +void gg_logoff(struct gg_session *sess) +{ + if (!sess) + return; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); + + if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + if (sess->ssl) + SSL_shutdown(sess->ssl); +#endif + +#ifdef __GG_LIBGADU_HAVE_PTHREAD + if (sess->resolver) { + pthread_cancel(*((pthread_t*) sess->resolver)); + free(sess->resolver); + sess->resolver = NULL; + } +#else + if (sess->pid != -1) { + kill(sess->pid, SIGTERM); + waitpid(sess->pid, NULL, WNOHANG); + sess->pid = -1; + } +#endif + + if (sess->fd != -1) { + shutdown(sess->fd, SHUT_RDWR); + close(sess->fd); + sess->fd = -1; + } +} + +/* + * gg_image_request() + * + * wysy³a ¿±danie wys³ania obrazka o podanych parametrach. + * + * - sess - opis sesji + * - recipient - numer adresata + * - size - rozmiar obrazka + * - crc32 - suma kontrolna obrazka + * + * 0/-1 + */ +int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) +{ + struct gg_send_msg s; + struct gg_msg_image_request r; + char dummy = 0; + int res; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (size < 0) { + errno = EINVAL; + return -1; + } + + s.recipient = gg_fix32(recipient); + s.seq = gg_fix32(0); + s.msgclass = gg_fix32(GG_CLASS_MSG); + + r.flag = 0x04; + r.size = gg_fix32(size); + r.crc32 = gg_fix32(crc32); + + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); + + if (!res) { + struct gg_image_queue *q = malloc(sizeof(*q)); + char *buf; + + if (!q) { + gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); + return -1; + } + + buf = malloc(size); + if (size && !buf) + { + gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); + free(q); + return -1; + } + + memset(q, 0, sizeof(*q)); + + q->sender = recipient; + q->size = size; + q->crc32 = crc32; + q->image = buf; + + if (!sess->images) + sess->images = q; + else { + struct gg_image_queue *qq; + + for (qq = sess->images; qq->next; qq = qq->next) + ; + + qq->next = q; + } + } + + return res; +} + +/* + * gg_image_reply() + * + * wysy³a ¿±dany obrazek. + * + * - sess - opis sesji + * - recipient - numer adresata + * - filename - nazwa pliku + * - image - bufor z obrazkiem + * - size - rozmiar obrazka + * + * 0/-1 + */ +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) +{ + struct gg_msg_image_reply *r; + struct gg_send_msg s; + const char *tmp; + char buf[1910]; + int res = -1; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); + + if (!sess || !filename || !image) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (size < 0) { + errno = EINVAL; + return -1; + } + + /* wytnij ¶cie¿ki, zostaw tylko nazwê pliku */ + while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) + filename = tmp + 1; + + if (strlen(filename) < 1 || strlen(filename) > 1024) { + errno = EINVAL; + return -1; + } + + s.recipient = gg_fix32(recipient); + s.seq = gg_fix32(0); + s.msgclass = gg_fix32(GG_CLASS_MSG); + + buf[0] = 0; + r = (void*) &buf[1]; + + r->flag = 0x05; + r->size = gg_fix32(size); + r->crc32 = gg_fix32(gg_crc32(0, image, size)); + + while (size > 0) { + int buflen, chunklen; + + /* \0 + struct gg_msg_image_reply */ + buflen = sizeof(struct gg_msg_image_reply) + 1; + + /* w pierwszym kawa³ku jest nazwa pliku */ + if (r->flag == 0x05) { + strcpy(buf + buflen, filename); + buflen += strlen(filename) + 1; + } + + chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size; + + memcpy(buf + buflen, image, chunklen); + size -= chunklen; + image += chunklen; + + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); + + if (res == -1) + break; + + r->flag = 0x06; + } + + return res; +} + +/* + * gg_send_message_ctcp() + * + * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer + * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomo¶ci + * - recipient - numer adresata + * - message - tre¶æ wiadomo¶ci + * - message_len - d³ugo¶æ + * + * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. + */ +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) +{ + struct gg_send_msg s; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + s.recipient = gg_fix32(recipient); + s.seq = gg_fix32(0); + s.msgclass = gg_fix32(msgclass); + + return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); +} + +/* + * gg_send_message() + * + * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer + * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomo¶ci + * - recipient - numer adresata + * - message - tre¶æ wiadomo¶ci + * + * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. + */ +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); + + return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); +} + +/* + * gg_send_message_richtext() + * + * wysy³a kolorow± wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer + * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomo¶ci + * - recipient - numer adresata + * - message - tre¶æ wiadomo¶ci + * - format - informacje o formatowaniu + * - formatlen - d³ugo¶æ informacji o formatowaniu + * + * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. + */ +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) +{ + struct gg_send_msg s; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (!message) { + errno = EFAULT; + return -1; + } + + s.recipient = gg_fix32(recipient); + if (!sess->seq) + sess->seq = 0x01740000 | (rand() & 0xffff); + s.seq = gg_fix32(sess->seq); + s.msgclass = gg_fix32(msgclass); + sess->seq += (rand() % 0x300) + 0x300; + + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1) + return -1; + + return gg_fix32(s.seq); +} + +/* + * gg_send_message_confer() + * + * wysy³a wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca losowy numer + * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomo¶ci + * - recipients_count - ilo¶æ adresatów + * - recipients - numerki adresatów + * - message - tre¶æ wiadomo¶ci + * + * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. + */ +int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); + + return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); +} + +/* + * gg_send_message_confer_richtext() + * + * wysy³a kolorow± wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca + * losowy numer sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do + * potwierdzenia. + * + * - sess - opis sesji + * - msgclass - rodzaj wiadomo¶ci + * - recipients_count - ilo¶æ adresatów + * - recipients - numerki adresatów + * - message - tre¶æ wiadomo¶ci + * - format - informacje o formatowaniu + * - formatlen - d³ugo¶æ informacji o formatowaniu + * + * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu. + */ +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) +{ + struct gg_send_msg s; + struct gg_msg_recipients r; + int i, j, k; + uin_t *recps; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { + errno = EINVAL; + return -1; + } + + r.flag = 0x01; + r.count = gg_fix32(recipients_count - 1); + + if (!sess->seq) + sess->seq = 0x01740000 | (rand() & 0xffff); + s.seq = gg_fix32(sess->seq); + s.msgclass = gg_fix32(msgclass); + + recps = malloc(sizeof(uin_t) * recipients_count); + if (!recps) + return -1; + + for (i = 0; i < recipients_count; i++) { + + s.recipient = gg_fix32(recipients[i]); + + for (j = 0, k = 0; j < recipients_count; j++) + if (recipients[j] != recipients[i]) { + recps[k] = gg_fix32(recipients[j]); + k++; + } + + if (!i) + sess->seq += (rand() % 0x300) + 0x300; + + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { + free(recps); + return -1; + } + } + + free(recps); + + return gg_fix32(s.seq); +} + +/* + * gg_ping() + * + * wysy³a do serwera pakiet ping. + * + * - sess - opis sesji + * + * 0, -1. + */ +int gg_ping(struct gg_session *sess) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + return gg_send_packet(sess, GG_PING, NULL); +} + +/* + * gg_notify_ex() + * + * wysy³a serwerowi listê kontaktów (wraz z odpowiadaj±cymi im typami userów), + * dziêki czemu wie, czyj stan nas interesuje. + * + * - sess - opis sesji + * - userlist - wska¼nik do tablicy numerów + * - types - wska¼nik do tablicy typów u¿ytkowników + * - count - ilo¶æ numerków + * + * 0, -1. + */ +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) +{ + struct gg_notify *n; + uin_t *u; + char *t; + int i, res = 0; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (!userlist || !count) + return gg_send_packet(sess, GG_LIST_EMPTY, NULL); + + while (count > 0) { + int part_count, packet_type; + + if (count > 400) { + part_count = 400; + packet_type = GG_NOTIFY_FIRST; + } else { + part_count = count; + packet_type = GG_NOTIFY_LAST; + } + + if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) + return -1; + + for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { + n[i].uin = gg_fix32(*u); + n[i].dunno1 = *t; + } + + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { + free(n); + res = -1; + break; + } + + count -= part_count; + userlist += part_count; + types += part_count; + + free(n); + } + + return res; +} + +/* + * gg_notify() + * + * wysy³a serwerowi listê kontaktów, dziêki czemu wie, czyj stan nas + * interesuje. + * + * - sess - opis sesji + * - userlist - wska¼nik do tablicy numerów + * - count - ilo¶æ numerków + * + * 0, -1. + */ +int gg_notify(struct gg_session *sess, uin_t *userlist, int count) +{ + struct gg_notify *n; + uin_t *u; + int i, res = 0; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (!userlist || !count) + return gg_send_packet(sess, GG_LIST_EMPTY, NULL); + + while (count > 0) { + int part_count, packet_type; + + if (count > 400) { + part_count = 400; + packet_type = GG_NOTIFY_FIRST; + } else { + part_count = count; + packet_type = GG_NOTIFY_LAST; + } + + if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) + return -1; + + for (u = userlist, i = 0; i < part_count; u++, i++) { + n[i].uin = gg_fix32(*u); + n[i].dunno1 = GG_USER_NORMAL; + } + + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { + res = -1; + free(n); + break; + } + + free(n); + + userlist += part_count; + count -= part_count; + } + + return res; +} + +/* + * gg_add_notify_ex() + * + * dodaje do listy kontaktów dany numer w trakcie po³±czenia. + * dodawanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html) + * + * - sess - opis sesji + * - uin - numer + * - type - typ + * + * 0, -1. + */ +int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) +{ + struct gg_add_remove a; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + a.uin = gg_fix32(uin); + a.dunno1 = type; + + return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); +} + +/* + * gg_add_notify() + * + * dodaje do listy kontaktów dany numer w trakcie po³±czenia. + * + * - sess - opis sesji + * - uin - numer + * + * 0, -1. + */ +int gg_add_notify(struct gg_session *sess, uin_t uin) +{ + return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); +} + +/* + * gg_remove_notify_ex() + * + * usuwa z listy kontaktów w trakcie po³±czenia. + * usuwanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html) + * + * - sess - opis sesji + * - uin - numer + * - type - typ + * + * 0, -1. + */ +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) +{ + struct gg_add_remove a; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + a.uin = gg_fix32(uin); + a.dunno1 = type; + + return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); +} + +/* + * gg_remove_notify() + * + * usuwa z listy kontaktów w trakcie po³±czenia. + * + * - sess - opis sesji + * - uin - numer + * + * 0, -1. + */ +int gg_remove_notify(struct gg_session *sess, uin_t uin) +{ + return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); +} + +/* + * gg_userlist_request() + * + * wysy³a ¿±danie/zapytanie listy kontaktów na serwerze. + * + * - sess - opis sesji + * - type - rodzaj zapytania/¿±dania + * - request - tre¶æ zapytania/¿±dania (mo¿e byæ NULL) + * + * 0, -1 + */ +int gg_userlist_request(struct gg_session *sess, char type, const char *request) +{ + int len; + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + if (!request) { + sess->userlist_blocks = 1; + return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); + } + + len = strlen(request); + + sess->userlist_blocks = 0; + + while (len > 2047) { + sess->userlist_blocks++; + + if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1) + return -1; + + if (type == GG_USERLIST_PUT) + type = GG_USERLIST_PUT_MORE; + + request += 2047; + len -= 2047; + } + + sess->userlist_blocks++; + + return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/libgadu.h b/kopete/protocols/gadu/libgadu/libgadu.h new file mode 100644 index 00000000..18588500 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/libgadu.h @@ -0,0 +1,1310 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Wo¼ny <speedy@ziew.org> + * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org> + * Tomasz Chiliñski <chilek@chilan.com> + * Piotr Wysocki <wysek@linux.bydg.org> + * Dawid Jarosz <dawjar@poczta.onet.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __GG_LIBGADU_H +#define __GG_LIBGADU_H + +#ifdef __cplusplus +#ifdef _WIN32 +#pragma pack(push, 1) +#endif +extern "C" { +#endif + +#include <libgadu-config.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> + +#ifdef __GG_LIBGADU_HAVE_OPENSSL +#include <openssl/ssl.h> +#endif + +/* + * typedef uin_t + * + * typ reprezentuj±cy numer osoby. + */ +typedef uint32_t uin_t; + +/* + * ogólna struktura opisuj±ca ró¿ne sesje. przydatna w klientach. + */ +#define gg_common_head(x) \ + int fd; /* podgl±dany deskryptor */ \ + int check; /* sprawdzamy zapis czy odczyt */ \ + int state; /* aktualny stan maszynki */ \ + int error; /* kod b³êdu dla GG_STATE_ERROR */ \ + int type; /* rodzaj sesji */ \ + int id; /* identyfikator */ \ + int timeout; /* sugerowany timeout w sekundach */ \ + int (*callback)(x*); /* callback przy zmianach */ \ + void (*destroy)(x*); /* funkcja niszczenia */ + +struct gg_common { + gg_common_head(struct gg_common) +}; + +struct gg_image_queue; + +/* + * struct gg_session + * + * struktura opisuj±ca dan± sesjê. tworzona przez gg_login(), zwalniana + * przez gg_free_session(). + */ +struct gg_session { + gg_common_head(struct gg_session) + + int async; /* czy po³±czenie jest asynchroniczne */ + int pid; /* pid procesu resolvera */ + int port; /* port, z którym siê ³±czymy */ + int seq; /* numer sekwencyjny ostatniej wiadomo¶ci */ + int last_pong; /* czas otrzymania ostatniego ping/pong */ + int last_event; /* czas otrzymania ostatniego pakietu */ + + struct gg_event *event; /* zdarzenie po ->callback() */ + + uint32_t proxy_addr; /* adres proxy, keszowany */ + uint16_t proxy_port; /* port proxy */ + + uint32_t hub_addr; /* adres huba po resolvniêciu */ + uint32_t server_addr; /* adres serwera, od huba */ + + uint32_t client_addr; /* adres klienta */ + uint16_t client_port; /* port, na którym klient s³ucha */ + + uint32_t external_addr; /* adres zewnetrzny klienta */ + uint16_t external_port; /* port zewnetrzny klienta */ + + uin_t uin; /* numerek klienta */ + char *password; /* i jego has³o. zwalniane automagicznie */ + + int initial_status; /* pocz±tkowy stan klienta */ + int status; /* aktualny stan klienta */ + + char *recv_buf; /* bufor na otrzymywane pakiety */ + int recv_done; /* ile ju¿ wczytano do bufora */ + int recv_left; /* i ile jeszcze trzeba wczytaæ */ + + int protocol_version; /* wersja u¿ywanego protoko³u */ + char *client_version; /* wersja u¿ywanego klienta */ + int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */ + + char *initial_descr; /* pocz±tkowy opis stanu klienta */ + + void *resolver; /* wska¼nik na informacje resolvera */ + + char *header_buf; /* bufor na pocz±tek nag³ówka */ + unsigned int header_done;/* ile ju¿ mamy */ + +#ifdef __GG_LIBGADU_HAVE_OPENSSL + SSL *ssl; /* sesja TLS */ + SSL_CTX *ssl_ctx; /* kontekst sesji? */ +#else + void *ssl; /* zachowujemy ABI */ + void *ssl_ctx; +#endif + + int image_size; /* maksymalny rozmiar obrazków w KiB */ + + char *userlist_reply; /* fragment odpowiedzi listy kontaktów */ + + int userlist_blocks; /* na ile kawa³ków podzielono listê kontaktów */ + + struct gg_image_queue *images; /* aktualnie wczytywane obrazki */ +}; + +/* + * struct gg_http + * + * ogólna struktura opisuj±ca stan wszystkich operacji HTTP. tworzona + * przez gg_http_connect(), zwalniana przez gg_http_free(). + */ +struct gg_http { + gg_common_head(struct gg_http) + + int async; /* czy po³±czenie asynchroniczne */ + int pid; /* pid procesu resolvera */ + int port; /* port, z którym siê ³±czymy */ + + char *query; /* bufor zapytania http */ + char *header; /* bufor nag³ówka */ + int header_size; /* rozmiar wczytanego nag³ówka */ + char *body; /* bufor otrzymanych informacji */ + unsigned int body_size; /* oczekiwana ilo¶æ informacji */ + + void *data; /* dane danej operacji http */ + + char *user_data; /* dane u¿ytkownika, nie s± zwalniane przez gg_http_free() */ + + void *resolver; /* wska¼nik na informacje resolvera */ + + unsigned int body_done; /* ile ju¿ tre¶ci odebrano? */ +}; + +#ifdef __GNUC__ +#define GG_PACKED __attribute__ ((packed)) +#else +#define GG_PACKED +#endif + +#define GG_MAX_PATH 276 + +/* + * struct gg_file_info + * + * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbêdnej przy + * wysy³aniu plików. + */ +struct gg_file_info { + uint32_t mode; /* dwFileAttributes */ + uint32_t ctime[2]; /* ftCreationTime */ + uint32_t atime[2]; /* ftLastAccessTime */ + uint32_t mtime[2]; /* ftLastWriteTime */ + uint32_t size_hi; /* nFileSizeHigh */ + uint32_t size; /* nFileSizeLow */ + uint32_t reserved0; /* dwReserved0 */ + uint32_t reserved1; /* dwReserved1 */ + unsigned char filename[GG_MAX_PATH - 14]; /* cFileName */ + unsigned char short_filename[14]; /* cAlternateFileName */ +} GG_PACKED; + +/* + * struct gg_dcc + * + * struktura opisuj±ca nas³uchuj±ce gniazdo po³±czeñ miêdzy klientami. + * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free(). + */ +struct gg_dcc { + gg_common_head(struct gg_dcc) + + struct gg_event *event; /* opis zdarzenia */ + + int active; /* czy to my siê ³±czymy? */ + int port; /* port, na którym siedzi */ + uin_t uin; /* uin klienta */ + uin_t peer_uin; /* uin drugiej strony */ + int file_fd; /* deskryptor pliku */ + unsigned int offset; /* offset w pliku */ + unsigned int chunk_size;/* rozmiar kawa³ka */ + unsigned int chunk_offset;/* offset w aktualnym kawa³ku */ + struct gg_file_info file_info; + /* informacje o pliku */ + int established; /* po³±czenie ustanowione */ + char *voice_buf; /* bufor na pakiet po³±czenia g³osowego */ + int incoming; /* po³±czenie przychodz±ce */ + char *chunk_buf; /* bufor na kawa³ek danych */ + uint32_t remote_addr; /* adres drugiej strony */ + uint16_t remote_port; /* port drugiej strony */ +}; + +/* + * enum gg_session_t + * + * rodzaje sesji. + */ +enum gg_session_t { + GG_SESSION_GG = 1, /* po³±czenie z serwerem gg */ + GG_SESSION_HTTP, /* ogólna sesja http */ + GG_SESSION_SEARCH, /* szukanie */ + GG_SESSION_REGISTER, /* rejestrowanie */ + GG_SESSION_REMIND, /* przypominanie has³a */ + GG_SESSION_PASSWD, /* zmiana has³a */ + GG_SESSION_CHANGE, /* zmiana informacji o sobie */ + GG_SESSION_DCC, /* ogólne po³±czenie DCC */ + GG_SESSION_DCC_SOCKET, /* nas³uchuj±cy socket */ + GG_SESSION_DCC_SEND, /* wysy³anie pliku */ + GG_SESSION_DCC_GET, /* odbieranie pliku */ + GG_SESSION_DCC_VOICE, /* rozmowa g³osowa */ + GG_SESSION_USERLIST_GET, /* pobieranie userlisty */ + GG_SESSION_USERLIST_PUT, /* wysy³anie userlisty */ + GG_SESSION_UNREGISTER, /* usuwanie konta */ + GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */ + GG_SESSION_TOKEN, /* pobieranie tokenu */ + + GG_SESSION_USER0 = 256, /* zdefiniowana dla u¿ytkownika */ + GG_SESSION_USER1, /* j.w. */ + GG_SESSION_USER2, /* j.w. */ + GG_SESSION_USER3, /* j.w. */ + GG_SESSION_USER4, /* j.w. */ + GG_SESSION_USER5, /* j.w. */ + GG_SESSION_USER6, /* j.w. */ + GG_SESSION_USER7 /* j.w. */ +}; + +/* + * enum gg_state_t + * + * opisuje stan asynchronicznej maszyny. + */ +enum gg_state_t { + /* wspólne */ + GG_STATE_IDLE = 0, /* nie powinno wyst±piæ. */ + GG_STATE_RESOLVING, /* wywo³a³ gethostbyname() */ + GG_STATE_CONNECTING, /* wywo³a³ connect() */ + GG_STATE_READING_DATA, /* czeka na dane http */ + GG_STATE_ERROR, /* wyst±pi³ b³±d. kod w x->error */ + + /* gg_session */ + GG_STATE_CONNECTING_HUB, /* wywo³a³ connect() na huba */ + GG_STATE_CONNECTING_GG, /* wywo³a³ connect() na serwer */ + GG_STATE_READING_KEY, /* czeka na klucz */ + GG_STATE_READING_REPLY, /* czeka na odpowied¼ */ + GG_STATE_CONNECTED, /* po³±czy³ siê */ + + /* gg_http */ + GG_STATE_SENDING_QUERY, /* wysy³a zapytanie http */ + GG_STATE_READING_HEADER, /* czeka na nag³ówek http */ + GG_STATE_PARSING, /* przetwarza dane */ + GG_STATE_DONE, /* skoñczy³ */ + + /* gg_dcc */ + GG_STATE_LISTENING, /* czeka na po³±czenia */ + GG_STATE_READING_UIN_1, /* czeka na uin peera */ + GG_STATE_READING_UIN_2, /* czeka na swój uin */ + GG_STATE_SENDING_ACK, /* wysy³a potwierdzenie dcc */ + GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */ + GG_STATE_READING_REQUEST, /* czeka na komendê */ + GG_STATE_SENDING_REQUEST, /* wysy³a komendê */ + GG_STATE_SENDING_FILE_INFO, /* wysy³a informacje o pliku */ + GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */ + GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */ + GG_STATE_SENDING_FILE_ACK, /* wysy³a potwierdzenie pliku */ + GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */ + GG_STATE_SENDING_FILE_HEADER, /* wysy³a nag³ówek pliku */ + GG_STATE_READING_FILE_HEADER, /* czeka na nag³ówek */ + GG_STATE_GETTING_FILE, /* odbiera plik */ + GG_STATE_SENDING_FILE, /* wysy³a plik */ + GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */ + GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */ + GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */ + GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */ + GG_STATE_SENDING_VOICE_ACK, /* wysy³a potwierdzenie voip */ + GG_STATE_SENDING_VOICE_REQUEST, /* wysy³a ¿±danie voip */ + GG_STATE_READING_TYPE, /* czeka na typ po³±czenia */ + + /* nowe. bez sensu jest to API. */ + GG_STATE_TLS_NEGOTIATION /* negocjuje po³±czenie TLS */ +}; + +/* + * enum gg_check_t + * + * informuje, co proces klienta powinien sprawdziæ na deskryptorze danego + * po³±czenia. + */ +enum gg_check_t { + GG_CHECK_NONE = 0, /* nic. nie powinno wyst±piæ */ + GG_CHECK_WRITE = 1, /* sprawdzamy mo¿liwo¶æ zapisu */ + GG_CHECK_READ = 2 /* sprawdzamy mo¿liwo¶æ odczytu */ +}; + +/* + * struct gg_login_params + * + * parametry gg_login(). przeniesiono do struktury, ¿eby unikn±æ problemów + * z ci±g³ymi zmianami API, gdy dodano co¶ nowego do protoko³u. + */ +struct gg_login_params { + uin_t uin; /* numerek */ + char *password; /* has³o */ + int async; /* asynchroniczne sockety? */ + int status; /* pocz±tkowy status klienta */ + char *status_descr; /* opis statusu */ + uint32_t server_addr; /* adres serwera gg */ + uint16_t server_port; /* port serwera gg */ + uint32_t client_addr; /* adres dcc klienta */ + uint16_t client_port; /* port dcc klienta */ + int protocol_version; /* wersja protoko³u */ + char *client_version; /* wersja klienta */ + int has_audio; /* czy ma d¼wiêk? */ + int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */ + uint32_t external_addr; /* adres widziany na zewnatrz */ + uint16_t external_port; /* port widziany na zewnatrz */ + int tls; /* czy ³±czymy po TLS? */ + int image_size; /* maksymalny rozmiar obrazka w KiB */ + int era_omnix; /* czy udawaæ klienta era omnix? */ + + char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych, + * ¿eby z dodaniem parametru nie + * zmienia³ siê rozmiar struktury */ +}; + +struct gg_session *gg_login(const struct gg_login_params *p); +void gg_free_session(struct gg_session *sess); +void gg_logoff(struct gg_session *sess); +int gg_change_status(struct gg_session *sess, int status); +int gg_change_status_descr(struct gg_session *sess, int status, const char *descr); +int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time); +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message); +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen); +int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message); +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen); +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len); +int gg_ping(struct gg_session *sess); +int gg_userlist_request(struct gg_session *sess, char type, const char *request); +int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32); +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); + +uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); + +struct gg_image_queue { + uin_t sender; /* nadawca obrazka */ + uint32_t size; /* rozmiar */ + uint32_t crc32; /* suma kontrolna */ + char *filename; /* nazwa pliku */ + char *image; /* bufor z obrazem */ + uint32_t done; /* ile ju¿ wczytano */ + + struct gg_image_queue *next; /* nastêpny na li¶cie */ +}; + +/* + * enum gg_event_t + * + * rodzaje zdarzeñ. + */ +enum gg_event_t { + GG_EVENT_NONE = 0, /* nic siê nie wydarzy³o */ + GG_EVENT_MSG, /* otrzymano wiadomo¶æ */ + GG_EVENT_NOTIFY, /* kto¶ siê pojawi³ */ + GG_EVENT_NOTIFY_DESCR, /* kto¶ siê pojawi³ z opisem */ + GG_EVENT_STATUS, /* kto¶ zmieni³ stan */ + GG_EVENT_ACK, /* potwierdzenie wys³ania wiadomo¶ci */ + GG_EVENT_PONG, /* pakiet pong */ + GG_EVENT_CONN_FAILED, /* po³±czenie siê nie uda³o */ + GG_EVENT_CONN_SUCCESS, /* po³±czenie siê powiod³o */ + GG_EVENT_DISCONNECT, /* serwer zrywa po³±czenie */ + + GG_EVENT_DCC_NEW, /* nowe po³±czenie miêdzy klientami */ + GG_EVENT_DCC_ERROR, /* b³±d po³±czenia miêdzy klientami */ + GG_EVENT_DCC_DONE, /* zakoñczono po³±czenie */ + GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */ + GG_EVENT_DCC_CALLBACK, /* klient siê po³±czy³ na ¿±danie */ + GG_EVENT_DCC_NEED_FILE_INFO, /* nale¿y wype³niæ file_info */ + GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */ + GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */ + GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy g³osowej */ + + GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */ + GG_EVENT_PUBDIR50_READ, /* odczytano w³asne dane z katalogu */ + GG_EVENT_PUBDIR50_WRITE, /* wpisano w³asne dane do katalogu */ + + GG_EVENT_STATUS60, /* kto¶ zmieni³ stan w GG 6.0 */ + GG_EVENT_NOTIFY60, /* kto¶ siê pojawi³ w GG 6.0 */ + GG_EVENT_USERLIST, /* odpowied¼ listy kontaktów w GG 6.0 */ + GG_EVENT_IMAGE_REQUEST, /* pro¶ba o wys³anie obrazka GG 6.0 */ + GG_EVENT_IMAGE_REPLY, /* podes³any obrazek GG 6.0 */ + GG_EVENT_DCC_ACK /* potwierdzenie transmisji */ +}; + +#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY + +/* + * enum gg_failure_t + * + * okre¶la powód nieudanego po³±czenia. + */ +enum gg_failure_t { + GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */ + GG_FAILURE_CONNECTING, /* nie mo¿na siê po³±czyæ */ + GG_FAILURE_INVALID, /* serwer zwróci³ nieprawid³owe dane */ + GG_FAILURE_READING, /* zerwano po³±czenie podczas odczytu */ + GG_FAILURE_WRITING, /* zerwano po³±czenie podczas zapisu */ + GG_FAILURE_PASSWORD, /* nieprawid³owe has³o */ + GG_FAILURE_404, /* XXX nieu¿ywane */ + GG_FAILURE_TLS, /* b³±d negocjacji TLS */ + GG_FAILURE_NEED_EMAIL, /* serwer roz³±czy³ nas z pro¶b± o zmianê emaila */ + GG_FAILURE_INTRUDER, /* za du¿o prób po³±czenia siê z nieprawid³owym has³em */ + GG_FAILURE_UNAVAILABLE /* serwery s± wy³±czone */ +}; + +/* + * enum gg_error_t + * + * okre¶la rodzaj b³êdu wywo³anego przez dan± operacjê. nie zawiera + * przesadnie szczegó³owych informacji o powodzie b³êdu, by nie komplikowaæ + * obs³ugi b³êdów. je¶li wymagana jest wiêksza dok³adno¶æ, nale¿y sprawdziæ + * zawarto¶æ zmiennej errno. + */ +enum gg_error_t { + GG_ERROR_RESOLVING = 1, /* b³±d znajdowania hosta */ + GG_ERROR_CONNECTING, /* b³±d ³aczenia siê */ + GG_ERROR_READING, /* b³±d odczytu */ + GG_ERROR_WRITING, /* b³±d wysy³ania */ + + GG_ERROR_DCC_HANDSHAKE, /* b³±d negocjacji */ + GG_ERROR_DCC_FILE, /* b³±d odczytu/zapisu pliku */ + GG_ERROR_DCC_EOF, /* plik siê skoñczy³? */ + GG_ERROR_DCC_NET, /* b³±d wysy³ania/odbierania */ + GG_ERROR_DCC_REFUSED /* po³±czenie odrzucone przez usera */ +}; + +/* + * struktury dotycz±ce wyszukiwania w GG 5.0. NIE NALE¯Y SIÊ DO NICH + * ODWO£YWAÆ BEZPO¦REDNIO! do dostêpu do nich s³u¿± funkcje gg_pubdir50_*() + */ +struct gg_pubdir50_entry { + int num; + char *field; + char *value; +}; + +struct gg_pubdir50_s { + int count; + uin_t next; + int type; + uint32_t seq; + struct gg_pubdir50_entry *entries; + int entries_count; +}; + +/* + * typedef gg_pubdir_50_t + * + * typ opisuj±cy zapytanie lub wynik zapytania katalogu publicznego + * z protoko³u GG 5.0. nie nale¿y siê odwo³ywaæ bezpo¶rednio do jego + * pól -- s³u¿± do tego funkcje gg_pubdir50_*() + */ +typedef struct gg_pubdir50_s *gg_pubdir50_t; + +/* + * struct gg_event + * + * struktura opisuj±ca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub + * z gg_dcc_watch_fd() + */ +struct gg_event { + int type; /* rodzaj zdarzenia -- gg_event_t */ + union { /* @event */ + struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów -- GG_EVENT_NOTIFY */ + + enum gg_failure_t failure; /* b³±d po³±czenia -- GG_EVENT_FAILURE */ + + struct gg_dcc *dcc_new; /* nowe po³±czenie bezpo¶rednie -- GG_EVENT_DCC_NEW */ + + int dcc_error; /* b³±d po³±czenia bezpo¶redniego -- GG_EVENT_DCC_ERROR */ + + gg_pubdir50_t pubdir50; /* wynik operacji zwi±zanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */ + + struct { /* @msg odebrano wiadomo¶æ -- GG_EVENT_MSG */ + uin_t sender; /* numer nadawcy */ + int msgclass; /* klasa wiadomo¶ci */ + time_t time; /* czas nadania */ + unsigned char *message; /* tre¶æ wiadomo¶ci */ + + int recipients_count; /* ilo¶æ odbiorców konferencji */ + uin_t *recipients; /* odbiorcy konferencji */ + + int formats_length; /* d³ugo¶æ informacji o formatowaniu tekstu */ + void *formats; /* informacje o formatowaniu tekstu */ + } msg; + + struct { /* @notify_descr informacje o li¶cie kontaktów z opisami stanu -- GG_EVENT_NOTIFY_DESCR */ + struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów */ + char *descr; /* opis stanu */ + } notify_descr; + + struct { /* @status zmiana stanu -- GG_EVENT_STATUS */ + uin_t uin; /* numer */ + uint32_t status; /* nowy stan */ + char *descr; /* opis stanu */ + } status; + + struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ + uin_t uin; /* numer */ + int status; /* nowy stan */ + uint32_t remote_ip; /* adres ip */ + uint16_t remote_port; /* port */ + int version; /* wersja klienta */ + int image_size; /* maksymalny rozmiar grafiki w KiB */ + char *descr; /* opis stanu */ + time_t time; /* czas powrotu */ + } status60; + + struct { /* @notify60 informacja o li¶cie kontaktów -- GG_EVENT_NOTIFY60 */ + uin_t uin; /* numer */ + int status; /* stan */ + uint32_t remote_ip; /* adres ip */ + uint16_t remote_port; /* port */ + int version; /* wersja klienta */ + int image_size; /* maksymalny rozmiar grafiki w KiB */ + char *descr; /* opis stanu */ + time_t time; /* czas powrotu */ + } *notify60; + + struct { /* @ack potwierdzenie wiadomo¶ci -- GG_EVENT_ACK */ + uin_t recipient; /* numer odbiorcy */ + int status; /* stan dorêczenia wiadomo¶ci */ + int seq; /* numer sekwencyjny wiadomo¶ci */ + } ack; + + struct { /* @dcc_voice_data otrzymano dane d¼wiêkowe -- GG_EVENT_DCC_VOICE_DATA */ + uint8_t *data; /* dane d¼wiêkowe */ + int length; /* ilo¶æ danych d¼wiêkowych */ + } dcc_voice_data; + + struct { /* @userlist odpowied¼ listy kontaktów serwera */ + char type; /* rodzaj odpowiedzi */ + char *reply; /* tre¶æ odpowiedzi */ + } userlist; + + struct { /* @image_request pro¶ba o obrazek */ + uin_t sender; /* nadawca pro¶by */ + uint32_t size; /* rozmiar obrazka */ + uint32_t crc32; /* suma kontrolna */ + } image_request; + + struct { /* @image_reply odpowied¼ z obrazkiem */ + uin_t sender; /* nadawca odpowiedzi */ + uint32_t size; /* rozmiar obrazka */ + uint32_t crc32; /* suma kontrolna */ + char *filename; /* nazwa pliku */ + char *image; /* bufor z obrazkiem */ + } image_reply; + } event; +}; + +struct gg_event *gg_watch_fd(struct gg_session *sess); +void gg_event_free(struct gg_event *e); +#define gg_free_event gg_event_free + +/* + * funkcje obs³ugi listy kontaktów. + */ +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count); +int gg_notify(struct gg_session *sess, uin_t *userlist, int count); +int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type); +int gg_add_notify(struct gg_session *sess, uin_t uin); +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type); +int gg_remove_notify(struct gg_session *sess, uin_t uin); + +/* + * funkcje obs³ugi http. + */ +struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header); +int gg_http_watch_fd(struct gg_http *h); +void gg_http_stop(struct gg_http *h); +void gg_http_free(struct gg_http *h); +void gg_http_free_fields(struct gg_http *h); +#define gg_free_http gg_http_free + +/* + * struktury opisuj±ca kryteria wyszukiwania dla gg_search(). nieaktualne, + * zast±pione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI. + */ +struct gg_search_request { + int active; + unsigned int start; + char *nickname; + char *first_name; + char *last_name; + char *city; + int gender; + int min_birth; + int max_birth; + char *email; + char *phone; + uin_t uin; +}; + +struct gg_search { + int count; + struct gg_search_result *results; +}; + +struct gg_search_result { + uin_t uin; + char *first_name; + char *last_name; + char *nickname; + int born; + int gender; + char *city; + int active; +}; + +#define GG_GENDER_NONE 0 +#define GG_GENDER_FEMALE 1 +#define GG_GENDER_MALE 2 + +/* + * funkcje wyszukiwania. + */ +struct gg_http *gg_search(const struct gg_search_request *r, int async); +int gg_search_watch_fd(struct gg_http *f); +void gg_free_search(struct gg_http *f); +#define gg_search_free gg_free_search + +const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start); +const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start); +const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start); +const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start); +void gg_search_request_free(struct gg_search_request *r); + +/* + * funkcje obs³ugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje + * zachowuj± pewien poziom abstrakcji, ¿eby unikn±æ zmian ABI przy zmianach + * w protokole. + * + * NIE NALE¯Y SIÊ ODWO£YWAÆ DO PÓL gg_pubdir50_t BEZPO¦REDNIO! + */ +uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req); +gg_pubdir50_t gg_pubdir50_new(int type); +int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value); +int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq); +const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field); +int gg_pubdir50_type(gg_pubdir50_t res); +int gg_pubdir50_count(gg_pubdir50_t res); +uin_t gg_pubdir50_next(gg_pubdir50_t res); +uint32_t gg_pubdir50_seq(gg_pubdir50_t res); +void gg_pubdir50_free(gg_pubdir50_t res); + +#define GG_PUBDIR50_UIN "FmNumber" +#define GG_PUBDIR50_STATUS "FmStatus" +#define GG_PUBDIR50_FIRSTNAME "firstname" +#define GG_PUBDIR50_LASTNAME "lastname" +#define GG_PUBDIR50_NICKNAME "nickname" +#define GG_PUBDIR50_BIRTHYEAR "birthyear" +#define GG_PUBDIR50_CITY "city" +#define GG_PUBDIR50_GENDER "gender" +#define GG_PUBDIR50_GENDER_FEMALE "1" +#define GG_PUBDIR50_GENDER_MALE "2" +#define GG_PUBDIR50_GENDER_SET_FEMALE "2" +#define GG_PUBDIR50_GENDER_SET_MALE "1" +#define GG_PUBDIR50_ACTIVE "ActiveOnly" +#define GG_PUBDIR50_ACTIVE_TRUE "1" +#define GG_PUBDIR50_START "fmstart" +#define GG_PUBDIR50_FAMILYNAME "familyname" +#define GG_PUBDIR50_FAMILYCITY "familycity" + +int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length); + +/* + * struct gg_pubdir + * + * operacje na katalogu publicznym. + */ +struct gg_pubdir { + int success; /* czy siê uda³o */ + uin_t uin; /* otrzymany numerek. 0 je¶li b³±d */ +}; + +/* ogólne funkcje, nie powinny byæ u¿ywane */ +int gg_pubdir_watch_fd(struct gg_http *f); +void gg_pubdir_free(struct gg_http *f); +#define gg_free_pubdir gg_pubdir_free + +struct gg_token { + int width; /* szeroko¶æ obrazka */ + int height; /* wysoko¶æ obrazka */ + int length; /* ilo¶æ znaków w tokenie */ + char *tokenid; /* id tokenu */ +}; + +/* funkcje dotycz±ce tokenów */ +struct gg_http *gg_token(int async); +int gg_token_watch_fd(struct gg_http *h); +void gg_token_free(struct gg_http *h); + +/* rejestracja nowego numerka */ +struct gg_http *gg_register(const char *email, const char *password, int async); +struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async); +struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async); +#define gg_register_watch_fd gg_pubdir_watch_fd +#define gg_register_free gg_pubdir_free +#define gg_free_register gg_pubdir_free + +struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async); +struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async); +struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async); +#define gg_unregister_watch_fd gg_pubdir_watch_fd +#define gg_unregister_free gg_pubdir_free + +/* przypomnienie has³a e-mailem */ +struct gg_http *gg_remind_passwd(uin_t uin, int async); +struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async); +struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async); +#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd +#define gg_remind_passwd_free gg_pubdir_free +#define gg_free_remind_passwd gg_pubdir_free + +/* zmiana has³a */ +struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async); +struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async); +struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async); +struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async); +#define gg_change_passwd_free gg_pubdir_free +#define gg_free_change_passwd gg_pubdir_free + +/* + * struct gg_change_info_request + * + * opis ¿±dania zmiany informacji w katalogu publicznym. + */ +struct gg_change_info_request { + char *first_name; /* imiê */ + char *last_name; /* nazwisko */ + char *nickname; /* pseudonim */ + char *email; /* email */ + int born; /* rok urodzenia */ + int gender; /* p³eæ */ + char *city; /* miasto */ +}; + +struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city); +void gg_change_info_request_free(struct gg_change_info_request *r); + +struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async); +#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd +#define gg_change_pubdir_free gg_pubdir_free +#define gg_free_change_pubdir gg_pubdir_free + +/* + * funkcje dotycz±ce listy kontaktów na serwerze. + */ +struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async); +int gg_userlist_get_watch_fd(struct gg_http *f); +void gg_userlist_get_free(struct gg_http *f); + +struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async); +int gg_userlist_put_watch_fd(struct gg_http *f); +void gg_userlist_put_free(struct gg_http *f); + +struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async); +int gg_userlist_remove_watch_fd(struct gg_http *f); +void gg_userlist_remove_free(struct gg_http *f); + + + +/* + * funkcje dotycz±ce komunikacji miêdzy klientami. + */ +extern int gg_dcc_port; /* port, na którym nas³uchuje klient */ +extern unsigned long gg_dcc_ip; /* adres, na którym nas³uchuje klient */ + +int gg_dcc_request(struct gg_session *sess, uin_t uin); + +struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); +struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); +struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); +void gg_dcc_set_type(struct gg_dcc *d, int type); +int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename); +int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename); +int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length); + +#define GG_DCC_VOICE_FRAME_LENGTH 195 +#define GG_DCC_VOICE_FRAME_LENGTH_505 326 + +struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port); +#define gg_dcc_socket_free gg_free_dcc +#define gg_dcc_socket_watch_fd gg_dcc_watch_fd + +struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d); + +void gg_dcc_free(struct gg_dcc *c); +#define gg_free_dcc gg_dcc_free + +/* + * je¶li chcemy sobie podebugowaæ, wystarczy ustawiæ `gg_debug_level'. + * niestety w miarê przybywania wpisów `gg_debug(...)' nie chcia³o mi + * siê ustawiaæ odpowiednich leveli, wiêc wiêkszo¶æ sz³a do _MISC. + */ +extern int gg_debug_level; /* poziom debugowania. mapa bitowa sta³ych GG_DEBUG_* */ + +/* + * mo¿na podaæ wska¼nik do funkcji obs³uguj±cej wywo³ania gg_debug(). + * nieoficjalne, nieudokumentowane, mo¿e siê zmieniæ. je¶li kto¶ jest + * zainteresowany, niech da znaæ na ekg-devel. + */ +extern void (*gg_debug_handler)(int level, const char *format, va_list ap); + +/* + * mo¿na podaæ plik, do którego bêd± zapisywane teksty z gg_debug(). + */ +extern FILE *gg_debug_file; + +#define GG_DEBUG_NET 1 +#define GG_DEBUG_TRAFFIC 2 +#define GG_DEBUG_DUMP 4 +#define GG_DEBUG_FUNCTION 8 +#define GG_DEBUG_MISC 16 + +#ifdef GG_DEBUG_DISABLE +#define gg_debug(x, y...) do { } while(0) +#else +void gg_debug(int level, const char *format, ...); +#endif + +const char *gg_libgadu_version(void); + +/* + * konfiguracja http proxy. + */ +extern int gg_proxy_enabled; /* w³±cza obs³ugê proxy */ +extern char *gg_proxy_host; /* okre¶la adres serwera proxy */ +extern int gg_proxy_port; /* okre¶la port serwera proxy */ +extern char *gg_proxy_username; /* okre¶la nazwê u¿ytkownika przy autoryzacji serwera proxy */ +extern char *gg_proxy_password; /* okre¶la has³o u¿ytkownika przy autoryzacji serwera proxy */ +extern int gg_proxy_http_only; /* w³±cza obs³ugê proxy wy³±cznie dla us³ug HTTP */ + + +/* + * adres, z którego ¶lemy pakiety (np ³±czymy siê z serwerem) + * u¿ywany przy gg_connect() + */ +extern unsigned long gg_local_ip; +/* + * ------------------------------------------------------------------------- + * poni¿ej znajduj± siê wewnêtrzne sprawy biblioteki. zwyk³y klient nie + * powinien ich w ogóle ruszaæ, bo i nie ma po co. wszystko mo¿na za³atwiæ + * procedurami wy¿szego poziomu, których definicje znajduj± siê na pocz±tku + * tego pliku. + * ------------------------------------------------------------------------- + */ + +#ifdef __GG_LIBGADU_HAVE_PTHREAD +int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); +#endif + +#ifdef _WIN32 +int gg_thread_socket(int thread_id, int socket); +#endif + +int gg_resolve(int *fd, int *pid, const char *hostname); + +#ifdef __GNUC__ +char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +#else +char *gg_saprintf(const char *format, ...); +#endif + +char *gg_vsaprintf(const char *format, va_list ap); + +#define gg_alloc_sprintf gg_saprintf + +char *gg_get_line(char **ptr); + +int gg_connect(void *addr, int port, int async); +struct in_addr *gg_gethostbyname(const char *hostname); +char *gg_read_line(int sock, char *buf, int length); +void gg_chomp(char *line); +char *gg_urlencode(const char *str); +int gg_http_hash(const char *format, ...); +int gg_read(struct gg_session *sess, char *buf, int length); +int gg_write(struct gg_session *sess, const char *buf, int length); +void *gg_recv_packet(struct gg_session *sess); +int gg_send_packet(struct gg_session *sess, int type, ...); +unsigned int gg_login_hash(const unsigned char *password, unsigned int seed); +uint32_t gg_fix32(uint32_t x); +uint16_t gg_fix16(uint16_t x); +#define fix16 gg_fix16 +#define fix32 gg_fix32 +char *gg_proxy_auth(void); +char *gg_base64_encode(const char *buf); +char *gg_base64_decode(const char *buf); +int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq); + +#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" +#define GG_APPMSG_PORT 80 +#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl" +#define GG_PUBDIR_PORT 80 +#define GG_REGISTER_HOST "register.gadu-gadu.pl" +#define GG_REGISTER_PORT 80 +#define GG_REMIND_HOST "retr.gadu-gadu.pl" +#define GG_REMIND_PORT 80 + +#define GG_DEFAULT_PORT 8074 +#define GG_HTTPS_PORT 443 +#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)" + +#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158" +#define GG_DEFAULT_PROTOCOL_VERSION 0x24 +#define GG_DEFAULT_TIMEOUT 30 +#define GG_HAS_AUDIO_MASK 0x40000000 +#define GG_ERA_OMNIX_MASK 0x04000000 +#define GG_LIBGADU_VERSION "CVS" + +#define GG_DEFAULT_DCC_PORT 1550 + +struct gg_header { + uint32_t type; /* typ pakietu */ + uint32_t length; /* d³ugo¶æ reszty pakietu */ +} GG_PACKED; + +#define GG_WELCOME 0x0001 +#define GG_NEED_EMAIL 0x0014 + +struct gg_welcome { + uint32_t key; /* klucz szyfrowania has³a */ +} GG_PACKED; + +#define GG_LOGIN 0x000c + +struct gg_login { + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash has³a */ + uint32_t status; /* status na dzieñ dobry */ + uint32_t version; /* moja wersja klienta */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym s³ucham */ +} GG_PACKED; + +#define GG_LOGIN_EXT 0x0013 + +struct gg_login_ext { + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash has³a */ + uint32_t status; /* status na dzieñ dobry */ + uint32_t version; /* moja wersja klienta */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym s³ucham */ + uint32_t external_ip; /* zewnêtrzny adres ip */ + uint16_t external_port; /* zewnêtrzny port */ +} GG_PACKED; + +#define GG_LOGIN60 0x0015 + +struct gg_login60 { + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash has³a */ + uint32_t status; /* status na dzieñ dobry */ + uint32_t version; /* moja wersja klienta */ + uint8_t dunno1; /* 0x00 */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym s³ucham */ + uint32_t external_ip; /* zewnêtrzny adres ip */ + uint16_t external_port; /* zewnêtrzny port */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno2; /* 0xbe */ +} GG_PACKED; + +#define GG_LOGIN_OK 0x0003 + +#define GG_LOGIN_FAILED 0x0009 + +#define GG_PUBDIR50_REQUEST 0x0014 + +#define GG_PUBDIR50_WRITE 0x01 +#define GG_PUBDIR50_READ 0x02 +#define GG_PUBDIR50_SEARCH 0x03 +#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH +#define GG_PUBDIR50_SEARCH_REPLY 0x05 + +struct gg_pubdir50_request { + uint8_t type; /* GG_PUBDIR50_* */ + uint32_t seq; /* czas wys³ania zapytania */ +} GG_PACKED; + +#define GG_PUBDIR50_REPLY 0x000e + +struct gg_pubdir50_reply { + uint8_t type; /* GG_PUBDIR50_* */ + uint32_t seq; /* czas wys³ania zapytania */ +} GG_PACKED; + +#define GG_NEW_STATUS 0x0002 + +#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostêpny */ +#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostêpny z opisem (4.8) */ +#define GG_STATUS_AVAIL 0x0002 /* dostêpny */ +#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostêpny z opisem (4.9) */ +#define GG_STATUS_BUSY 0x0003 /* zajêty */ +#define GG_STATUS_BUSY_DESCR 0x0005 /* zajêty z opisem (4.8) */ +#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ +#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ +#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ + +#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ + +#define GG_STATUS_DESCR_MAXSIZE 70 + +/* + * makra do ³atwego i szybkiego sprawdzania stanu. + */ + +/* GG_S_F() tryb tylko dla znajomych */ +#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0) + +/* GG_S() stan bez uwzglêdnienia trybu tylko dla znajomych */ +#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) + +/* GG_S_A() dostêpny */ +#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) + +/* GG_S_NA() niedostêpny */ +#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) + +/* GG_S_B() zajêty */ +#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR) + +/* GG_S_I() niewidoczny */ +#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) + +/* GG_S_D() stan opisowy */ +#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) + +/* GG_S_BL() blokowany lub blokuj±cy */ +#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED) + +struct gg_new_status { + uint32_t status; /* na jaki zmieniæ? */ +} GG_PACKED; + +#define GG_NOTIFY_FIRST 0x000f +#define GG_NOTIFY_LAST 0x0010 + +#define GG_NOTIFY 0x0010 + +struct gg_notify { + uint32_t uin; /* numerek danej osoby */ + uint8_t dunno1; /* rodzaj wpisu w li¶cie */ +} GG_PACKED; + +#define GG_USER_OFFLINE 0x01 /* bêdziemy niewidoczni dla u¿ytkownika */ +#define GG_USER_NORMAL 0x03 /* zwyk³y u¿ytkownik */ +#define GG_USER_BLOCKED 0x04 /* zablokowany u¿ytkownik */ + +#define GG_LIST_EMPTY 0x0012 + +#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */ + +struct gg_notify_reply { + uint32_t uin; /* numerek */ + uint32_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym s³ucha klient */ + uint32_t version; /* wersja klienta */ + uint16_t dunno2; /* znowu port? */ +} GG_PACKED; + +#define GG_NOTIFY_REPLY60 0x0011 + +struct gg_notify_reply60 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym s³ucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ +} GG_PACKED; + +#define GG_STATUS60 0x000f + +struct gg_status60 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym s³ucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ +} GG_PACKED; + +#define GG_ADD_NOTIFY 0x000d +#define GG_REMOVE_NOTIFY 0x000e + +struct gg_add_remove { + uint32_t uin; /* numerek */ + uint8_t dunno1; /* bitmapa */ +} GG_PACKED; + +#define GG_STATUS 0x0002 + +struct gg_status { + uint32_t uin; /* numerek */ + uint32_t status; /* nowy stan */ +} GG_PACKED; + +#define GG_SEND_MSG 0x000b + +#define GG_CLASS_QUEUED 0x0001 +#define GG_CLASS_OFFLINE GG_CLASS_QUEUED +#define GG_CLASS_MSG 0x0004 +#define GG_CLASS_CHAT 0x0008 +#define GG_CLASS_CTCP 0x0010 +#define GG_CLASS_ACK 0x0020 +#define GG_CLASS_EXT GG_CLASS_ACK /* kompatybilno¶æ wstecz */ + +#define GG_MSG_MAXSIZE 2000 + +struct gg_send_msg { + uint32_t recipient; + uint32_t seq; + uint32_t msgclass; +} GG_PACKED; + +struct gg_msg_richtext { + uint8_t flag; + uint16_t length; +} GG_PACKED; + +struct gg_msg_richtext_format { + uint16_t position; + uint8_t font; +} GG_PACKED; + +struct gg_msg_richtext_image { + uint16_t unknown1; + uint32_t size; + uint32_t crc32; +} GG_PACKED; + +#define GG_FONT_BOLD 0x01 +#define GG_FONT_ITALIC 0x02 +#define GG_FONT_UNDERLINE 0x04 +#define GG_FONT_COLOR 0x08 +#define GG_FONT_IMAGE 0x80 + +struct gg_msg_richtext_color { + uint8_t red; + uint8_t green; + uint8_t blue; +} GG_PACKED; + +struct gg_msg_recipients { + uint8_t flag; + uint32_t count; +} GG_PACKED; + +struct gg_msg_image_request { + uint8_t flag; + uint32_t size; + uint32_t crc32; +} GG_PACKED; + +struct gg_msg_image_reply { + uint8_t flag; + uint32_t size; + uint32_t crc32; + /* char filename[]; */ + /* char image[]; */ +} GG_PACKED; + +#define GG_SEND_MSG_ACK 0x0005 + +#define GG_ACK_BLOCKED 0x0001 +#define GG_ACK_DELIVERED 0x0002 +#define GG_ACK_QUEUED 0x0003 +#define GG_ACK_MBOXFULL 0x0004 +#define GG_ACK_NOT_DELIVERED 0x0006 + +struct gg_send_msg_ack { + uint32_t status; + uint32_t recipient; + uint32_t seq; +} GG_PACKED; + +#define GG_RECV_MSG 0x000a + +struct gg_recv_msg { + uint32_t sender; + uint32_t seq; + uint32_t time; + uint32_t msgclass; +} GG_PACKED; + +#define GG_PING 0x0008 + +#define GG_PONG 0x0007 + +#define GG_DISCONNECTING 0x000b + +#define GG_USERLIST_REQUEST 0x0016 + +#define GG_USERLIST_PUT 0x00 +#define GG_USERLIST_PUT_MORE 0x01 +#define GG_USERLIST_GET 0x02 + +struct gg_userlist_request { + uint8_t type; +} GG_PACKED; + +#define GG_USERLIST_REPLY 0x0010 + +#define GG_USERLIST_PUT_REPLY 0x00 +#define GG_USERLIST_PUT_MORE_REPLY 0x02 +#define GG_USERLIST_GET_REPLY 0x06 +#define GG_USERLIST_GET_MORE_REPLY 0x04 + +struct gg_userlist_reply { + uint8_t type; +} GG_PACKED; + +/* + * pakiety, sta³e, struktury dla DCC + */ + +struct gg_dcc_tiny_packet { + uint8_t type; /* rodzaj pakietu */ +} GG_PACKED; + +struct gg_dcc_small_packet { + uint32_t type; /* rodzaj pakietu */ +} GG_PACKED; + +struct gg_dcc_big_packet { + uint32_t type; /* rodzaj pakietu */ + uint32_t dunno1; /* niewiadoma */ + uint32_t dunno2; /* niewiadoma */ +} GG_PACKED; + +/* + * póki co, nie znamy dok³adnie protoko³u. nie wiemy, co czemu odpowiada. + * nazwy s± niepowa¿ne i tymczasowe. + */ +#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */ +#define GG_DCC_HAVE_FILE 0x0001 /* wiêc mu damy */ +#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */ +#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */ +#define GG_DCC_CATCH_FILE 0x0002 /* wysy³amy plik */ + +#define GG_DCC_FILEATTR_READONLY 0x0020 + +#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */ +#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */ +#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */ +#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */ + +#ifdef __cplusplus +} +#ifdef _WIN32 +#pragma pack(pop) +#endif +#endif + +#endif /* __GG_LIBGADU_H */ + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/pubdir.c b/kopete/protocols/gadu/libgadu/pubdir.c new file mode 100644 index 00000000..7ed545ff --- /dev/null +++ b/kopete/protocols/gadu/libgadu/pubdir.c @@ -0,0 +1,689 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Dawid Jarosz <dawjar@poczta.onet.pl> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libgadu.h" + +/* + * gg_register3() + * + * rozpoczyna rejestracjê u¿ytkownika protoko³em GG 6.0. wymaga wcze¶niejszego + * pobrania tokenu za pomoc± funkcji gg_token(). + * + * - email - adres e-mail klienta + * - password - has³o klienta + * - tokenid - identyfikator tokenu + * - tokenval - warto¶æ tokenu + * - async - po³±czenie asynchroniczne + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ + * funkcj± gg_register_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async) +{ + struct gg_http *h; + char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query; + + if (!email || !password || !tokenid || !tokenval) { + gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n"); + errno = EFAULT; + return NULL; + } + + __pwd = gg_urlencode(password); + __email = gg_urlencode(email); + __tokenid = gg_urlencode(tokenid); + __tokenval = gg_urlencode(tokenval); + + if (!__pwd || !__email || !__tokenid || !__tokenval) { + gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n"); + free(__pwd); + free(__email); + free(__tokenid); + free(__tokenval); + return NULL; + } + + form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", + __pwd, __email, __tokenid, __tokenval, + gg_http_hash("ss", email, password)); + + free(__pwd); + free(__email); + free(__tokenid); + free(__tokenval); + + if (!form) { + gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n"); + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form); + + query = gg_saprintf( + "Host: " GG_REGISTER_HOST "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Content-Length: %d\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s", + (int) strlen(form), form); + + free(form); + + if (!query) { + gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n"); + return NULL; + } + + if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { + gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n"); + free(query); + return NULL; + } + + h->type = GG_SESSION_REGISTER; + + free(query); + + h->callback = gg_pubdir_watch_fd; + h->destroy = gg_pubdir_free; + + if (!async) + gg_pubdir_watch_fd(h); + + return h; +} + +/* + * gg_unregister3() + * + * usuwa konto u¿ytkownika z serwera protoko³em GG 6.0 + * + * - uin - numerek GG + * - password - has³o klienta + * - tokenid - identyfikator tokenu + * - tokenval - warto¶æ tokenu + * - async - po³±czenie asynchroniczne + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ + * funkcj± gg_unregister_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async) +{ + struct gg_http *h; + char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query; + + if (!password || !tokenid || !tokenval) { + gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n"); + errno = EFAULT; + return NULL; + } + + __pwd = gg_saprintf("%ld", random()); + __fmpwd = gg_urlencode(password); + __tokenid = gg_urlencode(tokenid); + __tokenval = gg_urlencode(tokenval); + + if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) { + gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n"); + free(__pwd); + free(__fmpwd); + free(__tokenid); + free(__tokenval); + return NULL; + } + + form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval, gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd)); + + free(__fmpwd); + free(__pwd); + free(__tokenid); + free(__tokenval); + + if (!form) { + gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n"); + return NULL; + } + + gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form); + + query = gg_saprintf( + "Host: " GG_REGISTER_HOST "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Content-Length: %d\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s", + (int) strlen(form), form); + + free(form); + + if (!query) { + gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n"); + return NULL; + } + + if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { + gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n"); + free(query); + return NULL; + } + + h->type = GG_SESSION_UNREGISTER; + + free(query); + + h->callback = gg_pubdir_watch_fd; + h->destroy = gg_pubdir_free; + + if (!async) + gg_pubdir_watch_fd(h); + + return h; +} + +/* + * gg_change_passwd4() + * + * wysy³a ¿±danie zmiany has³a zgodnie z protoko³em GG 6.0. wymaga + * wcze¶niejszego pobrania tokenu za pomoc± funkcji gg_token(). + * + * - uin - numer + * - email - adres e-mail + * - passwd - stare has³o + * - newpasswd - nowe has³o + * - tokenid - identyfikator tokenu + * - tokenval - warto¶æ tokenu + * - async - po³±czenie asynchroniczne + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ + * funkcj± gg_change_passwd_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async) +{ + struct gg_http *h; + char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval; + + if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) { + gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n"); + errno = EFAULT; + return NULL; + } + + __fmpwd = gg_urlencode(passwd); + __pwd = gg_urlencode(newpasswd); + __email = gg_urlencode(email); + __tokenid = gg_urlencode(tokenid); + __tokenval = gg_urlencode(tokenval); + + if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) { + gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n"); + free(__fmpwd); + free(__pwd); + free(__email); + free(__tokenid); + free(__tokenval); + return NULL; + } + + if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd)))) { + gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n"); + free(__fmpwd); + free(__pwd); + free(__email); + free(__tokenid); + free(__tokenval); + + return NULL; + } + + free(__fmpwd); + free(__pwd); + free(__email); + free(__tokenid); + free(__tokenval); + + gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form); + + query = gg_saprintf( + "Host: " GG_REGISTER_HOST "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Content-Length: %d\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s", + (int) strlen(form), form); + + free(form); + + if (!query) { + gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n"); + return NULL; + } + + if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) { + gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n"); + free(query); + return NULL; + } + + h->type = GG_SESSION_PASSWD; + + free(query); + + h->callback = gg_pubdir_watch_fd; + h->destroy = gg_pubdir_free; + + if (!async) + gg_pubdir_watch_fd(h); + + return h; +} + +/* + * gg_remind_passwd3() + * + * wysy³a ¿±danie przypomnienia has³a e-mailem. + * + * - uin - numer + * - email - adres e-mail taki, jak ten zapisany na serwerze + * - async - po³±czenie asynchroniczne + * - tokenid - identyfikator tokenu + * - tokenval - warto¶æ tokenu + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ + * funkcj± gg_remind_passwd_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async) +{ + struct gg_http *h; + char *form, *query, *__tokenid, *__tokenval, *__email; + + if (!tokenid || !tokenval || !email) { + gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n"); + errno = EFAULT; + return NULL; + } + + __tokenid = gg_urlencode(tokenid); + __tokenval = gg_urlencode(tokenval); + __email = gg_urlencode(email); + + if (!__tokenid || !__tokenval || !__email) { + gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); + free(__tokenid); + free(__tokenval); + free(__email); + return NULL; + } + + if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) { + gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n"); + free(__tokenid); + free(__tokenval); + free(__email); + return NULL; + } + + free(__tokenid); + free(__tokenval); + free(__email); + + gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form); + + query = gg_saprintf( + "Host: " GG_REMIND_HOST "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Content-Length: %d\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s", + (int) strlen(form), form); + + free(form); + + if (!query) { + gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n"); + return NULL; + } + + if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) { + gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n"); + free(query); + return NULL; + } + + h->type = GG_SESSION_REMIND; + + free(query); + + h->callback = gg_pubdir_watch_fd; + h->destroy = gg_pubdir_free; + + if (!async) + gg_pubdir_watch_fd(h); + + return h; +} + +/* + * gg_pubdir_watch_fd() + * + * przy asynchronicznych operacjach na katalogu publicznym nale¿y wywo³ywaæ + * tê funkcjê przy zmianach na obserwowanym deskryptorze. + * + * - h - struktura opisuj±ca po³±czenie + * + * je¶li wszystko posz³o dobrze to 0, inaczej -1. operacja bêdzie + * zakoñczona, je¶li h->state == GG_STATE_DONE. je¶li wyst±pi jaki¶ + * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error. + */ +int gg_pubdir_watch_fd(struct gg_http *h) +{ + struct gg_pubdir *p; + char *tmp; + + if (!h) { + errno = EFAULT; + return -1; + } + + if (h->state == GG_STATE_ERROR) { + gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n"); + errno = EINVAL; + return -1; + } + + if (h->state != GG_STATE_PARSING) { + if (gg_http_watch_fd(h) == -1) { + gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n"); + errno = EINVAL; + return -1; + } + } + + if (h->state != GG_STATE_PARSING) + return 0; + + h->state = GG_STATE_DONE; + + if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) { + gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n"); + return -1; + } + + p->success = 0; + p->uin = 0; + + gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body); + + if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) { + p->success = 1; + p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0); + gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin); + } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { + p->success = 1; + if (tmp[7] == ':') + p->uin = strtol(tmp + 8, NULL, 0); + gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin); + } else + gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n"); + + return 0; +} + +/* + * gg_pubdir_free() + * + * zwalnia pamiêæ po efektach operacji na katalogu publicznym. + * + * - h - zwalniana struktura + */ +void gg_pubdir_free(struct gg_http *h) +{ + if (!h) + return; + + free(h->data); + gg_http_free(h); +} + +/* + * gg_token() + * + * pobiera z serwera token do autoryzacji zak³adania konta, usuwania + * konta i zmiany has³a. + * + * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ + * funkcj± gg_token_free(), albo NULL je¶li wyst±pi³ b³±d. + */ +struct gg_http *gg_token(int async) +{ + struct gg_http *h; + const char *query; + + query = "Host: " GG_REGISTER_HOST "\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Content-Length: 0\r\n" + "Pragma: no-cache\r\n" + "\r\n"; + + if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) { + gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n"); + return NULL; + } + + h->type = GG_SESSION_TOKEN; + + h->callback = gg_token_watch_fd; + h->destroy = gg_token_free; + + if (!async) + gg_token_watch_fd(h); + + return h; +} + +/* + * gg_token_watch_fd() + * + * przy asynchronicznych operacjach zwi±zanych z tokenem nale¿y wywo³ywaæ + * tê funkcjê przy zmianach na obserwowanym deskryptorze. + * + * - h - struktura opisuj±ca po³±czenie + * + * je¶li wszystko posz³o dobrze to 0, inaczej -1. operacja bêdzie + * zakoñczona, je¶li h->state == GG_STATE_DONE. je¶li wyst±pi jaki¶ + * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error. + */ +int gg_token_watch_fd(struct gg_http *h) +{ + if (!h) { + errno = EFAULT; + return -1; + } + + if (h->state == GG_STATE_ERROR) { + gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n"); + errno = EINVAL; + return -1; + } + + if (h->state != GG_STATE_PARSING) { + if (gg_http_watch_fd(h) == -1) { + gg_debug(GG_DEBUG_MISC, "=> token, http failure\n"); + errno = EINVAL; + return -1; + } + } + + if (h->state != GG_STATE_PARSING) + return 0; + + /* je¶li h->data jest puste, to ¶ci±gali¶my tokenid i url do niego, + * ale je¶li co¶ tam jest, to znaczy, ¿e mamy drugi etap polegaj±cy + * na pobieraniu tokenu. */ + if (!h->data) { + int width, height, length; + char *url = NULL, *tokenid = NULL, *path, *headers; + const char *host; + struct gg_http *h2; + struct gg_token *t; + + gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body); + + if (h->body && (!(url = malloc(strlen(h->body))) || !(tokenid = malloc(strlen(h->body))))) { + gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n"); + free(url); + return -1; + } + + if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) { + gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n"); + free(url); + free(tokenid); + errno = EINVAL; + return -1; + } + + /* dostali¶my tokenid i wszystkie niezbêdne informacje, + * wiêc pobierzmy obrazek z tokenem */ + + if (strncmp(url, "http://", 7)) { + path = gg_saprintf("%s?tokenid=%s", url, tokenid); + host = GG_REGISTER_HOST; + } else { + char *slash = strchr(url + 7, '/'); + + if (slash) { + path = gg_saprintf("%s?tokenid=%s", slash, tokenid); + *slash = 0; + host = url + 7; + } else { + gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n"); + free(url); + free(tokenid); + errno = EINVAL; + return -1; + } + } + + if (!path) { + gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); + free(url); + free(tokenid); + return -1; + } + + if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) { + gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n"); + free(path); + free(url); + free(tokenid); + return -1; + } + + if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) { + gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n"); + free(headers); + free(url); + free(path); + free(tokenid); + return -1; + } + + free(headers); + free(path); + free(url); + + memcpy(h, h2, sizeof(struct gg_http)); + free(h2); + + h->type = GG_SESSION_TOKEN; + + h->callback = gg_token_watch_fd; + h->destroy = gg_token_free; + + if (!h->async) + gg_token_watch_fd(h); + + if (!(h->data = t = malloc(sizeof(struct gg_token)))) { + gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n"); + free(tokenid); + return -1; + } + + t->width = width; + t->height = height; + t->length = length; + t->tokenid = tokenid; + } else { + /* obrazek mamy w h->body */ + h->state = GG_STATE_DONE; + } + + return 0; +} + +/* + * gg_token_free() + * + * zwalnia pamiêæ po efektach pobierania tokenu. + * + * - h - zwalniana struktura + */ +void gg_token_free(struct gg_http *h) +{ + struct gg_token *t; + + if (!h) + return; + + if ((t = h->data)) + free(t->tokenid); + + free(h->data); + gg_http_free(h); +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/libgadu/pubdir50.c b/kopete/protocols/gadu/libgadu/pubdir50.c new file mode 100644 index 00000000..6ef285e7 --- /dev/null +++ b/kopete/protocols/gadu/libgadu/pubdir50.c @@ -0,0 +1,467 @@ +/* $Id$ */ + +/* + * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "libgadu.h" + +/* + * gg_pubdir50_new() + * + * tworzy now± zmienn± typu gg_pubdir50_t. + * + * zaalokowana zmienna lub NULL w przypadku braku pamiêci. + */ +gg_pubdir50_t gg_pubdir50_new(int type) +{ + gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s)); + + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type); + + if (!res) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n"); + return NULL; + } + + memset(res, 0, sizeof(struct gg_pubdir50_s)); + + res->type = type; + + return res; +} + +/* + * gg_pubdir50_add_n() // funkcja wewnêtrzna + * + * funkcja dodaje lub zastêpuje istniej±ce pole do zapytania lub odpowiedzi. + * + * - req - wska¼nik opisu zapytania, + * - num - numer wyniku (0 dla zapytania), + * - field - nazwa pola, + * - value - warto¶æ pola, + * + * 0/-1 + */ +static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value) +{ + struct gg_pubdir50_entry *tmp = NULL, *entry; + char *dupfield, *dupvalue; + int i; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value); + + if (!(dupvalue = strdup(value))) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); + return -1; + } + + for (i = 0; i < req->entries_count; i++) { + if (req->entries[i].num != num || strcmp(req->entries[i].field, field)) + continue; + + free(req->entries[i].value); + req->entries[i].value = dupvalue; + + return 0; + } + + if (!(dupfield = strdup(field))) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); + free(dupvalue); + return -1; + } + + if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n"); + free(dupfield); + free(dupvalue); + return -1; + } + + req->entries = tmp; + + entry = &req->entries[req->entries_count]; + entry->num = num; + entry->field = dupfield; + entry->value = dupvalue; + + req->entries_count++; + + return 0; +} + +/* + * gg_pubdir50_add() + * + * funkcja dodaje pole do zapytania. + * + * - req - wska¼nik opisu zapytania, + * - field - nazwa pola, + * - value - warto¶æ pola, + * + * 0/-1 + */ +int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value) +{ + return gg_pubdir50_add_n(req, 0, field, value); +} + +/* + * gg_pubdir50_seq_set() + * + * ustawia numer sekwencyjny zapytania. + * + * - req - zapytanie, + * - seq - nowy numer sekwencyjny. + * + * 0/-1. + */ +int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq) +{ + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq); + + if (!req) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + req->seq = seq; + + return 0; +} + +/* + * gg_pubdir50_free() + * + * zwalnia pamiêæ po zapytaniu lub rezultacie szukania u¿ytkownika. + * + * - s - zwalniana zmienna, + */ +void gg_pubdir50_free(gg_pubdir50_t s) +{ + int i; + + if (!s) + return; + + for (i = 0; i < s->entries_count; i++) { + free(s->entries[i].field); + free(s->entries[i].value); + } + + free(s->entries); + free(s); +} + +/* + * gg_pubdir50() + * + * wysy³a zapytanie katalogu publicznego do serwera. + * + * - sess - sesja, + * - req - zapytanie. + * + * numer sekwencyjny wyszukiwania lub 0 w przypadku b³êdu. + */ +uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) +{ + int i, size = 5; + uint32_t res; + char *buf, *p; + struct gg_pubdir50_request *r; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); + + if (!sess || !req) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); + errno = EFAULT; + return 0; + } + + if (sess->state != GG_STATE_CONNECTED) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); + errno = ENOTCONN; + return 0; + } + + for (i = 0; i < req->entries_count; i++) { + /* wyszukiwanie bierze tylko pierwszy wpis */ + if (req->entries[i].num) + continue; + + size += strlen(req->entries[i].field) + 1; + size += strlen(req->entries[i].value) + 1; + } + + if (!(buf = malloc(size))) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); + return 0; + } + + r = (struct gg_pubdir50_request*) buf; + res = time(NULL); + r->type = req->type; + r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL)); + req->seq = gg_fix32(r->seq); + + for (i = 0, p = buf + 5; i < req->entries_count; i++) { + if (req->entries[i].num) + continue; + + strcpy(p, req->entries[i].field); + p += strlen(p) + 1; + + strcpy(p, req->entries[i].value); + p += strlen(p) + 1; + } + + if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1) + res = 0; + + free(buf); + + return res; +} + +/* + * gg_pubdir50_handle_reply() // funkcja wewnêtrzna + * + * analizuje przychodz±cy pakiet odpowiedzi i zapisuje wynik w struct gg_event. + * + * - e - opis zdarzenia + * - packet - zawarto¶æ pakietu odpowiedzi + * - length - d³ugo¶æ pakietu odpowiedzi + * + * 0/-1 + */ +int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) +{ + const char *end = packet + length, *p; + struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; + gg_pubdir50_t res; + int num = 0; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length); + + if (!e || !packet) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (length < 5) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n"); + errno = EINVAL; + return -1; + } + + if (!(res = gg_pubdir50_new(r->type))) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n"); + return -1; + } + + e->event.pubdir50 = res; + + res->seq = gg_fix32(r->seq); + + switch (res->type) { + case GG_PUBDIR50_READ: + e->type = GG_EVENT_PUBDIR50_READ; + break; + + case GG_PUBDIR50_WRITE: + e->type = GG_EVENT_PUBDIR50_WRITE; + break; + + default: + e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY; + break; + } + + /* brak wyników? */ + if (length == 5) + return 0; + + /* pomiñ pocz±tek odpowiedzi */ + p = packet + 5; + + while (p < end) { + const char *field, *value; + + field = p; + + /* sprawd¼, czy nie mamy podzia³u na kolejne pole */ + if (!*field) { + num++; + field++; + } + + value = NULL; + + for (p = field; p < end; p++) { + /* je¶li mamy koniec tekstu... */ + if (!*p) { + /* ...i jeszcze nie mieli¶my warto¶ci pola to + * wiemy, ¿e po tym zerze jest warto¶æ... */ + if (!value) + value = p + 1; + else + /* ...w przeciwym wypadku koniec + * warto¶ci i mo¿emy wychodziæ + * grzecznie z pêtli */ + break; + } + } + + /* sprawd¼my, czy pole nie wychodzi poza pakiet, ¿eby nie + * mieæ segfaultów, je¶li serwer przestanie zakañczaæ pakietów + * przez \0 */ + + if (p == end) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n"); + goto failure; + } + + p++; + + /* je¶li dostali¶my namier na nastêpne wyniki, to znaczy ¿e + * mamy koniec wyników i nie jest to kolejna osoba. */ + if (!strcasecmp(field, "nextstart")) { + res->next = atoi(value); + num--; + } else { + if (gg_pubdir50_add_n(res, num, field, value) == -1) + goto failure; + } + } + + res->count = num + 1; + + return 0; + +failure: + gg_pubdir50_free(res); + return -1; +} + +/* + * gg_pubdir50_get() + * + * pobiera informacjê z rezultatu wyszukiwania. + * + * - res - rezultat wyszukiwania, + * - num - numer odpowiedzi, + * - field - nazwa pola (wielko¶æ liter nie ma znaczenia). + * + * warto¶æ pola lub NULL, je¶li nie znaleziono. + */ +const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field) +{ + char *value = NULL; + int i; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field); + + if (!res || num < 0 || !field) { + gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n"); + errno = EINVAL; + return NULL; + } + + for (i = 0; i < res->entries_count; i++) { + if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) { + value = res->entries[i].value; + break; + } + } + + return value; +} + +/* + * gg_pubdir50_count() + * + * zwraca ilo¶æ wyników danego zapytania. + * + * - res - odpowied¼ + * + * ilo¶æ lub -1 w przypadku b³êdu. + */ +int gg_pubdir50_count(gg_pubdir50_t res) +{ + return (!res) ? -1 : res->count; +} + +/* + * gg_pubdir50_type() + * + * zwraca rodzaj zapytania lub odpowiedzi. + * + * - res - zapytanie lub odpowied¼ + * + * ilo¶æ lub -1 w przypadku b³êdu. + */ +int gg_pubdir50_type(gg_pubdir50_t res) +{ + return (!res) ? -1 : res->type; +} + +/* + * gg_pubdir50_next() + * + * zwraca numer, od którego nale¿y rozpocz±æ kolejne wyszukiwanie, je¶li + * zale¿y nam na kolejnych wynikach. + * + * - res - odpowied¼ + * + * numer lub -1 w przypadku b³êdu. + */ +uin_t gg_pubdir50_next(gg_pubdir50_t res) +{ + return (!res) ? (unsigned) -1 : res->next; +} + +/* + * gg_pubdir50_seq() + * + * zwraca numer sekwencyjny zapytania lub odpowiedzi. + * + * - res - zapytanie lub odpowied¼ + * + * numer lub -1 w przypadku b³êdu. + */ +uint32_t gg_pubdir50_seq(gg_pubdir50_t res) +{ + return (!res) ? (unsigned) -1 : res->seq; +} + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: notnil + * End: + * + * vim: shiftwidth=8: + */ diff --git a/kopete/protocols/gadu/ui/Makefile.am b/kopete/protocols/gadu/ui/Makefile.am new file mode 100644 index 00000000..12ee702a --- /dev/null +++ b/kopete/protocols/gadu/ui/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO +AM_CPPFLAGS = $(KOPETE_INCLUDES) \ + -I$(srcdir)/.. \ + $(all_includes) + +noinst_LTLIBRARIES = libgaduui.la + +libgaduui_la_SOURCES = gaduadd.ui gadusearch.ui gadueditaccountui.ui gaduawayui.ui gaduregisteraccountui.ui \ + empty.cpp + +EXTRA_DIST = gaduadd.ui gadusearch.ui gadueditaccountui.ui gaduawayui.ui gaduregisteraccountui.ui \ + empty.cpp diff --git a/kopete/protocols/gadu/ui/empty.cpp b/kopete/protocols/gadu/ui/empty.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/kopete/protocols/gadu/ui/empty.cpp diff --git a/kopete/protocols/gadu/ui/gaduadd.ui b/kopete/protocols/gadu/ui/gaduadd.ui new file mode 100644 index 00000000..f8d673b6 --- /dev/null +++ b/kopete/protocols/gadu/ui/gaduadd.ui @@ -0,0 +1,360 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>GaduAddUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GaduAddUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>394</width> + <height>340</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout39</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Gadu-Gadu &UIN:</string> + </property> + <property name="textFormat"> + <enum>AutoText</enum> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + <property name="alignment"> + <set>AlignVCenter</set> + </property> + <property name="buddy" stdset="0"> + <cstring>addEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the Gadu-Gadu account you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the Gadu-Gadu account you would like to add. This should be in the form of a number (no decimals, no spaces). This field is mandatory.</string> + </property> + </widget> + <widget class="KRestrictedLine"> + <property name="name"> + <cstring>addEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the Gadu-Gadu account you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the Gadu-Gadu account you would like to add. This should be in the form of a number (no decimals, no spaces). This field is mandatory.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string><i>(for example: 1234567)</i></string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Forename:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>fornameEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The forename of the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The forename (first name) of the contact you wish to add. Optionally this may include a middle name.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1_2</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Surname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>snameEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The surname of the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The surname (last name) of the contact you wish to add.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>N&ickname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>nickEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>A nickname for the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>A nickname for the contact you wish to add.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1_4</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Email address:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>emailEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1_4_2</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Telephone number:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>emailEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>fornameEdit_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>The forename of the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The forename (first name) of the contact you wish to add. Optionally this may include a middle name.</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>snameEdit_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>The surname of the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The surname (last name) of the contact you wish to add.</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>nickEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>A nickname for the contact you wish to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>A nickname for the contact you wish to add.</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>emailEdit_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>telephoneEdit_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>E-Mail address for this contact.</string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QCheckBox" row="4" column="0"> + <property name="name"> + <cstring>notAFriend_</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Offline to contact when you set "&Just for friends"</string> + </property> + <property name="toolTip" stdset="0"> + <string>Check if you want to exclude this contact from the "Just for friends" status mode.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check if you want to exclude this contact from the "Just for friends" status mode.</string> + </property> + </widget> + <widget class="QListView" row="3" column="0"> + <column> + <property name="text"> + <string>Group</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <item> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + </item> + <item> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + </item> + <property name="name"> + <cstring>groups</cstring> + </property> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>krestrictedline.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/gadu/ui/gaduawayui.ui b/kopete/protocols/gadu/ui/gaduawayui.ui new file mode 100644 index 00000000..3e475bce --- /dev/null +++ b/kopete/protocols/gadu/ui/gaduawayui.ui @@ -0,0 +1,194 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>GaduAwayUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GaduAwayUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>332</width> + <height>188</height> + </rect> + </property> + <property name="backgroundOrigin"> + <enum>WidgetOrigin</enum> + </property> + <property name="caption"> + <string>Away Dialog</string> + </property> + <property name="focusPolicy"> + <enum>TabFocus</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>statusGroup_</cstring> + </property> + <property name="title"> + <string>Status</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Choose status, by default present status is selected. +So all you need to do is just to type in your description. +Choosing Offline status will disconnect you, with given description.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>onlineButton_</cstring> + </property> + <property name="text"> + <string>O&nline</string> + </property> + <property name="buttonGroupId"> + <number>4</number> + </property> + <property name="toolTip" stdset="0"> + <string>Set your status to Online.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Set your status to Online, indicating that you are available to chat with anyone who wishes.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>awayButton_</cstring> + </property> + <property name="text"> + <string>&Busy</string> + </property> + <property name="buttonGroupId"> + <number>5</number> + </property> + <property name="toolTip" stdset="0"> + <string>Set your status to busy.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Set your status to busy, indicating that you may should not be bothered with trivial chat, and may not be able to reply immediately.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>invisibleButton_</cstring> + </property> + <property name="text"> + <string>&Invisible</string> + </property> + <property name="buttonGroupId"> + <number>22</number> + </property> + <property name="toolTip" stdset="0"> + <string>Set status to invisible, which will hide your presence from other users.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Set status to invisible, which will hide your presence from other users (who will see you as offline). However you may still chat, and see the online presence of others.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>offlineButton_</cstring> + </property> + <property name="text"> + <string>O&ffline</string> + </property> + <property name="buttonGroupId"> + <number>21</number> + </property> + <property name="toolTip" stdset="0"> + <string>Choose this status to disconnect with description entered below.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Choose this status to disconnect with description entered below.</string> + </property> + </widget> + </vbox> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout278</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>&Message:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>textEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Description of your status.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Description of your status (up to 70 characters).</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>textEdit_</cstring> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="maxLength"> + <number>70</number> + </property> + <property name="toolTip" stdset="0"> + <string>Description of your status.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Description of your status (up to 70 characters).</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>textEdit_</tabstop> + <tabstop>onlineButton_</tabstop> + <tabstop>awayButton_</tabstop> + <tabstop>invisibleButton_</tabstop> + <tabstop>offlineButton_</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/gadu/ui/gadueditaccountui.ui b/kopete/protocols/gadu/ui/gadueditaccountui.ui new file mode 100644 index 00000000..01edaa08 --- /dev/null +++ b/kopete/protocols/gadu/ui/gadueditaccountui.ui @@ -0,0 +1,873 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>GaduAccountEditUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GaduAccountEditUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>580</width> + <height>390</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="caption"> + <string>Account Preferences - Gadu-Gadu</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget4</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>B&asic Setup</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox63</cstring> + </property> + <property name="title"> + <string>Account Information</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Gadu-Gadu &UIN:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>loginEdit_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of your Gadu-Gadu account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of your Gadu-Gadu account. This should be in the form of a number (no decimals, no spaces).</string> + </property> + </widget> + <widget class="KLineEdit"> + <property name="name"> + <cstring>loginEdit_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="maxLength"> + <number>16</number> + </property> + <property name="edited"> + <bool>true</bool> + </property> + <property name="dragEnabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of your Gadu-Gadu account.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of your Gadu-Gadu account. This should be in the form of a number (no decimals, no spaces).</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Kopete::UI::PasswordWidget"> + <property name="name"> + <cstring>passwordWidget_</cstring> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>autoLoginCheck_</cstring> + </property> + <property name="text"> + <string>E&xclude from connect all</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check to disable automatic connection. If checked, you may connect to this account manually using the icon in the bottom of the main Kopete window.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Registration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>To connect to the Gadu-Gadu network, you will need a Gadu-Gadu account.<br><br> +If you do not currently have an account, please click the button to create one.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>registerNew</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Re&gister New Account</string> + </property> + <property name="toolTip" stdset="0"> + <string>Register a new account on this network.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Register a new account on this network.</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer15</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>80</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>A&ccount Preferences</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>160</height> + </size> + </property> + </spacer> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox64</cstring> + </property> + <property name="title"> + <string>Connection Preferences</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>dccCheck_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Use direct connections (DCC)</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget" row="3" column="0"> + <property name="name"> + <cstring>layout65</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Use protocol encr&yption (SSL):</string> + </property> + <property name="buddy" stdset="0"> + <cstring>useTls_</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Whether or not you want to enable SSL encrypted communication with the server.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Whether or not you want to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>If Available</string> + </property> + </item> + <item> + <property name="text"> + <string>Required</string> + </property> + </item> + <item> + <property name="text"> + <string>Do Not Use</string> + </property> + </item> + <property name="name"> + <cstring>useTls_</cstring> + </property> + <property name="autoCompletion"> + <bool>false</bool> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Whether or not you want to enable SSL encrypted communication with the server.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Whether or not you want to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>cacheServersCheck__</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>C&ache server information</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Cache connection information for each server connected to in case the main load-balancing server fails.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This option is used whenever the primary Gadu-Gadu load-balancing server fails. If this is checked, Kopete will try to connect to the actual servers directly using cached information about them. This prevents connection errors when the main load-balancing server does not answer. In practice it only helps very rarely.</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>ignoreCheck_</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Ignore people off your contact list</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>U&ser Information</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>connectLabel</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="paletteForegroundColor"> + <color> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string><p align="center">You must be connected to change your Personal Information.</p></string> + </property> + </widget> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>userInformation</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>User Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>uiSurnamea</cstring> + </property> + <property name="text"> + <string>Surname:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3</cstring> + </property> + <property name="text"> + <string>Your nick name:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3_2</cstring> + </property> + <property name="text"> + <string>Gender:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3_2_2</cstring> + </property> + <property name="text"> + <string>Year of birth:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3_2_3</cstring> + </property> + <property name="text"> + <string>City:</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiName</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiSurname</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>nickName</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string></string> + </property> + </item> + <item> + <property name="text"> + <string>Female</string> + </property> + </item> + <item> + <property name="text"> + <string>Male</string> + </property> + </item> + <property name="name"> + <cstring>uiGender</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiYOB</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiCity</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_5_3_2_4_2</cstring> + </property> + <property name="text"> + <string>Values below are going to be used in search, but will not appear in results.</string> + </property> + </widget> + <spacer row="1" column="1"> + <property name="name"> + <cstring>spacer15_3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3_2_4</cstring> + </property> + <property name="text"> + <string>Maiden name:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_5_3_2_4_3</cstring> + </property> + <property name="text"> + <string>City of origin:</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiMeiden</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>uiOrgin</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>&File Transfer</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>dcc</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="title"> + <string>Global DCC Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string><qt><p align="center"><font color="#ff0000">These options affect <b>all</b> Gadu-Gadu accounts.</font></p></qt></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>optionOverrideDCC</cstring> + </property> + <property name="text"> + <string>&Override default configuration</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout33</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout32</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>3</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Local &IP address /</string> + </property> + <property name="buddy" stdset="0"> + <cstring>ipAddress</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>po&rt:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>dccPort</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="KRestrictedLine"> + <property name="name"> + <cstring>ipAddress</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>0.0.0.0</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>dccPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>65535</number> + </property> + <property name="value"> + <number>8010</number> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer9</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>180</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>labelStatusMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </hbox> +</widget> +<customwidgets> + <customwidget> + <class>Kopete::UI::PasswordWidget</class> + <header location="local">kopetepasswordwidget.h</header> + <sizehint> + <width>50</width> + <height>50</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>changed()</signal> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="866">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000032949444154388db59531681b6714c77f32373c8186ef0305eea005093258900eca26d30e3174a8a807d1c9ee940e5d4a276f09a414e22974ee609a4c75a0857a70a20c199ce93424e43414aee0c26910dc8105f7410df706413a7c915551db049a3e38b87bf7bedffddfc7ff7d578be398456c6c6cbce13d441cc7b5da02fcf4e8e99bde7a8f899b501515d959f64e10e71cd949c6e8d508e6cb7cb050fae49727444d87ed08a566dc0cea545a621b96725e62c522f312c4929ff9e7725e6203439282ec0bc72f74150c30c927d89690163f539619a044564973a1980ae54c01c136a1db518a0024808942780dead16a27e7e0ca55949a81668023b242fcd2901c394663072cd408ad75e18b6d43a7076143710aa1b9049ccd326e064a5979e8f0191cfc5878544368af1b24807caa4cfe507ef8aea0bf6dd8b92de7f00bc1562c95e64416e297f216aadcfa3ca43f10da1f8243112286871507fb05c3c7059d568bde96c5885b01af2d6e4a2db10dc8ff128e0fdd39f4cbaf8576dbe170702afcf6b86467bbce57df8680f0d3230767e0e62bdc55c5e53c476742fabbc318437f209886c3cd41d4b0f74049c78ef21476ef5846cf7ded2831848d55f0aa62816caade11adb7ed2fa0f71ce9d8619ac2e627824a45a72b00e413c5a95c0cf63e052bbe2014bfa738c3de3d251dfb0f8a80fda04e6480600113cc558a11a0e10b93a9225886cff04a8d10868662eab87f37271e59f2136f85a855bfda15f9594eb7a3b4ae0b933f95e161c5ceed88f254e97f2ad49b75eedf8562e2d8fb264355314da1dbada866abe47fedb106d01f78b71fec170c8f7276ef58da3de8f64a76bf6f634283730e9d2b9b8390ce0dae565c6a8e04b0710b746678f8a8e0e18382d173a1d7151c909fe4e84ccf57be3e76245b115143584ee73f27afc8e80b4c667e4c37b7054c8be1afde0de978a9c63485fea0457cec70aa089015ab9297e0938c240573cdb7651a4a7f20f43feb304a72aac2e73bd723da1fe5746ec0682bc26070f38c345905d7e238f6077c00dd8f85280211fcd91af84b02ef94a50c004502c1394813252f14575ca09839242f9484cb42df31e763edd237ff31d6c0ffa3fe17f0fb86c7715cfb1ba8bd86cc8d2decd30000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>optionOverrideDCC</sender> + <signal>toggled(bool)</signal> + <receiver>dccPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideDCC</sender> + <signal>toggled(bool)</signal> + <receiver>ipAddress</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideDCC</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel2_3</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideDCC</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel2</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>tabWidget4</tabstop> + <tabstop>loginEdit_</tabstop> + <tabstop>autoLoginCheck_</tabstop> + <tabstop>registerNew</tabstop> + <tabstop>cacheServersCheck__</tabstop> + <tabstop>dccCheck_</tabstop> + <tabstop>useTls_</tabstop> + <tabstop>optionOverrideDCC</tabstop> + <tabstop>ipAddress</tabstop> + <tabstop>dccPort</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>krestrictedline.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/gadu/ui/gaduregisteraccountui.ui b/kopete/protocols/gadu/ui/gaduregisteraccountui.ui new file mode 100644 index 00000000..5a0e475e --- /dev/null +++ b/kopete/protocols/gadu/ui/gaduregisteraccountui.ui @@ -0,0 +1,423 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>GaduRegisterAccountUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GaduRegisterAccountUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>376</width> + <height>394</height> + </rect> + </property> + <property name="caption"> + <string>Register Account - Gadu-Gadu</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout33</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>pixmapEmailAddress</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>labelPasswordVerify</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Repeat pass&word:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>valuePasswordVerify</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>A confirmation of the password you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>A confirmation of the password you would like to use for this account.</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="2"> + <property name="name"> + <cstring>valuePassword</cstring> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>The password you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The password you would like to use for this account.</string> + </property> + </widget> + <widget class="KLineEdit" row="0" column="2"> + <property name="name"> + <cstring>valueEmailAddress</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Your E-mail address.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The E-mail address you would like to use to register this account.</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>pixmapVerificationSequence</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>labelEmailAddress</cstring> + </property> + <property name="text"> + <string>&E-Mail address:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>valueEmailAddress</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>Your E-mail address.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The E-mail address you would like to use to register this account.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>pixmapPasswordVerify</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>labelVerificationSequence</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Verification sequence:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>valueVerificationSequence</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The text from the image below.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The text from the image below. This is used to prevent abusive automated registration scripts.</string> + </property> + </widget> + <widget class="QLineEdit" row="3" column="2"> + <property name="name"> + <cstring>valueVerificationSequence</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The text from the image below.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The text from the image below. This is used to prevent abusive automated registration scripts.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>pixmapPassword</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>labelPassword</cstring> + </property> + <property name="text"> + <string>&Password:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>valuePassword</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The password you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The password you would like to use for this account.</string> + </property> + </widget> + <widget class="KLineEdit" row="2" column="2"> + <property name="name"> + <cstring>valuePasswordVerify</cstring> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>A confirmation of the password you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>A confirmation of the password you would like to use for this account.</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layoutImageCenter</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacerImageLeft</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>23</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>pixmapToken</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>20</horstretch> + <verstretch>13</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>256</width> + <height>64</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>256</width> + <height>64</height> + </size> + </property> + <property name="backgroundMode"> + <enum>PaletteForeground</enum> + </property> + <property name="paletteForegroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + <property name="frameShape"> + <enum>Box</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Gadu-Gadu registration token.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This field contains an image with number that you need to type into the <b>Verification Sequence</b> field above.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacerImageRight</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>22</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>labelInstructions</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><i>Type the letters and numbers shown in the image above into the <b>Verification Sequence</b> field. This is used to prevent automated registration abuse.</i></string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacerMiddle</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>labelStatusMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>valueEmailAddress</tabstop> + <tabstop>valuePassword</tabstop> + <tabstop>valuePasswordVerify</tabstop> + <tabstop>valueVerificationSequence</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/gadu/ui/gadusearch.ui b/kopete/protocols/gadu/ui/gadusearch.ui new file mode 100644 index 00000000..ac215b1a --- /dev/null +++ b/kopete/protocols/gadu/ui/gadusearch.ui @@ -0,0 +1,692 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>GaduPublicDirectory</class> +<widget class="QWidget"> + <property name="name"> + <cstring>GaduPublicDirectory</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>570</width> + <height>386</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QWidgetStack" row="0" column="0"> + <property name="name"> + <cstring>pubsearch</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>page</cstring> + </property> + <attribute name="id"> + <number>0</number> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>buttonGroup2</cstring> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout38</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout36</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout31</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout29</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1a</cstring> + </property> + <property name="text"> + <string>Name:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2a</cstring> + </property> + <property name="text"> + <string>Surname:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3a</cstring> + </property> + <property name="text"> + <string>Nick:</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3_2a</cstring> + </property> + <property name="text"> + <string>City:</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout30</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>nameS</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>surname</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>nick</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>cityS</cstring> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout34</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_2a</cstring> + </property> + <property name="text"> + <string>Age from:</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>ageFrom</cstring> + </property> + <property name="cursor"> + <cursor>0</cursor> + </property> + <property name="buttonSymbols"> + <enum>PlusMinus</enum> + </property> + <property name="maxValue"> + <number>120</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_3</cstring> + </property> + <property name="text"> + <string>to:</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>ageTo</cstring> + </property> + <property name="cursor"> + <cursor>0</cursor> + </property> + <property name="buttonSymbols"> + <enum>PlusMinus</enum> + </property> + <property name="maxValue"> + <number>120</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>297</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout32</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_4a</cstring> + </property> + <property name="text"> + <string>Gender:</string> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string></string> + </property> + </item> + <item> + <property name="text"> + <string>Male</string> + </property> + </item> + <item> + <property name="text"> + <string>Female</string> + </property> + </item> + <property name="name"> + <cstring>gender</cstring> + </property> + <property name="focusPolicy"> + <enum>TabFocus</enum> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget" row="3" column="0"> + <property name="name"> + <cstring>layout37</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout33</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>uin_static</cstring> + </property> + <property name="text"> + <string>User number:</string> + </property> + </widget> + <widget class="KRestrictedLine"> + <property name="name"> + <cstring>UIN</cstring> + </property> + <property name="maxLength"> + <number>32</number> + </property> + <property name="echoMode"> + <enum>Normal</enum> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>radioByUin</cstring> + </property> + <property name="text"> + <string>Request information about user:</string> + </property> + <property name="autoRepeat"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>radioByData</cstring> + </property> + <property name="autoMask"> + <bool>true</bool> + </property> + <property name="text"> + <string>Search by specified data:</string> + </property> + </widget> + <widget class="QLayoutWidget" row="5" column="0"> + <property name="name"> + <cstring>layout35</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>onlyOnline</cstring> + </property> + <property name="text"> + <string>Lookup only those that are currently online</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>224</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <spacer row="4" column="0"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>page</cstring> + </property> + <attribute name="id"> + <number>1</number> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView" row="0" column="0"> + <column> + <property name="text"> + <string>Status</string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Nick Name</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Age</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>City</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>UIN</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <item> + <property name="text"> + <string>12</string> + </property> + <property name="text"> + <string>DONT_TRANSLATE</string> + </property> + <property name="text"> + <string>DONT_TRANSL</string> + </property> + <property name="text"> + <string>999</string> + </property> + <property name="text"> + <string>DONT_TRANSL</string> + </property> + <property name="text"> + <string>245324956234</string> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + </item> + <property name="name"> + <cstring>listFound</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>512</width> + <height>256</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>640</width> + <height>512</height> + </size> + </property> + <property name="lineWidth"> + <number>2</number> + </property> + <property name="resizePolicy"> + <enum>AutoOneFit</enum> + </property> + <property name="selectionMode" stdset="0"> + <enum>Extended</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>NoColumn</enum> + </property> + <property name="fullWidth"> + <bool>false</bool> + </property> + <property name="itemsMovable"> + <bool>false</bool> + </property> + <property name="autoOpen"> + <bool>false</bool> + </property> + <property name="dropVisualizer"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </widget> + </grid> +</widget> +<images> + <image name="image0"> + <data format="XPM.GZ" length="499">789c8590cf6ac3300cc6ef790a13dd4259d37414c6e823acec58183be85fd21dda42d71ec6d8bb57929d50b6c28463f4fba4cf913d6fd2f6f52535f3eaf38ce70f4ebcc3536ae4b2df7fbdbdafbfab7ab14ab69ed2a29e55f543e2b4391ed473b01cda08c7de9127544765796cc3288e7d275dbb74c4829aab43603f7a29a362ae7246afc70c39004a5234434084488a0686a179923543f42f3698fa90984990a63e1389894455a7f3008818544004217bdd695d117e60d19054c49650d1c22b7e07d5d1ebffb08e61b0e6db996d0a4421fd6fe63f77bbfb06bfdeeae7b9ba0259af7424</data> + </image> +</images> +<connections> + <connection> + <sender>radioByData</sender> + <signal>toggled(bool)</signal> + <receiver>UIN</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByData</sender> + <signal>toggled(bool)</signal> + <receiver>uin_static</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>ageFrom</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>ageTo</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>cityS</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>gender</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>nameS</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>surname</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_2_2a</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_2a</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_3_2a</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1a</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>nick</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_4a</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>radioByUin</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_3a</receiver> + <slot>setDisabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>nameS</tabstop> + <tabstop>surname</tabstop> + <tabstop>nick</tabstop> + <tabstop>cityS</tabstop> + <tabstop>gender</tabstop> + <tabstop>UIN</tabstop> + <tabstop>ageFrom</tabstop> + <tabstop>ageTo</tabstop> + <tabstop>onlyOnline</tabstop> + <tabstop>listFound</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>krestrictedline.h</includehint> + <includehint>klistview.h</includehint> +</includehints> +</UI> |