summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/gadu
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/gadu')
-rw-r--r--kopete/protocols/gadu/Makefile.am38
-rw-r--r--kopete/protocols/gadu/README.gadu43
-rw-r--r--kopete/protocols/gadu/gaduaccount.cpp1261
-rw-r--r--kopete/protocols/gadu/gaduaccount.h173
-rw-r--r--kopete/protocols/gadu/gaduaddcontactpage.cpp134
-rw-r--r--kopete/protocols/gadu/gaduaddcontactpage.h61
-rw-r--r--kopete/protocols/gadu/gaduaway.cpp86
-rw-r--r--kopete/protocols/gadu/gaduaway.h49
-rw-r--r--kopete/protocols/gadu/gaducommands.cpp411
-rw-r--r--kopete/protocols/gadu/gaducommands.h150
-rw-r--r--kopete/protocols/gadu/gaducontact.cpp384
-rw-r--r--kopete/protocols/gadu/gaducontact.h119
-rw-r--r--kopete/protocols/gadu/gaducontactlist.cpp207
-rw-r--r--kopete/protocols/gadu/gaducontactlist.h67
-rw-r--r--kopete/protocols/gadu/gadudcc.cpp186
-rw-r--r--kopete/protocols/gadu/gadudcc.h66
-rw-r--r--kopete/protocols/gadu/gadudccserver.cpp196
-rw-r--r--kopete/protocols/gadu/gadudccserver.h66
-rw-r--r--kopete/protocols/gadu/gadudcctransaction.cpp454
-rw-r--r--kopete/protocols/gadu/gadudcctransaction.h87
-rw-r--r--kopete/protocols/gadu/gadueditaccount.cpp263
-rw-r--r--kopete/protocols/gadu/gadueditaccount.h63
-rw-r--r--kopete/protocols/gadu/gadueditcontact.cpp203
-rw-r--r--kopete/protocols/gadu/gadueditcontact.h60
-rw-r--r--kopete/protocols/gadu/gaduprotocol.cpp243
-rw-r--r--kopete/protocols/gadu/gaduprotocol.h108
-rw-r--r--kopete/protocols/gadu/gadupubdir.cpp344
-rw-r--r--kopete/protocols/gadu/gadupubdir.h80
-rw-r--r--kopete/protocols/gadu/gaduregisteraccount.cpp212
-rw-r--r--kopete/protocols/gadu/gaduregisteraccount.h62
-rw-r--r--kopete/protocols/gadu/gadurichtextformat.cpp291
-rw-r--r--kopete/protocols/gadu/gadurichtextformat.h53
-rw-r--r--kopete/protocols/gadu/gadusession.cpp811
-rw-r--r--kopete/protocols/gadu/gadusession.h178
-rw-r--r--kopete/protocols/gadu/icons/Makefile.am2
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_away.pngbin0 -> 895 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_busy.pngbin0 -> 559 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_busy_d.pngbin0 -> 735 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_con.mngbin0 -> 9292 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_connecting.pngbin0 -> 715 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.pngbin0 -> 325 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_ignored.pngbin0 -> 1018 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_invi.pngbin0 -> 487 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_invi_d.pngbin0 -> 623 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_offline.pngbin0 -> 819 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_offline_d.pngbin0 -> 854 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_online.pngbin0 -> 395 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_online_d.pngbin0 -> 478 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-app-gadu_protocol.pngbin0 -> 944 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr32-app-gadu_protocol.pngbin0 -> 2127 bytes
-rw-r--r--kopete/protocols/gadu/kopete_gadu.desktop78
-rw-r--r--kopete/protocols/gadu/libgadu/COPYING504
-rw-r--r--kopete/protocols/gadu/libgadu/Makefile.am12
-rw-r--r--kopete/protocols/gadu/libgadu/common.c822
-rw-r--r--kopete/protocols/gadu/libgadu/compat.h29
-rw-r--r--kopete/protocols/gadu/libgadu/dcc.c1298
-rw-r--r--kopete/protocols/gadu/libgadu/events.c1580
-rw-r--r--kopete/protocols/gadu/libgadu/http.c522
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu-config.h.in30
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu.c1818
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu.h1310
-rw-r--r--kopete/protocols/gadu/libgadu/pubdir.c689
-rw-r--r--kopete/protocols/gadu/libgadu/pubdir50.c467
-rw-r--r--kopete/protocols/gadu/ui/Makefile.am12
-rw-r--r--kopete/protocols/gadu/ui/empty.cpp0
-rw-r--r--kopete/protocols/gadu/ui/gaduadd.ui360
-rw-r--r--kopete/protocols/gadu/ui/gaduawayui.ui194
-rw-r--r--kopete/protocols/gadu/ui/gadueditaccountui.ui873
-rw-r--r--kopete/protocols/gadu/ui/gaduregisteraccountui.ui423
-rw-r--r--kopete/protocols/gadu/ui/gadusearch.ui692
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("&lt;") );
+ input.replace( '>', QString::fromLatin1("&gt;") );
+ input.replace( '\n', QString::fromLatin1( "<br />" ) );
+ input.replace( '\t', QString::fromLatin1( "&nbsp;&nbsp;&nbsp;&nbsp;" ) );
+ input.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( " &nbsp;" ) );
+ 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( &params_, 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( &params_ );
+
+}
+
+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
new file mode 100644
index 00000000..a3ecf056
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_away.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png
new file mode 100644
index 00000000..18d1640c
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png
new file mode 100644
index 00000000..d9790b54
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_con.mng b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng
new file mode 100644
index 00000000..64eea9ea
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png
new file mode 100644
index 00000000..23d80cb4
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png
new file mode 100644
index 00000000..128e27eb
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png
new file mode 100644
index 00000000..98674ef5
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png
new file mode 100644
index 00000000..ab994042
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png
new file mode 100644
index 00000000..e2f44f62
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png
new file mode 100644
index 00000000..65569e12
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png
new file mode 100644
index 00000000..5b5740b3
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online.png b/kopete/protocols/gadu/icons/cr16-action-gg_online.png
new file mode 100644
index 00000000..652c127a
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_online.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png
new file mode 100644
index 00000000..26b28d49
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png
new file mode 100644
index 00000000..46f062c3
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png
new file mode 100644
index 00000000..2ff51736
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png
Binary files differ
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 &amp;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>&lt;i&gt;(for example: 1234567)&lt;/i&gt;</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>&amp;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>&amp;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&amp;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>&amp;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>&amp;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 "&amp;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&amp;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>&amp;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>&amp;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&amp;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>&amp;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&amp;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 &amp;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&amp;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.&lt;br&gt;&lt;br&gt;
+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&amp;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&amp;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>&amp;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&amp;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&amp;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&amp;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>&lt;p align="center"&gt;You must be connected to change your Personal Information.&lt;/p&gt;</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>&amp;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>&lt;qt&gt;&lt;p align="center"&gt;&lt;font color="#ff0000"&gt;These options affect &lt;b&gt;all&lt;/b&gt; Gadu-Gadu accounts.&lt;/font&gt;&lt;/p&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionOverrideDCC</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;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 &amp;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&amp;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&amp;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>&amp;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>&amp;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>&amp;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 &lt;b&gt;Verification Sequence&lt;/b&gt; 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>&lt;i&gt;Type the letters and numbers shown in the image above into the &lt;b&gt;Verification Sequence&lt;/b&gt; field. This is used to prevent automated registration abuse.&lt;/i&gt;</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>