diff options
Diffstat (limited to 'kopete/protocols/oscar/liboscar')
158 files changed, 26497 insertions, 0 deletions
diff --git a/kopete/protocols/oscar/liboscar/DESIGN b/kopete/protocols/oscar/liboscar/DESIGN new file mode 100644 index 00000000..3f772e22 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/DESIGN @@ -0,0 +1,12 @@ +This file attempts to detail the design of the liboscar library. It's still a +work in progress. + +liboscar is based off of the libgroupwise library which handles connections to +Novell's Groupwise messenging system. libgroupwise is based off of the libiris +library which is used to interface with the jabber instant messaging network. + +Details of the library: +============================================ + +All the protocol actions are encapsulated in Tasks. + diff --git a/kopete/protocols/oscar/liboscar/HACKING b/kopete/protocols/oscar/liboscar/HACKING new file mode 100644 index 00000000..9bd25476 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/HACKING @@ -0,0 +1,194 @@ +This is the oscar HACKING file. It details the current coding style that is being +used in this plugin. Thanks to Scott Wheeler for providing the skeleton I based this +file on + +================================================================================ +Code Documentation +================================================================================ + +Please add doxygen comments to the header files where appropriate. I don't expect +anyone to add comments for functions that they're overriding from the base class +but comments everywhere would be good. + +Please comment parts of the code that might be unclear, need more thinking about, +reimplementing, etc. It will help people look for things to do if they want to help +out. + +Please don't remove the kdDebug lines from any of the source files. If they're +excessive, either wrap them in an ifdef and put the ifdef in the soon to be +created oscardebug.h file so that they can be enabled and disabled at the will of +other developers or users. I also tend to use kdDebug statements to document +my code in the place of comments for the simpler sections. + +================================================================================ +Indentation +================================================================================ + +I use tabs to indent everything. When I say tabs, I mean the 'tab' character. Please +don't use 8 spaces to indent. Just hit the 'tab' key, and make sure that space indentation +is turned off in whatever editor you use. However, the exception to the indentation +rule is anything that's inside of a namespace block should not be indented. + + +static void foo() +{ + if ( bar() ) <-- 1 tab + baz(); <-- 2 tabs +} + +namespace +{ +class Foo +{ +Q_OBJECT +public: + Foo(); + ~Foo(); +}; +} + + + + +vim or kate modelines that modify the way tabs are displayed are encouraged, as +long as they don't actually change the way tabs are saved to a file. + +================================================================================ +Braces +================================================================================ + +Braces opening classes, structs, namespaces, functions, and conditionals should be +on their own line. Here's an example: + +class Foo +{ + // stuff +}; + +if ( foo == bar ) +{ + // stuff +} + +while ( foo == bar && + baz == quux && + flop == pop ) +{ + // stuff +} + +static void foo() +{ + // stuff +} + +Also conditionals / loops that only contiain one line in their body (but where +the conditional statement fits onto one line) should omit braces: + +if ( foo == bar ) + baz(); + +But: + +if ( baz == quux && + ralf == spot ) +{ + bar(); +} + +================================================================================ +Spaces +================================================================================ + +Spaces should be used between the conditional / loop type and the +conditional statement. They should also not be used after parenthesis. However +the should be to mark of mathematical or comparative operators. + +if ( foo == bar ) + ^ ^ ^ + +is correct. However: + +if(foo == bar) + +is not. + +================================================================================ +Header Organization +================================================================================ + +Member variables should always be private and prefixed with "m_". Accessors may +not be inline in the headers. The organization of the members in a class should be +roughly as follows: + +public: +public slots: +protected: +protected slots: +signals: +private: // member funtions +private slots: +private: // member variables + +If there are no private slots there is no need for two private sections, however +private functions and private variables should be clearly separated. + +The implementations files -- .cpp files -- should follow (when possible) the +same order of function declarations as the header files. + +Virtual functions should always be marked as such even in derived classes where +it is not strictly necessary. + +================================================================================ +Whitespace +================================================================================ + +Whitespace should be used liberally. When blocks of code are logically distinct +I tend to put a blank line between them. This is difficult to explain +systematically but after looking a bit at the current code organization this +ideally will be somewhat clear. + +Parenthesis should be padded by spaces on one side. This is easier to illustrate in +an example: + +void Client::foo() //correct +void Client::foo3( int, int, int ) //correct + +void Client::foo(int, int, int) //incorrect +void Client::foo(int,int,int) //also incorrect + +Operators should be padded by spaces in conditionals. Again, more examples to +illustrate + +if (foo==bar) +m+=(n*2)-3; + +should be: + +if ( foo == bar ) +m += ( n * 2 ) - 3; + +================================================================================ +Pointer and Reference Operators +================================================================================ + +This one is pretty simple. I prefer "Foo* f" to "Foo *f" in function signatures +and declarations. The same goes for "Foo& f" over "Foo &f". + +================================================================================ + +There are likely things missing here and I'll try to add them over time as I +notice things that are often missed. Please let me know if specific points are +ambiguous. + +Also, please note that since this library is based heavily off of Kopete's +libgroupwise library that the coding style in certain files may not match what's +written in this document. Those files that don't match will be corrected eventually. + +To make things easier on you, kate modelines are provided at the end of certain files +to help enforce the coding style. If you're using the new C S&S Indenter that will be in +KDE 3.4, I can provide a patch that will automatically implement the space padding around +parenthesis. Please mail me so I can send it to you. + +Matt Rogers <mattr@kde.org> + diff --git a/kopete/protocols/oscar/liboscar/Makefile.am b/kopete/protocols/oscar/liboscar/Makefile.am new file mode 100644 index 00000000..ea757b69 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/Makefile.am @@ -0,0 +1,26 @@ + +METASOURCES = AUTO +noinst_LTLIBRARIES = liboscar.la + +AM_CPPFLAGS = -I$(top_srcdir)/kopete/libkopete $(all_includes) + + +liboscar_la_SOURCES = oscarutils.cpp client.cpp task.cpp connector.cpp \ + inputprotocolbase.cpp coreprotocol.cpp flapprotocol.cpp snacprotocol.cpp transfer.cpp rtf.cc \ + bytestream.cpp oscarclientstream.cpp safedelete.cpp stream.cpp oscarconnector.cpp \ + oscarbytestream.cpp buffer.cpp md5.c logintask.cpp aimlogintask.cpp icqlogintask.cpp \ + closeconnectiontask.cpp rateclassmanager.cpp serverversionstask.cpp rateinfotask.cpp \ + errortask.cpp locationrightstask.cpp profiletask.cpp blmlimitstask.cpp \ + servicesetuptask.cpp icbmparamstask.cpp ssimanager.cpp rateclass.cpp rateclass.h \ + prmparamstask.cpp ssiparamstask.cpp ssilisttask.cpp ssiactivatetask.cpp \ + clientreadytask.cpp senddcinfotask.cpp sendidletimetask.cpp ownuserinfotask.cpp \ + connection.cpp onlinenotifiertask.cpp userdetails.cpp ssimodifytask.cpp \ + oscartypeclasses.cpp oscarmessage.cpp messagereceivertask.cpp sendmessagetask.cpp icqtask.cpp \ + offlinemessagestask.cpp ssiauthtask.cpp userinfotask.cpp icquserinfo.cpp icquserinfotask.cpp \ + usersearchtask.cpp warningtask.cpp changevisibilitytask.cpp typingnotifytask.cpp \ + buddyicontask.cpp serverredirecttask.cpp oscarsettings.cpp \ + chatnavservicetask.cpp connectionhandler.cpp chatservicetask.cpp + +liboscar_la_LDFLAGS = -no-undefined $(all_libraries) +liboscar_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) + diff --git a/kopete/protocols/oscar/liboscar/TODO b/kopete/protocols/oscar/liboscar/TODO new file mode 100644 index 00000000..1ec9be98 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/TODO @@ -0,0 +1,37 @@ +This is the TODO file for liboscar. Please note that this TODO file is on a +very short timeframe since the goal is to have liboscar done before KDE 3.4. +Realistically, KDE 4 is a better goal, but i want to push hard for KDE 3.4 + +If you're going to be looking at the docs, I suggest downloading the zip file (click the download link) from iserverd.khstu.ru/oscar for +faster loading. + +Misc. Before Merge things +==================================== + +- Don't hardcode the values in SendDCInfoTask. Find a way to get them from the account or something. +- Rename SendDCInfoTask to SendExtInfoTask (rename the files on the server too. contact sysadmin@kde.org to see about this. It may have to wait until the merge) +- Check capabilities handling (the code is from oscarsocket, we need to make sure it will still work ok for liboscar until we come up with something better) +- Test moving contacts from one group to another + + +Direct Connections +==================================== +When/If we get around to it. Matt knows absolutely nothing about direct connections and the only online source of documentation is no longer online. :( +This will definately be one of those things we have to dissect gaim for. :/ + + +SNAC 0x15 parsing +==================================== + +SNAC 0x15 parsing is done. however parts may need to be reworked as things have gotten +very messy. we currently don't do a good job of handling extra data (i.e. i can't call +addInitialData with just the initial data and get the type 1 tlv length right. maybe a +prepareSend( const Buffer& ) function that adds the type one tlv to our packet so we +get the tlv length right. + +also, we may want to implement a removeInitialData function that we can call if the packet +is for us so we don't have to have code in all the icq tasks that get rid of the initial tlv +data that we parse in parse initial data. + + + diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.cpp b/kopete/protocols/oscar/liboscar/aimlogintask.cpp new file mode 100644 index 00000000..69a9c770 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/aimlogintask.cpp @@ -0,0 +1,254 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "aimlogintask.h" + +#include <stdlib.h> +#include <kdebug.h> +#include <klocale.h> +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +#include "md5.h" + +using namespace Oscar; + +AimLoginTask::AimLoginTask( Task* parent ) + : Task ( parent ) +{ +} + +AimLoginTask::~AimLoginTask() +{ +} + +void AimLoginTask::onGo() +{ + //send Snac 17,06 + sendAuthStringRequest(); + //when we have the authKey, login + connect( this, SIGNAL( haveAuthKey() ), this, SLOT( sendLoginRequest() ) ); +} + +bool AimLoginTask::forMe( Transfer* transfer ) const +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st && st->snacService() == 0x17 ) + { + WORD subtype = st->snacSubtype(); + switch ( subtype ) + { + case 0x0002: + case 0x0003: + case 0x0006: + case 0x0007: + return true; + break; + default: + return false; + break; + } + } + return false; +} + +const QByteArray& AimLoginTask::cookie() const +{ + return m_cookie; +} + +const QString& AimLoginTask::bosHost() const +{ + return m_bosHost; +} + +const QString& AimLoginTask::bosPort() const +{ + return m_bosPort; +} + +bool AimLoginTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if (!st) + return false; + + WORD subtype = st->snacSubtype(); + switch ( subtype ) + { + case 0x0003: + setTransfer( transfer ); + handleLoginResponse(); + setTransfer( 0 ); + return true; + break; + case 0x0007: + setTransfer( transfer ); + processAuthStringReply(); + setTransfer( 0 ); + return true; + break; + default: + return false; + break; + } + + return false; + } + return false; +} + +void AimLoginTask::sendAuthStringRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "SEND CLI_AUTH_REQUEST, sending login request" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0017, 0x0006, 0x0000, client()->snacSequence() }; + + Buffer* outbuf = new Buffer; + outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1() ); + outbuf->addDWord(0x004B0000); // empty TLV 0x004B + outbuf->addDWord(0x005A0000); // empty TLV 0x005A + + Transfer* st = createTransfer( f, s, outbuf ); + send( st ); +} + +void AimLoginTask::processAuthStringReply() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got the authorization key" << endl; + Buffer *inbuf = transfer()->buffer(); + WORD keylen = inbuf->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Key length is " << keylen << endl; + m_authKey.duplicate( inbuf->getBlock(keylen) ); + emit haveAuthKey(); +} + +void AimLoginTask::sendLoginRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_MD5_LOGIN) sending AIM login" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0017, 0x0002, 0x0000, client()->snacSequence() }; + Buffer *outbuf = new Buffer; + const Oscar::ClientVersion* version = client()->version(); + + outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1()); + + QByteArray digest( 17 ); //apparently MD5 digests are 16 bytes long + encodePassword( digest ); + digest[16] = '\0'; //do this so that addTLV sees a NULL-terminator + + outbuf->addTLV(0x0025, 16, digest); + outbuf->addTLV(0x0003, version->clientString.length(), version->clientString.latin1() ); + outbuf->addTLV16(0x0016, version->clientId ); + outbuf->addTLV16(0x0017, version->major ); + outbuf->addTLV16(0x0018, version->minor ); + outbuf->addTLV16(0x0019, version->point ); + outbuf->addTLV16(0x001a, version->build ); + outbuf->addDWord(0x00140004); //TLV type 0x0014, length 0x0004 + outbuf->addDWord( version->other ); //TLV data for type 0x0014 + outbuf->addTLV(0x000f, version->lang.length(), version->lang.latin1() ); + outbuf->addTLV(0x000e, version->country.length(), version->country.latin1() ); + + //if set, old-style buddy lists will not work... you will need to use SSI + outbuf->addTLV8(0x004a,0x01); + + Transfer *st = createTransfer( f, s, outbuf ); + send( st ); +} + +void AimLoginTask::handleLoginResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV SNAC 0x17, 0x07 - AIM Login Response" << endl; + + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer() ); + + if ( !st ) + { + setError( -1 , QString::null ); + return; + } + + QValueList<TLV> tlvList = st->buffer()->getTLVList(); + + TLV uin = findTLV( tlvList, 0x0001 ); + if ( uin ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [SN], SN=" << QString( uin.data ) << endl; + } + + TLV err = findTLV( tlvList, 0x0008 ); + + if ( err ) + { + WORD errorNum = ( ( err.data[0] << 8 ) | err.data[1] ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << k_funcinfo << "found TLV(8) [ERROR] error= " << + errorNum << endl; + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, errorNum ); + setError( errorNum, QString::null ); + return; //if there's an error, we'll need to disconnect anyways + } + + TLV server = findTLV( tlvList, 0x0005 ); + if ( server ) + { + QString ip = QString( server.data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << ip << endl; + int index = ip.find( ':' ); + m_bosHost = ip.left( index ); + ip.remove( 0 , index+1 ); //get rid of the colon and everything before it + m_bosPort = ip.left(4); //we only need 4 bytes + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "We should reconnect to server '" << m_bosHost << + "' on port " << m_bosPort << endl; + } + + TLV cookie = findTLV( tlvList, 0x0006 ); + if ( cookie ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl; + m_cookie.duplicate( cookie.data ); + setSuccess( 0, QString::null ); + } + tlvList.clear(); +} + +void AimLoginTask::encodePassword( QByteArray& digest ) const +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + md5_state_t state; + md5_init( &state ); + md5_append( &state, ( const md5_byte_t* ) m_authKey.data(), m_authKey.size() ); + md5_append( &state, ( const md5_byte_t* ) client()->password().latin1(), client()->password().length() ); + md5_append( &state, ( const md5_byte_t* ) AIM_MD5_STRING, strlen( AIM_MD5_STRING ) ); + md5_finish( &state, ( md5_byte_t* ) digest.data() ); +} + +//kate: indent-mode csands; tab-width 4; + +#include "aimlogintask.moc" diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.h b/kopete/protocols/oscar/liboscar/aimlogintask.h new file mode 100644 index 00000000..66308178 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/aimlogintask.h @@ -0,0 +1,82 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_AIMLOGINTASK_H_ +#define _OSCAR_AIMLOGINTASK_H_ + +#include "task.h" + +using namespace Oscar; + +class AimLoginTask : public Task +{ +Q_OBJECT +public: + AimLoginTask( Task* parent ); + ~AimLoginTask(); + bool take( Transfer* transfer ); + virtual void onGo(); + + //Protocol specific stuff + const QByteArray& cookie() const; + const QString& bosHost() const; + const QString& bosPort() const; + +protected: + bool forMe( Transfer* transfer ) const; + +signals: + void haveAuthKey(); + +private: + //! Encodes a password using MD5 + void encodePassword( QByteArray& digest ) const; + + //! Send SNAC 0x17, 0x06 + void sendAuthStringRequest(); + + //! Handle SNAC 0x17, 0x07 + void processAuthStringReply(); + + //! Handle SNAC 0x17, 0x03 + void handleLoginResponse(); + + //! Parse the error codes to generate a reason why sign-on failed + //Massive code duplication with CloseConnectionTask + bool parseDisconnectCode( int error, QString& reason ); + +private slots: + //! Send SNAC 0x17, 0x02 + void sendLoginRequest(); + +private: + //! The authorization key to use when encoding the password + QByteArray m_authKey; + + //! The all important connection cookie + QByteArray m_cookie; + + //! The new BOS Host + QString m_bosHost; + + //! The new BOS Port + QString m_bosPort; + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.cpp b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp new file mode 100644 index 00000000..c3fe7e6e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp @@ -0,0 +1,94 @@ +/* + Kopete Oscar Protocol + blmlimitstask - Get the BLM service limits + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "blmlimitstask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" + +BLMLimitsTask::BLMLimitsTask( Task* parent ) + : Task( parent ) +{ +} + + +BLMLimitsTask::~BLMLimitsTask() +{ +} + + +bool BLMLimitsTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 3 && st->snacSubtype() == 3 ) + return true; + else + return false; +} + +bool BLMLimitsTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + Buffer* buffer = transfer->buffer(); + while ( buffer->length() != 0 ) + { + TLV t = buffer->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Max BLM entries: " + << t.data << endl; + break; + case 0x0002: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max watcher entries: " + << t.data << endl; + break; + case 0x0003: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max online notifications(?): " + << t.data << endl; + break; + } + } + setSuccess( 0, QString::null ); + return true; + } + else + return false; +} + +void BLMLimitsTask::onGo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending BLM limits request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0003, 0x0002, 0x0000, client()->snacSequence() }; + + Buffer* buffer = new Buffer(); + buffer->addTLV16( 0x0005, 0x0003 ); + + Transfer *t = createTransfer( f, s, buffer ); + send( t ); +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.h b/kopete/protocols/oscar/liboscar/blmlimitstask.h new file mode 100644 index 00000000..7ded03a7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/blmlimitstask.h @@ -0,0 +1,43 @@ +/* + Kopete Oscar Protocol + blmlimitstask.h - Fetch the limits for the BLM service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef BLMLIMITSTASK_H +#define BLMLIMITSTASK_H + +#include "task.h" + +/** +Fetch the limits for the BLM service + +@author Matt Rogers +*/ +class BLMLimitsTask : public Task +{ +public: + BLMLimitsTask( Task* parent ); + + ~BLMLimitsTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.cpp b/kopete/protocols/oscar/liboscar/buddyicontask.cpp new file mode 100644 index 00000000..b2a35b1d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buddyicontask.cpp @@ -0,0 +1,245 @@ +// buddyicontask.cpp + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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 + +#include "buddyicontask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "oscarutils.h" +#include <typeinfo> + +BuddyIconTask::BuddyIconTask( Task* parent ) + :Task( parent ) +{ + m_seq = 0; + m_refNum = -1; + m_iconLength = 0; + m_hashType = 0; +} + +void BuddyIconTask::uploadIcon( WORD length, const QByteArray& data ) +{ + m_iconLength = length; + m_icon = data; + m_action = Send; +} + +void BuddyIconTask::requestIconFor( const QString& user ) +{ + m_user = user; + m_action = Receive; +} + +void BuddyIconTask::setHash( const QByteArray& md5Hash ) +{ + m_hash = md5Hash; +} + +void BuddyIconTask::setHashType( BYTE type ) +{ + m_hashType = type; +} + +void BuddyIconTask::onGo() +{ + if ( m_action == Send && m_icon.count() == 0 ) + return; + + if ( m_action == Receive && ( m_user.isEmpty() || m_hash.count() == 0 ) ) + return; + + if ( m_action == Receive ) + { + if ( client()->isIcq() ) + sendICQBuddyIconRequest(); + else + sendAIMBuddyIconRequest(); + } + else + sendIcon(); +} + +bool BuddyIconTask::forMe( const Transfer* transfer ) +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacRequest() != m_seq ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sequences don't match" << endl; + return false; + } + + if ( st->snacService() == 0x0010 ) + { + switch( st->snacSubtype() ) + { + case 0x0003: + case 0x0005: + case 0x0007: + return true; + break; + default: + return false; + break; + } + } + + return false; +} + +bool BuddyIconTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( !st ) + return false; + + setTransfer( transfer ); + if ( st->snacSubtype() == 0x0003 ) + handleUploadResponse(); + else if ( st->snacSubtype() == 0x0005 ) + handleAIMBuddyIconResponse(); + else + handleICQBuddyIconResponse(); + + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; +} + +void BuddyIconTask::sendIcon() +{ + kdDebug(OSCAR_RAW_DEBUG) << "icon length: " << m_iconLength << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0002, 0x0000, m_seq }; + Buffer* b = new Buffer; + b->addWord( 1 ); //gaim hard codes it, so will we + b->addWord( m_iconLength ); + b->addString( m_icon ); + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleUploadResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server acked icon upload" << endl; + Buffer* b = transfer()->buffer(); + b->skipBytes( 4 ); + BYTE iconHashSize = b->getByte(); + QByteArray hash( b->getBlock( iconHashSize ) ); + //check the hash + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "hash " << hash << endl; + setSuccess( 0, QString::null ); +} + + +void BuddyIconTask::sendAIMBuddyIconRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0004, 0x0000, m_seq }; + Buffer* b = new Buffer; + + b->addBUIN( m_user.latin1() ); //TODO: check encoding + b->addByte( 0x01 ); + b->addWord( 0x0001 ); + b->addByte( m_hashType ); + b->addByte( m_hash.size() ); //MD5 Hash Size + b->addString( m_hash, m_hash.size() ); //MD5 Hash + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleAIMBuddyIconResponse() +{ + Buffer* b = transfer()->buffer(); + QString user = b->getBUIN(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl; + b->skipBytes(2); //unknown field. not used + BYTE iconType = b->getByte(); + Q_UNUSED( iconType ); + BYTE hashSize = b->getByte(); + QByteArray iconHash; + iconHash.duplicate( b->getBlock(hashSize) ); + WORD iconSize = b->getWord(); + QByteArray icon; + icon.duplicate( b->getBlock(iconSize) ); + emit haveIcon( user, icon ); +} + +void BuddyIconTask::sendICQBuddyIconRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl; + FLAP f = { 0x02, 0, 0 }; + m_seq = client()->snacSequence(); + SNAC s = { 0x0010, 0x0006, 0x0000, m_seq }; + Buffer* b = new Buffer; + + b->addBUIN( m_user.latin1() ); //TODO: check encoding + b->addByte( 0x01 ); + b->addWord( 0x0001 ); + b->addByte( m_hashType ); + b->addByte( m_hash.size() ); //MD5 Hash Size + b->addString( m_hash, m_hash.size() ); //MD5 Hash + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void BuddyIconTask::handleICQBuddyIconResponse() +{ + Buffer* b = transfer()->buffer(); + QString user = b->getBUIN(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl; + + b->skipBytes(2); //not used + BYTE iconType = b->getByte(); + Q_UNUSED( iconType ); + + BYTE hashSize = b->getByte(); + QByteArray iconHash; + iconHash.duplicate( b->getBlock(hashSize) ); + + b->skipBytes(1); //not used + b->skipBytes(2); //not used + BYTE iconType2 = b->getByte(); + Q_UNUSED( iconType2 ); + + BYTE hashSize2 = b->getByte(); + QByteArray iconHash2; + iconHash2.duplicate( b->getBlock(hashSize2) ); + + WORD iconSize = b->getWord(); + QByteArray icon; + icon.duplicate( b->getBlock(iconSize) ); + + emit haveIcon( user, icon ); +} + +#include "buddyicontask.moc" + + diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.h b/kopete/protocols/oscar/liboscar/buddyicontask.h new file mode 100644 index 00000000..af7931f0 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buddyicontask.h @@ -0,0 +1,69 @@ +// buddyicontask.h + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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 fdeven 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 + +#ifndef BUDDYICONTASK_H +#define BUDDYICONTASK_H + +#include "task.h" +#include <qcstring.h> + +class Transfer; + +class BuddyIconTask : public Task +{ +Q_OBJECT +public: + BuddyIconTask( Task* parent ); + + void uploadIcon( WORD length, const QByteArray& data ); + void setReferenceNum( WORD num ); + + void requestIconFor( const QString& user ); + void setHash( const QByteArray& md5Hash ); + void setHashType( BYTE type ); + + //! Task implementation + void onGo(); + bool forMe( const Transfer* transfer ); + bool take( Transfer* transfer ); + +signals: + void haveIcon( const QString&, QByteArray ); + +private: + void sendIcon(); + void handleUploadResponse(); + void sendAIMBuddyIconRequest(); + void handleAIMBuddyIconResponse(); + void sendICQBuddyIconRequest(); + void handleICQBuddyIconResponse(); + +private: + enum Action { Send, Receive }; + Action m_action; + WORD m_iconLength; + int m_refNum; + QByteArray m_icon; + QString m_user; + QByteArray m_hash; + BYTE m_hashType; + DWORD m_seq; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/buffer.cpp b/kopete/protocols/oscar/liboscar/buffer.cpp new file mode 100644 index 00000000..b04587e7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buffer.cpp @@ -0,0 +1,519 @@ +/*************************************************************************** + buffer.cpp - description + ------------------- + begin : Thu Jun 6 2002 + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> +#include <kapplication.h> +#include "buffer.h" + +#include <ctype.h> + +Buffer::Buffer() +{ + mReadPos=0; +} + +Buffer::Buffer( const Buffer& other ) +{ + mBuffer.duplicate( other.mBuffer ); + mReadPos = other.mReadPos; +} + +Buffer::Buffer(const char *b, Q_ULONG len) +{ + mBuffer.duplicate(b, len); + mReadPos=0; +} + +Buffer::Buffer( const QByteArray& data ) +{ + mBuffer.duplicate( data ); + mReadPos = 0; +} + + +Buffer::~Buffer() +{ +} + + +int Buffer::addByte(const BYTE b) +{ + expandBuffer(1); + mBuffer[mBuffer.size()-1] = b; + + return mBuffer.size(); +} + +int Buffer::addLEByte(const BYTE b) +{ + expandBuffer(1); + mBuffer[mBuffer.size()-1] = ((b) & 0xff); + + return mBuffer.size(); +} + + +int Buffer::addWord(const WORD w) +{ + expandBuffer(2); + mBuffer[mBuffer.size()-2] = ((w & 0xff00) >> 8); + mBuffer[mBuffer.size()-1] = (w & 0x00ff); + + return mBuffer.size(); +} + +int Buffer::addLEWord(const WORD w) +{ + expandBuffer(2); + mBuffer[mBuffer.size()-2] = (unsigned char) ((w >> 0) & 0xff); + mBuffer[mBuffer.size()-1] = (unsigned char) ((w >> 8) & 0xff); + + return mBuffer.size(); +} + + +int Buffer::addDWord(const DWORD dw) +{ + expandBuffer(4); + mBuffer[mBuffer.size()-4] = (dw & 0xff000000) >> 24; + mBuffer[mBuffer.size()-3] = (dw & 0x00ff0000) >> 16; + mBuffer[mBuffer.size()-2] = (dw & 0x0000ff00) >> 8; + mBuffer[mBuffer.size()-1] = (dw & 0x000000ff); + + return mBuffer.size(); +} + +int Buffer::addLEDWord(const DWORD dw) +{ + expandBuffer(4); + mBuffer[mBuffer.size()-4] = (unsigned char) ((dw >> 0) & 0xff); + mBuffer[mBuffer.size()-3] = (unsigned char) ((dw >> 8) & 0xff); + mBuffer[mBuffer.size()-2] = (unsigned char) ((dw >> 16) & 0xff); + mBuffer[mBuffer.size()-1] = (unsigned char) ((dw >> 24) & 0xff); + + return mBuffer.size(); +} + +int Buffer::addString(QByteArray s) +{ + unsigned int pos = mBuffer.size(); + int len = s.size(); + expandBuffer(len); + + for ( int i = 0; i < len; i++ ) + mBuffer[pos + i] = s[i]; + + return mBuffer.size(); +} + +int Buffer::addString(QByteArray s, DWORD len) +{ + Q_UNUSED( len ); + return addString( s ); +} + +int Buffer::addString( const char* s, DWORD len ) +{ + QByteArray qba; + qba.duplicate( s, len ); + return addString( qba ); +} + +int Buffer::addString(const unsigned char* s, DWORD len) +{ + QByteArray qba; + qba.duplicate( (const char*) s, len ); + return addString( qba ); +} + +int Buffer::addLEString(const char *s, const DWORD len) +{ + unsigned int pos = mBuffer.size(); + expandBuffer(len); + //concatenate the new string onto the buffer + for(unsigned int i=0; i<len; i++) + { + mBuffer[pos+i]=((s[i]) & 0xff); + } + return mBuffer.size(); +} + + +void Buffer::clear() +{ + mBuffer.truncate( 0 ); + mReadPos=0; +} + +int Buffer::addTLV( const TLV& t ) +{ + return addTLV( t.type, t.length, t.data ); +} + +int Buffer::addTLV(WORD type, WORD len, const char *data) +{ + + addWord(type); + addWord(len); + return addString(data,len); +} + +int Buffer::addLETLV(WORD type, WORD len, const char *data) +{ + addLEWord( type ); + addLEWord( len ); + return addString( data, len ); +} + +BYTE Buffer::getByte() +{ + BYTE thebyte = 0x00; + + if(mReadPos < mBuffer.size()) + { + thebyte = mBuffer[mReadPos]; + mReadPos++; + } + else + kdDebug(14150) << "Buffer::getByte(): mBuffer empty" << endl; + + return thebyte; +} + +void Buffer::skipBytes( int bytesToSkip ) +{ + if (mReadPos < mBuffer.size()) + mReadPos += bytesToSkip; +} + +BYTE Buffer::getLEByte() +{ + BYTE b = getByte(); + return (b & 0xff); +} + +WORD Buffer::getWord() +{ + WORD theword, theword2, retword; + theword = getByte(); + theword2 = getByte(); + retword = (theword << 8) | theword2; + return retword; +} + +WORD Buffer::getLEWord() +{ + WORD theword1, theword2, retword; + theword1 = getLEByte(); + theword2 = getLEByte(); + retword = (theword2 << 8) | theword1; + return retword; +} + +DWORD Buffer::getDWord() +{ + DWORD word1, word2; + DWORD retdword; + word1 = getWord(); + word2 = getWord(); + retdword = (word1 << 16) | word2; + return retdword; +} + +DWORD Buffer::getLEDWord() +{ + DWORD word1, word2, retdword; + word1 = getLEWord(); + word2 = getLEWord(); + retdword = (word2 << 16) | word1; + return retdword; +} + +void Buffer::setBuf(char *b, const WORD len) +{ + kdDebug(14150) << k_funcinfo << "Called." << endl; + + mBuffer.duplicate(b, len); + mReadPos = 0; +} + +QByteArray Buffer::getBlock(WORD len) +{ + QByteArray ch( len ); + for ( int i = 0; i < len; i++ ) + { + ch[i] = getByte(); + } + + return ch; +} + +QByteArray Buffer::getBBlock(WORD len) +{ + QByteArray data; + data.duplicate(mBuffer.data() + mReadPos, len); + mReadPos += len; + return data; +} + + +WORD *Buffer::getWordBlock(WORD len) +{ + kdDebug(14150) << k_funcinfo << "of length " << len << endl; + WORD *ch=new WORD[len+1]; + for (unsigned int i=0; i<len; i++) + { + ch[i]=getWord(); + } + ch[len]=0; + return ch; +} + + +QCString Buffer::getLEBlock(WORD len) +{ + QCString ch; + for (unsigned int i=0;i<len;i++) + ch += getLEByte(); + + return ch; +} + +int Buffer::addTLV16(const WORD type, const WORD data) +{ + addWord(type); + addWord(0x0002); //2 bytes long + return addWord(data); +} + +int Buffer::addLETLV16(const WORD type, const WORD data) +{ + addLEWord(type); + addLEWord(0x0002); //2 bytes long + return addLEWord(data); +} + +int Buffer::addTLV8(const WORD type, const BYTE data) +{ + addWord(type); + addWord(0x0001); //1 byte long + return addByte(data); +} + +int Buffer::addLETLV8(const WORD type, const BYTE data) +{ + addLEWord(type); + addLEWord(0x0001); //1 byte long + return addLEByte(data); +} + +TLV Buffer::getTLV() +{ + TLV t; + if(length() >= 4) + { + t.type = getWord(); + t.length = getWord(); + if ( t ) + t.data = getBlock( t.length ); + /*else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Invalid TLV in buffer" << endl;*/ + } + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV data is " << t.data << endl; + return t; +} + +QValueList<TLV> Buffer::getTLVList() +{ + QValueList<TLV> ql; + + while (mReadPos < mBuffer.size()) + { + TLV t; + + t = getTLV(); + if ( !t ) + { + kdDebug(14150) << k_funcinfo << "Invalid TLV found" << endl; + continue; + } + + //kdDebug(14150) << k_funcinfo << "got TLV(" << t.type << ")" << endl; + ql.append(t); + } + + return ql; +} + +int Buffer::addChatTLV(const WORD type, const WORD exchange, + const QString &roomname, const WORD instance) +{ + addWord(type); + addWord(0x0005 + roomname.length()); + addWord(exchange); + addByte(roomname.length()); + addString(roomname.latin1(), roomname.length()); // TODO: check encoding + + return addWord(instance); +} + +void Buffer::expandBuffer(unsigned int inc) +{ + mBuffer.resize(mBuffer.size()+inc, QGArray::SpeedOptim); +} + +QCString Buffer::getLNTS() +{ + WORD len = getLEWord(); + QCString qcs; + qcs.duplicate( getBlock(len) ); + return qcs; +} + +QCString Buffer::getLELNTS() +{ + WORD len = getLEWord(); + QCString qcs; + qcs.duplicate( getBlock(len) ); + return qcs; +} + +int Buffer::addLNTS(const char *s) +{ + unsigned int len = strlen(s); + + addLEWord(len+1); + if(len > 0) + addString(s, len); + int ret = addByte(0x00); + return ret; +} + +int Buffer::addLELNTS(const char *s) +{ + unsigned int len = strlen(s); + int ret = addLEWord(len+1); + if(len > 0) + ret = addLEString(s, len); + ret = addByte(0x00); + return ret; +} + +int Buffer::addBSTR(const char * s) +{ + unsigned int len = strlen(s); + int ret = addWord(len); + if(len > 0) + ret = addString(s, len); + return ret; +} + +QByteArray Buffer::getBSTR() +{ + WORD len = getWord(); + QByteArray qba; + qba.duplicate( getBlock(len) ); + return qba; +} + +int Buffer::addBUIN(const char * s) +{ + unsigned int len = strlen(s); + int ret = addByte(len); + ret = addString(s, len); + return ret; +} + +QByteArray Buffer::getBUIN() +{ + BYTE len = getByte(); + QByteArray qba; + qba.duplicate( getBlock(len) ); + return qba; +} + +char *Buffer::buffer() const +{ + return mBuffer.data(); +} + +int Buffer::length() const +{ + return (mBuffer.size() - mReadPos); +} + +QString Buffer::toString() const +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + for ( it = mBuffer.begin(); it != mBuffer.end(); ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if ( c < 0x10 ) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + +QString Buffer::peekBSTR() +{ + int lastPos = mReadPos; + QByteArray qba = getBSTR(); + mReadPos = lastPos; + return QString( qba ); +} +QString Buffer::peekBUIN() +{ + int lastPos = mReadPos; + QByteArray qba = getBUIN(); + mReadPos = lastPos; + return QString( qba ); +} + +Buffer::operator QByteArray() const +{ + return mBuffer; +} +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/buffer.h b/kopete/protocols/oscar/liboscar/buffer.h new file mode 100644 index 00000000..900ddb50 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/buffer.h @@ -0,0 +1,268 @@ +/*************************************************************************** + buffer.h - description + ------------------- + begin : Thu Jun 6 2002 + + Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2003-2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef BUFFER_H +#define BUFFER_H + +#include "oscartypes.h" + +#include <qvaluelist.h> +#include <qcstring.h> + +class QString; + +using namespace Oscar; + +/** + * @brief A data buffer + */ +class Buffer +{ + public: + /** Default constructor */ + Buffer(); + Buffer( const Buffer& other ); + + /** + * \brief Create a prefilled buffer + * + * Constructor that creates a prefilled buffer of @p len length + * that contains the data from @p b. + */ + Buffer(const char *b, Q_ULONG len); + + /** + * \brief Create a prefilled buffer + * + * Constructor that creates a prefilled buffer from the QByteArray \p data + */ + Buffer( const QByteArray& data ); + + + /** Default destructor */ + ~Buffer(); + + /** + * returns the raw buffer + */ + char *buffer() const; + + /** + * Returns the remaining length of the buffer past the current read + * position. + */ + int length() const; + + /** + * adds the given string to the buffer (make sure it's NULL-terminated) + */ + int addString(QByteArray); + int addString(QByteArray, DWORD); + int addString(const char*, DWORD); + int addString(const unsigned char*, DWORD); + + /** + * Little-endian version of addString + */ + int addLEString(const char *, const DWORD); + + /** + * adds the given string to the buffer with the length in front of it + * (make sure it's NULL-terminated) + */ + int addLNTS(const char * s); + /** + * Little-endian version of addLNTS + */ + int addLELNTS(const char * s); + + /** + * adds the given DWord to the buffer + */ + int addDWord(const DWORD); + + /** + * adds the given word to the buffer + */ + int addWord(const WORD); + + /** + * adds the given word to the buffer in + * little-endian format as needed by old icq server + */ + int addLEWord(const WORD w); + + /** + * adds the given DWord to the buffer in + * little-endian format as needed by old icq server + */ + int addLEDWord(const DWORD dw); + + /** + * adds the given byte to the buffer + */ + int addByte(const BYTE); + int addLEByte(const BYTE); + + /** + * empties the current buffer. + */ + void clear(); + + /** + * Adds a TLV to the buffer + */ + int addTLV( const TLV& t ); + + /** + * Adds a TLV with the given type and data + */ + int addTLV(WORD, WORD, const char *); + + /** + * Adds a little-endian TLV with the given type and data + */ + int addLETLV(WORD, WORD, const char *); + + /** + * Returns a QString representation of the buffer + */ + QString toString() const; + + /** + * gets a DWord out of the buffer + */ + DWORD getDWord(); + + /** + * Gets a word out of the buffer + */ + WORD getWord(); + + /** + * Gets a byte out of the buffer + * It's not a constant method. It advances the buffer + * to the next BYTE after returning one. + */ + BYTE getByte(); + + /** + * Skip \p bytesToSkip number of bytes in the buffer + * Like getByte() the buffer is advanced when skipping + */ + void skipBytes( int bytesToSkip ); + + /** + * Same as above but returns little-endian + */ + WORD getLEWord(); + DWORD getLEDWord(); + BYTE getLEByte(); + + /** + * Set the buffer to the given values. + */ + void setBuf(char *, const WORD); + + /** + * Allocates memory for and gets a block of buffer bytes + */ + QByteArray getBlock(WORD len); + QByteArray getBBlock(WORD len); + + /** + * Allocates memory for and gets a block of buffer words + */ + WORD *getWordBlock(WORD len); + + /** + * Same as above but returning little-endian + */ + QCString getLEBlock(WORD len); + + /** + * Convenience function that gets a LNTS (long null terminated string) + * from the buffer. Otherwise you'd need a getWord() + getBlock() call :) + */ + QCString getLNTS(); + QCString getLELNTS(); + + /** + * adds a 16-bit long TLV + */ + int addTLV16(const WORD type, const WORD data); + + /** + * adds a 16-bit long little-endian TLV + */ + int addLETLV16(const WORD type, const WORD data); + + /** + * adds the given byte to a TLV + */ + int addTLV8(const WORD type, const BYTE data); + + /** + * adds the given byte to a little-endian TLV + */ + int addLETLV8(const WORD type, const BYTE data); + + /** + * Gets a TLV, storing it in a struct and returning it + */ + TLV getTLV(); + + /** + * Gets a list of TLV's + */ + QValueList<TLV> getTLVList(); + + /** + * Creates a chat data segment for a tlv and calls addTLV with that data + */ + int addChatTLV(const WORD, const WORD, const QString &, const WORD); + + /** + * Similar to the LNTS functions but string is NOT null-terminated + */ + int addBSTR(const char * s); + QByteArray getBSTR(); + QString peekBSTR(); + + int addBUIN(const char * s); + QByteArray getBUIN(); + QString peekBUIN(); + + operator QByteArray() const; + + private: + /** + * Make the buffer bigger by @p inc bytes + */ + void expandBuffer(unsigned int inc); + + private: + QByteArray mBuffer; + unsigned int mReadPos; + +}; + +#endif +// kate: tab-width 4; indent-mode csands; +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/oscar/liboscar/bytestream.cpp b/kopete/protocols/oscar/liboscar/bytestream.cpp new file mode 100644 index 00000000..7faa803b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/bytestream.cpp @@ -0,0 +1,270 @@ +/* + * bytestream.cpp - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * 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 + * + */ + +#include"bytestream.h" + +// CS_NAMESPACE_BEGIN + +//! \class ByteStream bytestream.h +//! \brief Base class for "bytestreams" +//! +//! This class provides a basic framework for a "bytestream", here defined +//! as a bi-directional, asynchronous pipe of data. It can be used to create +//! several different kinds of bytestream-applications, such as a console or +//! TCP connection, or something more abstract like a security layer or tunnel, +//! all with the same interface. The provided functions make creating such +//! classes simpler. ByteStream is a pure-virtual class, so you do not use it +//! on its own, but instead through a subclass such as \a BSocket. +//! +//! The signals connectionClosed(), delayedCloseFinished(), readyRead(), +//! bytesWritten(), and error() serve the exact same function as those from +//! <A HREF="http://doc.trolltech.com/3.1/qsocket.html">QSocket</A>. +//! +//! The simplest way to create a ByteStream is to reimplement isOpen(), close(), +//! and tryWrite(). Call appendRead() whenever you want to make data available for +//! reading. ByteStream will take care of the buffers with regards to the caller, +//! and will call tryWrite() when the write buffer gains data. It will be your +//! job to call tryWrite() whenever it is acceptable to write more data to +//! the underlying system. +//! +//! If you need more advanced control, reimplement read(), write(), bytesAvailable(), +//! and/or bytesToWrite() as necessary. +//! +//! Use appendRead(), appendWrite(), takeRead(), and takeWrite() to modify the +//! buffers. If you have more advanced requirements, the buffers can be accessed +//! directly with readBuf() and writeBuf(). +//! +//! Also available are the static convenience functions ByteStream::appendArray() +//! and ByteStream::takeArray(), which make dealing with byte queues very easy. + +class ByteStream::Private +{ +public: + Private() {} + + QByteArray readBuf, writeBuf; +}; + +//! +//! Constructs a ByteStream object with parent \a parent. +ByteStream::ByteStream(QObject *parent) +:QObject(parent) +{ + d = new Private; +} + +//! +//! Destroys the object and frees allocated resources. +ByteStream::~ByteStream() +{ + delete d; +} + +//! +//! Returns TRUE if the stream is open, meaning that you can write to it. +bool ByteStream::isOpen() const +{ + return false; +} + +//! +//! Closes the stream. If there is data in the write buffer then it will be +//! written before actually closing the stream. Once all data has been written, +//! the delayedCloseFinished() signal will be emitted. +//! \sa delayedCloseFinished() +void ByteStream::close() +{ +} + +//! +//! Writes array \a a to the stream. +void ByteStream::write(const QByteArray &a) +{ + if(!isOpen()) + return; + + bool doWrite = bytesToWrite() == 0 ? true: false; + appendWrite(a); + if(doWrite) + tryWrite(); +} + +//! +//! Reads bytes \a bytes of data from the stream and returns them as an array. If \a bytes is 0, then +//! \a read will return all available data. +QByteArray ByteStream::read(int bytes) +{ + return takeRead(bytes); +} + +//! +//! Returns the number of bytes available for reading. +int ByteStream::bytesAvailable() const +{ + return d->readBuf.size(); +} + +//! +//! Returns the number of bytes that are waiting to be written. +int ByteStream::bytesToWrite() const +{ + return d->writeBuf.size(); +} + +//! +//! Writes string \a cs to the stream. +void ByteStream::write(const QCString &cs) +{ + QByteArray block(cs.length()); + memcpy(block.data(), cs.data(), block.size()); + write(block); +} + +//! +//! Clears the read buffer. +void ByteStream::clearReadBuffer() +{ + d->readBuf.resize(0); +} + +//! +//! Clears the write buffer. +void ByteStream::clearWriteBuffer() +{ + d->writeBuf.resize(0); +} + +//! +//! Appends \a block to the end of the read buffer. +void ByteStream::appendRead(const QByteArray &block) +{ + appendArray(&d->readBuf, block); +} + +//! +//! Appends \a block to the end of the write buffer. +void ByteStream::appendWrite(const QByteArray &block) +{ + appendArray(&d->writeBuf, block); +} + +//! +//! Returns \a size bytes from the start of the read buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeRead(int size, bool del) +{ + return takeArray(&d->readBuf, size, del); +} + +//! +//! Returns \a size bytes from the start of the write buffer. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeWrite(int size, bool del) +{ + return takeArray(&d->writeBuf, size, del); +} + +//! +//! Returns a reference to the read buffer. +QByteArray & ByteStream::readBuf() +{ + return d->readBuf; +} + +//! +//! Returns a reference to the write buffer. +QByteArray & ByteStream::writeBuf() +{ + return d->writeBuf; +} + +//! +//! Attempts to try and write some bytes from the write buffer, and returns the number +//! successfully written or -1 on error. The default implementation returns -1. +int ByteStream::tryWrite() +{ + return -1; +} + +//! +//! Append array \a b to the end of the array pointed to by \a a. +void ByteStream::appendArray(QByteArray *a, const QByteArray &b) +{ + int oldsize = a->size(); + a->resize(oldsize + b.size()); + memcpy(a->data() + oldsize, b.data(), b.size()); +} + +//! +//! Returns \a size bytes from the start of the array pointed to by \a from. +//! If \a size is 0, then all available data will be returned. +//! If \a del is TRUE, then the bytes are also removed. +QByteArray ByteStream::takeArray(QByteArray *from, int size, bool del) +{ + QByteArray a; + if(size == 0) { + a = from->copy(); + if(del) + from->resize(0); + } + else { + if(size > (int)from->size()) + size = from->size(); + a.resize(size); + char *r = from->data(); + memcpy(a.data(), r, size); + if(del) { + int newsize = from->size()-size; + memmove(r, r+size, newsize); + from->resize(newsize); + } + } + return a; +} +/* + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +//! \fn void ByteStream::connectionClosed() +//! This signal is emitted when the remote end of the stream closes. + +//! \fn void ByteStream::delayedCloseFinished() +//! This signal is emitted when all pending data has been written to the stream +//! after an attempt to close. + +//! \fn void ByteStream::readyRead() +//! This signal is emitted when data is available to be read. + +//! \fn void ByteStream::bytesWritten(int x); +//! This signal is emitted when data has been successfully written to the stream. +//! \a x is the number of bytes written. + +//! \fn void ByteStream::error(int code) +//! This signal is emitted when an error occurs in the stream. The reason for +//! error is indicated by \a code. +*/ +// CS_NAMESPACE_END + +#include "bytestream.moc" diff --git a/kopete/protocols/oscar/liboscar/bytestream.h b/kopete/protocols/oscar/liboscar/bytestream.h new file mode 100644 index 00000000..7f964fbd --- /dev/null +++ b/kopete/protocols/oscar/liboscar/bytestream.h @@ -0,0 +1,78 @@ +/* + * bytestream.h - base class for bytestreams + * Copyright (C) 2003 Justin Karneges + * + * 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 + * + */ + +#ifndef CS_BYTESTREAM_H +#define CS_BYTESTREAM_H + +#include <qobject.h> +#include <qcstring.h> + +// CS_NAMESPACE_BEGIN + +// CS_EXPORT_BEGIN +class ByteStream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrRead, ErrWrite, ErrCustom = 10 }; + ByteStream(QObject *parent=0); + virtual ~ByteStream()=0; + + virtual bool isOpen() const; + virtual void close(); + virtual void write(const QByteArray &); + virtual QByteArray read(int bytes=0); + virtual int bytesAvailable() const; + virtual int bytesToWrite() const; + + void write(const QCString &); + + static void appendArray(QByteArray *a, const QByteArray &b); + static QByteArray takeArray(QByteArray *from, int size=0, bool del=true); + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void bytesWritten(int); + void error(int); + +protected: + void clearReadBuffer(); + void clearWriteBuffer(); + void appendRead(const QByteArray &); + void appendWrite(const QByteArray &); + QByteArray takeRead(int size=0, bool del=true); + QByteArray takeWrite(int size=0, bool del=true); + QByteArray & readBuf(); + QByteArray & writeBuf(); + virtual int tryWrite(); + +private: +//! \if _hide_doc_ + class Private; + Private *d; +//! \endif +}; +// CS_EXPORT_END + +// CS_NAMESPACE_END + +#endif diff --git a/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp new file mode 100644 index 00000000..5cb44720 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp @@ -0,0 +1,150 @@ +/* + Kopete Oscar Protocol + changevisibilitytask.cpp - Changes the visibility of the account via SSI + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "changevisibilitytask.h" + +#include <qvaluelist.h> +#include <kdebug.h> +#include "buffer.h" +#include "client.h" +#include "connection.h" +#include "oscartypeclasses.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "ssimanager.h" +#include "transfer.h" + + +ChangeVisibilityTask::ChangeVisibilityTask(Task* parent): Task(parent) +{ + m_sequence = 0; + m_visible = true; +} + + +ChangeVisibilityTask::~ChangeVisibilityTask() +{ +} + +void ChangeVisibilityTask::setVisible( bool visible ) +{ + m_visible = visible; +} + +bool ChangeVisibilityTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + SNAC s = st->snac(); //cheat + if ( s.family == 0x0013 && s.subtype == 0x000E ) + return true; + else + return false; +} + +bool ChangeVisibilityTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + } + else + { + setError( 0, QString::null ); + return false; + } +} + +void ChangeVisibilityTask::onGo() +{ + SSIManager* manager = client()->ssiManager(); + Oscar::SSI item = manager->visibilityItem(); + if ( !item ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Didn't find a visibility item" << endl; + setError( 0, QString::null ); + return; + } + + Buffer c8tlv; + BYTE visibleByte = m_visible ? 0x04 : 0x03; + c8tlv.addByte( visibleByte ); + + QValueList<Oscar::TLV> tList; + tList.append( TLV( 0x00CA, c8tlv.length(), c8tlv.buffer() ) ); + + Oscar::SSI newSSI(item); + if ( Oscar::uptateTLVs( newSSI, tList ) == false ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Visibility didn't change, don't update" << endl; + setSuccess( 0, QString::null ); + return; + } + + //remove the old item and add the new item indicating the + //change in visibility. + manager->removeItem( item ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found visibility item. changing setting" << endl; + manager->newItem( newSSI ); + sendEditStart(); + + Buffer* b = new Buffer(); + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0009, 0x0000, client()->snacSequence() }; + m_sequence = s.id; + b->addWord( 0 ); + b->addWord( newSSI.gid() ); + b->addWord( newSSI.bid() ); + b->addWord( newSSI.type() ); + b->addWord( newSSI.tlvListLength() ); + + QValueList<TLV>::const_iterator it2 = newSSI.tlvList().begin(); + QValueList<TLV>::const_iterator listEnd2 = newSSI.tlvList().end(); + for( ; it2 != listEnd2; ++it2 ) + b->addTLV( ( *it2 ) ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending visibility update" << endl; + Transfer* t = createTransfer( f, s, b ); + send( t ); + sendEditEnd(); +} + +void ChangeVisibilityTask::sendEditStart() +{ + SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() }; + FLAP editStart = { 0x02, 0, 0 }; + Buffer* emptyBuffer = new Buffer; + Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer ); + send( t1 ); +} + +void ChangeVisibilityTask::sendEditEnd() +{ + SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() }; + FLAP editEnd = { 0x02, 0, 0 }; + Buffer* emptyBuffer = new Buffer; + Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer ); + send( t5 ); +} + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/changevisibilitytask.h b/kopete/protocols/oscar/liboscar/changevisibilitytask.h new file mode 100644 index 00000000..0ec5ab04 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.h @@ -0,0 +1,58 @@ +/* + Kopete Oscar Protocol + changevisibilitytask.h - Changes the visibility of the account via SSI + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHANGEVISIBILITYTASK_H +#define CHANGEVISIBILITYTASK_H + +#include "task.h" + +/** + * This class provides a way to change how the account user + * appears on everybody else's contact list. It is used to + * implement the invisible online status in ICQ and AIM + * @author Matt Rogers + */ +class ChangeVisibilityTask : public Task +{ +public: + ChangeVisibilityTask( Task* parent ); + ~ChangeVisibilityTask(); + + void setVisible( bool visible = true ); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +private: + //damnit, this is ugly. time to refactor SSI stuff out into it's own + //class, file, whatever. + //! Send the SSI edit start packet + void sendEditStart(); + + //! Send the SSI edit end packet + void sendEditEnd(); + +private: + bool m_visible; + DWORD m_sequence; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp new file mode 100644 index 00000000..f661d1f4 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp @@ -0,0 +1,355 @@ +/* + Kopete Oscar Protocol - Chat Navigation service handlers + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "chatnavservicetask.h" + +#include <kdebug.h> + +#include "transfer.h" +#include "buffer.h" +#include "task.h" +#include "client.h" +#include "connection.h" + + +ChatNavServiceTask::ChatNavServiceTask( Task* parent ) : Task( parent ) +{ + m_type = Limits; +} + + +ChatNavServiceTask::~ChatNavServiceTask() +{ +} + +void ChatNavServiceTask::setRequestType( RequestType rt ) +{ + m_type = rt; +} + +ChatNavServiceTask::RequestType ChatNavServiceTask::requestType() +{ + return m_type; +} + +QValueList<int> ChatNavServiceTask::exchangeList() const +{ + return m_exchanges; +} + +bool ChatNavServiceTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + if ( st->snacService() == 0x000D && st->snacSubtype() == 0x0009 ) + return true; + + return false; +} + +bool ChatNavServiceTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + setTransfer( transfer ); + Buffer* b = transfer->buffer(); + while ( b->length() > 0 ) + { + TLV t = b->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got chat redirect TLV" << endl; + break; + case 0x0002: + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got max concurrent rooms TLV" << endl; + Buffer tlvTwo(t.data); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max concurrent rooms is " << tlvTwo.getByte() << endl; + break; + } + case 0x0003: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange info TLV found" << endl; + handleExchangeInfo( t ); + //set the exchanges for the client + emit haveChatExchanges( m_exchanges ); + break; + case 0x0004: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room info TLV found" << endl; + handleBasicRoomInfo( t ); + break; + }; + } + + + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + +} + +void ChatNavServiceTask::onGo() +{ + FLAP f = { 0x02, 0, 0x00 }; + SNAC s = { 0x000D, m_type, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +void ChatNavServiceTask::createRoom( WORD exchange, const QString& name ) +{ + //most of this comes from gaim. thanks to them for figuring it out + QString cookie = "create"; //hardcoded, seems to be ignored by AOL + QString lang = "en"; + QString charset = "us-ascii"; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x000D, 0x0008, 0x0000, client()->snacSequence() }; + Buffer *b = new Buffer; + + b->addWord( exchange ); + b->addBUIN( cookie.latin1() ); + b->addWord( 0xFFFF ); //assign the last instance + b->addByte( 0x01 ); //detail level + + //just send three TLVs + b->addWord( 0x0003 ); + + //i'm lazy, add TLVs manually + + b->addWord( 0x00D3 ); //type of 0x00D3 - name + b->addWord( name.length() ); + b->addString( name.latin1(), name.length() ); + + b->addWord( 0x00D6 ); //type of 0x00D6 - charset + b->addWord( charset.length() ); + b->addString( charset.latin1(), charset.length() ); + + b->addWord( 0x00D7 ); //type of 0x00D7 - lang + b->addWord( lang.length() ); + b->addString( lang.latin1(), lang.length() ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending join room packet" << endl; + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + + +void ChatNavServiceTask::handleExchangeInfo( const TLV& t ) +{ + kdDebug(OSCAR_RAW_DEBUG) << "Parsing exchange info TLV" << endl; + Buffer b(t.data); + ChatExchangeInfo exchangeInfo; + + exchangeInfo.number = b.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange id is: " << exchangeInfo.number << endl; + b.getWord(); + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); + Buffer tmp = t.data; + switch (t.type) + { + case 0x02: + //kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl; + break; + case 0x03: + exchangeInfo.maxRooms = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "max concurrent rooms for the exchange is " << t.data << endl; + break; + case 0x04: + exchangeInfo.maxRoomNameLength = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max room name length is " << exchangeInfo.maxRoomNameLength << endl; + break; + case 0x05: + //kdDebug(OSCAR_RAW_DEBUG) << "received root rooms info" << endl; + break; + case 0x06: + //kdDebug(OSCAR_RAW_DEBUG) << "received search tags" << endl; + break; + case 0xCA: + //kdDebug(OSCAR_RAW_DEBUG) << "have exchange creation time" << endl; + break; + case 0xC9: + //kdDebug(OSCAR_RAW_DEBUG) << "got chat flag" << endl; + break; + case 0xD0: + //kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl; + break; + case 0xD1: + exchangeInfo.maxMsgLength = tmp.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl; + break; + case 0xD2: + kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl; + break; + case 0xD3: + { + QString eName( t.data ); + kdDebug(OSCAR_RAW_DEBUG) << "exchange name: " << eName << endl; + exchangeInfo.description = eName; + break; + } + case 0xD4: + //kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl; + break; + case 0xD5: + exchangeInfo.canCreate = tmp.getByte(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "creation permissions " << exchangeInfo.canCreate << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl; + break; + } + } + m_exchanges.append( exchangeInfo.number ); +} + +void ChatNavServiceTask::handleBasicRoomInfo( const TLV& t ) +{ + kdDebug(OSCAR_RAW_DEBUG) << "Parsing room info TLV" << t.length << endl; + Buffer b(t.data); + WORD exchange = b.getWord(); + QByteArray cookie( b.getBlock( b.getByte() ) ); + WORD instance = b.getWord(); + b.getByte(); //detail level, which i'm not sure we need + WORD tlvCount = b.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "e: " << exchange + << " c: " << cookie << " i: " << instance << endl; + + QValueList<Oscar::TLV> tlvList = b.getTLVList(); + QValueList<Oscar::TLV>::iterator it, itEnd = tlvList.end(); + QString roomName; + for ( it = tlvList.begin(); it != itEnd; ++it ) + { + TLV t = ( *it ); + switch (t.type) + { + case 0x66: + kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl; + break; + case 0x67: + kdDebug(OSCAR_RAW_DEBUG) << "user array" << endl; + break; + case 0x68: + kdDebug(OSCAR_RAW_DEBUG) << "evil generated" << t.data << endl; + break; + case 0x69: + kdDebug(OSCAR_RAW_DEBUG) << "evil generated array" << endl; + break; + case 0x6A: + roomName = QString( t.data ); + kdDebug(OSCAR_RAW_DEBUG) << "fully qualified name" << roomName << endl; + break; + case 0x6B: + kdDebug(OSCAR_RAW_DEBUG) << "moderator" << endl; + break; + case 0x6D: + kdDebug(OSCAR_RAW_DEBUG) << "num children" << endl; + break; + case 0x06F: + kdDebug(OSCAR_RAW_DEBUG) << "occupancy" << endl; + break; + case 0x71: + kdDebug(OSCAR_RAW_DEBUG) << "occupant evil" << endl; + break; + case 0x75: + kdDebug(OSCAR_RAW_DEBUG) << "room activity" << endl; + break; + case 0xD0: + kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl; + break; + case 0xD1: + kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl; + break; + case 0xD2: + kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl; + break; + case 0xD3: + kdDebug(OSCAR_RAW_DEBUG) << "exchange name" << endl; + break; + case 0xD4: + kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl; + break; + case 0xD5: + kdDebug(OSCAR_RAW_DEBUG) << "creation permissions " << t.data << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl; + break; + } + } + + emit connectChat( exchange, cookie, instance, roomName ); +} + +void ChatNavServiceTask::handleCreateRoomInfo( const TLV& t ) +{ + Buffer b( t.data ); + WORD exchange = b.getWord(); + WORD cookieLength = b.getByte(); + QByteArray cookie( b.getBlock( cookieLength ) ); + WORD instance = b.getWord(); + BYTE detailLevel = b.getByte(); + + if ( detailLevel != 0x02 ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown detail level in response" << endl; + return; + } + + WORD numberTlvs = b.getWord(); + QValueList<Oscar::TLV> roomTLVList = b.getTLVList(); + QValueList<Oscar::TLV>::iterator itEnd = roomTLVList.end(); + for ( QValueList<Oscar::TLV>::iterator it = roomTLVList.begin(); + it != itEnd; ++ it ) + { + switch( ( *it ).type ) + { + case 0x006A: + { + QString fqcn = QString( ( *it ).data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "fqcn: " << fqcn << endl; + break; + } + case 0x00C9: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags: " << t.data << endl; + break; + case 0x00CA: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create time: " << t.data << endl; + break; + case 0x00D1: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max msg len: " << t.data << endl; + break; + case 0x00D2: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max occupancy: " << t.data << endl; + break; + case 0x00D3: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "name: " << QString( t.data ) << endl; + break; + case 0x00D5: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create perms: " << t.data << endl; + break; + }; + } +} + +#include "chatnavservicetask.moc" +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.h b/kopete/protocols/oscar/liboscar/chatnavservicetask.h new file mode 100644 index 00000000..6b7d8764 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.h @@ -0,0 +1,67 @@ +/* + Kopete Oscar Protocol - Chat Navigation service handlers + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATNAVSERVICETASK_H +#define CHATNAVSERVICETASK_H + +#include "task.h" + +#include <qvaluelist.h> +#include <oscartypes.h> + +class Transfer; + +/** + * @author Matt Rogers + */ +class ChatNavServiceTask : public Task +{ +Q_OBJECT +public: + ChatNavServiceTask( Task* parent ); + ~ChatNavServiceTask(); + + enum RequestType { Limits = 0x0002, Exchange, Room, ExtRoom, Members, + Search, Create }; + + void setRequestType( RequestType ); + RequestType requestType(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + void createRoom( WORD exchange, const QString& name ); //create a room. sends the packet as well + + QValueList<int> exchangeList() const; + +signals: + void haveChatExchanges( const QValueList<int>& ); + void connectChat( WORD, QByteArray, WORD, const QString& ); + +private: + void handleExchangeInfo( const TLV& t ); + void handleBasicRoomInfo( const TLV& t ); + void handleCreateRoomInfo( const TLV& t ); + +private: + QValueList<int> m_exchanges; + RequestType m_type; +}; + +#endif + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.cpp b/kopete/protocols/oscar/liboscar/chatservicetask.cpp new file mode 100644 index 00000000..9d07afe8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatservicetask.cpp @@ -0,0 +1,359 @@ +// Kopete Oscar Protocol - chat service task + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA + + +#include "chatservicetask.h" + +#include <qstring.h> +#include <kapplication.h> +#include <kdebug.h> +#include <qtextcodec.h> + +#include "connection.h" +#include "transfer.h" +#include "buffer.h" +#include "oscartypes.h" + +ChatServiceTask::ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room ) + : Task( parent ), m_encoding( "us-ascii" ) +{ + m_exchange = exchange; + m_room = room; +} + +ChatServiceTask::~ChatServiceTask() +{ + +} + +void ChatServiceTask::setMessage( const Oscar::Message& msg ) +{ + m_message = msg; +} + +void ChatServiceTask::setEncoding( const QCString& enc ) +{ + m_encoding = enc; +} + +void ChatServiceTask::onGo() +{ + if ( !m_message ) + { + setSuccess( true, QString::null ); + return; + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending '" << m_message.textArray() << "' to the " + << m_room << " room" << endl; + Buffer* b = new Buffer(); + b->addDWord( KApplication::random() ); //use kapp since it's convenient + b->addDWord( KApplication::random() ); + b->addWord( 0x0003 ); //this be message channel 3 mateys! arrr!! + b->addDWord( 0x00010000 ); //TLV 1 - this means it's a public message + b->addDWord( 0x00060000 ); //TLV 6 - enables the server sending you your message back + + Buffer tlv5; + TLV type2, type3, type1; + + type2.type = 0x0002; + type2.length = 0x0008; + type2.data = m_encoding; + + type3.type = 0x0003; + type3.length = 0x0002; + type3.data = QCString( "en" ); //hardcode for right now. don't know that we can do others + + type1.type = 0x0001; + type1.length = m_message.textArray().size(); + type1.data = m_message.textArray(); + tlv5.addWord( 0x0005 ); + tlv5.addWord( 12 + type1.length + type2.length + type3.length ); + tlv5.addTLV( type1 ); + tlv5.addTLV( type2 ); + tlv5.addTLV( type3 ); + + b->addString( tlv5.buffer(), tlv5.length() ); + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x000E, 0x0005, 0x0000, client()->snacSequence() }; + Transfer* t = createTransfer( f, s, b ); + send( t ); + setSuccess( true ); +} + +bool ChatServiceTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t ); + if ( !st ) + return false; + + if ( st->snacService() != 0x000E ) + return false; + + switch ( st->snacSubtype() ) + { + case 0x0003: + case 0x0002: + case 0x0006: + case 0x0009: + case 0x0004: + return true; + break; + default: + return false; + break; + } + + return true; +} + +bool ChatServiceTask::take( Transfer* t ) +{ + if ( !forMe( t ) ) + return false; + + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + if ( !st ) + return false; + + setTransfer( t ); + + switch ( st->snacSubtype() ) + { + case 0x0002: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parse room info" << endl; + parseRoomInfo(); + break; + case 0x0003: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user joined notification" << endl; + parseJoinNotification(); + break; + case 0x0004: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user left notification" << endl; + parseLeftNotification(); + break; + case 0x0006: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message from room to client" << endl; + parseChatMessage(); + break; + case 0x0009: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "chat error or data" << endl; + break; + }; + + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; +} + +void ChatServiceTask::parseRoomInfo() +{ + WORD instance; + BYTE detailLevel; + Buffer* b = transfer()->buffer(); + + m_exchange = b->getWord(); + QByteArray cookie( b->getBlock( b->getByte() ) ); + instance = b->getWord(); + + detailLevel = b->getByte(); + + //skip the tlv count, we don't care. Buffer::getTLVList() handles this all + //correctly anyways + b->skipBytes( 2 ); + + QValueList<Oscar::TLV> tlvList = b->getTLVList(); + QValueList<Oscar::TLV>::iterator it = tlvList.begin(); + QValueList<Oscar::TLV>::iterator itEnd = tlvList.end(); + for ( ; it != itEnd; ++it ) + { + switch ( ( *it ).type ) + { + case 0x006A: + m_internalRoom = QString( ( *it ).data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room name: " << m_room << endl; + break; + case 0x006F: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "num occupants: " << ( *it ).data << endl; + break; + case 0x0073: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "occupant list" << endl; + break; + case 0x00C9: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags" << endl; + break; + case 0x00CA: //creation time + case 0x00D1: //max message length + case 0x00D3: //room description + case 0x00D6: //encoding 1 + case 0x00D7: //language 1 + case 0x00D8: //encoding 2 + case 0x00D9: //language 2 + case 0x00DA: //maximum visible message length + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unhandled TLV type " << ( *it ).type << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown TLV type " << ( *it ).type << endl; + break; + } + } +} + +void ChatServiceTask::parseJoinNotification() +{ + Buffer* b = transfer()->buffer(); + while ( b->length() > 0 ) + { + QString sender( b->getBUIN() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl; + WORD warningLevel = b->getWord(); + WORD numTLVs = b->getWord(); + for ( int i = 0; i < numTLVs; i++ ) + { + TLV t = b->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl; + break; + case 0x000F: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl; + break; + case 0x0003: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl; + break; + } + } + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "emitted userJoinedChat" << endl; + emit userJoinedChat( m_exchange, m_room, sender ); + } + +} + +void ChatServiceTask::parseLeftNotification() +{ + Buffer* b = transfer()->buffer(); + while ( b->length() > 0 ) + { + QString sender( b->getBUIN() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl; + WORD warningLevel = b->getWord(); + WORD numTLVs = b->getWord(); + for ( int i = 0; i < numTLVs; i++ ) + { + TLV t = b->getTLV(); + switch ( t.type ) + { + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl; + break; + case 0x000F: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl; + break; + case 0x0003: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl; + break; + } + } + emit userLeftChat( m_exchange, m_room, sender ); + } +} + +void ChatServiceTask::parseChatMessage() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "have new chat room message" << endl; + Buffer* b = transfer()->buffer(); + bool whisper = true, reflection = false; + QByteArray language, encoding, message; + QString sender; + QByteArray icbmCookie( b->getBlock( 8 ) ); + b->skipBytes( 2 ); //message channel always 0x03 + QValueList<Oscar::TLV> chatTLVs = b->getTLVList(); + QValueList<Oscar::TLV>::iterator it, itEnd = chatTLVs.end(); + for ( it = chatTLVs.begin(); it != itEnd; ++it ) + { + switch ( ( *it ).type ) + { + case 0x0001: //if present, message was sent to the room + whisper = false; + break; + case 0x0006: //enable reflection + reflection = true; + break; + case 0x0005: //the good stuff - the actual message + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsing the message" << endl; + //oooh! look! more TLVS! i love those! + Buffer b( ( *it ).data ); + while ( b.length() >= 4 ) + { + TLV t = b.getTLV(); + switch( t.type ) + { + case 0x0003: + language = t.data; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "language: " << language << endl; + break; + case 0x0002: + encoding = t.data; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "encoding: " << encoding << endl; + break; + case 0x0001: + message = t.data; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message: " << message << endl; + break; + } + } + } + break; + case 0x0003: //user info + { + Buffer b( ( *it ).data ); + sender = QString( b.getBUIN() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got user info. sender is " << sender << endl; + } + break; + + } + } + + QTextCodec* codec = QTextCodec::codecForName( encoding ); + if ( ! codec ) + codec = QTextCodec::codecForMib( 4 ); + QString msgText( codec->toUnicode( message ) ); + Oscar::Message omessage; + omessage.setReceiver( client()->userId() ); + omessage.setSender( sender ); + omessage.setTimestamp( QDateTime::currentDateTime() ); + omessage.setText( Oscar::Message::UTF8, msgText ); + omessage.setType( 0x03 ); + omessage.setExchange( m_exchange ); + omessage.setChatRoom( m_room ); + emit newChatMessage( omessage ); +} + +void ChatServiceTask::parseChatError() +{ + +} + + +#include "chatservicetask.moc" + diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.h b/kopete/protocols/oscar/liboscar/chatservicetask.h new file mode 100644 index 00000000..90e29300 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/chatservicetask.h @@ -0,0 +1,65 @@ +// Kopete Oscar Protocol - Chat service handling + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA + +#ifndef CHATSERVICETASK_H +#define CHATSERVICETASK_H + +#include "task.h" +#include "oscarmessage.h" + +class Transfer; + +class ChatServiceTask : public Task +{ +Q_OBJECT +public: + ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room ); + ~ChatServiceTask(); + + void onGo(); + bool take( Transfer* t ); + + void parseRoomInfo(); + + void parseJoinNotification(); + void parseLeftNotification(); + + void parseChatMessage(); + void parseChatError(); + + void setMessage( const Oscar::Message& msg ); + void setEncoding( const QCString &enc ); + +signals: + void userJoinedChat( Oscar::WORD, const QString& r, const QString& u ); + void userLeftChat( Oscar::WORD, const QString& r, const QString& u ); + void newChatMessage( const Oscar::Message& msg ); + +protected: + bool forMe( const Transfer* t ) const; + +private: + WORD m_exchange; + QString m_room; + QString m_internalRoom; + Oscar::Message m_message; + QCString m_encoding; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/client.cpp b/kopete/protocols/oscar/liboscar/client.cpp new file mode 100644 index 00000000..af967512 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/client.cpp @@ -0,0 +1,1353 @@ +/* + client.cpp - Kopete Oscar Protocol + + Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "client.h" + +#include <qtimer.h> +#include <qtextcodec.h> + +#include <kdebug.h> //for kdDebug() +#include <klocale.h> + +#include "buddyicontask.h" +#include "clientreadytask.h" +#include "connectionhandler.h" +#include "changevisibilitytask.h" +#include "chatnavservicetask.h" +#include "errortask.h" +#include "icquserinfo.h" +#include "icquserinfotask.h" +#include "logintask.h" +#include "connection.h" +#include "messagereceivertask.h" +#include "onlinenotifiertask.h" +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "oscarsettings.h" +#include "oscarutils.h" +#include "ownuserinfotask.h" +#include "profiletask.h" +#include "senddcinfotask.h" +#include "sendmessagetask.h" +#include "serverredirecttask.h" +#include "servicesetuptask.h" +#include "ssimanager.h" +#include "ssimodifytask.h" +#include "ssiauthtask.h" +#include "offlinemessagestask.h" +#include "task.h" +#include "typingnotifytask.h" +#include "userinfotask.h" +#include "usersearchtask.h" +#include "warningtask.h" +#include "chatservicetask.h" +#include "rateclassmanager.h" + + +namespace +{ + class DefaultCodecProvider : public Client::CodecProvider + { + public: + virtual QTextCodec* codecForContact( const QString& ) const + { + return QTextCodec::codecForMib( 4 ); + } + virtual QTextCodec* codecForAccount() const + { + return QTextCodec::codecForMib( 4 ); + } + }; + + DefaultCodecProvider defaultCodecProvider; +} + +class Client::ClientPrivate +{ +public: + ClientPrivate() {} + + QString host, user, pass; + uint port; + int tzoffset; + bool active; + + enum { StageOne, StageTwo }; + int stage; + + //Protocol specific data + bool isIcq; + bool redirectRequested; + QValueList<WORD> redirectionServices; + WORD currentRedirect; + QByteArray cookie; + DWORD connectAsStatus; // icq only + QString connectWithMessage; // icq only + Oscar::Settings* settings; + + //Tasks + ErrorTask* errorTask; + OnlineNotifierTask* onlineNotifier; + OwnUserInfoTask* ownStatusTask; + MessageReceiverTask* messageReceiverTask; + SSIAuthTask* ssiAuthTask; + ICQUserInfoRequestTask* icqInfoTask; + UserInfoTask* userInfoTask; + TypingNotifyTask * typingNotifyTask; + SSIModifyTask* ssiModifyTask; + //Managers + SSIManager* ssiManager; + ConnectionHandler connections; + + //Our Userinfo + UserDetails ourDetails; + + //Infos + QValueList<int> exchanges; + + QString statusMessage; // for away-,DND-message etc... + + //away messages + struct AwayMsgRequest + { + QString contact; + ICQStatus contactStatus; + }; + QValueList<AwayMsgRequest> awayMsgRequestQueue; + QTimer* awayMsgRequestTimer; + CodecProvider* codecProvider; + + const Oscar::ClientVersion* version; +}; + +Client::Client( QObject* parent ) +:QObject( parent, "oscarclient" ) +{ + m_loginTask = 0L; + m_loginTaskTwo = 0L; + + d = new ClientPrivate; + d->tzoffset = 0; + d->active = false; + d->isIcq = false; //default to AIM + d->redirectRequested = false; + d->currentRedirect = 0; + d->connectAsStatus = 0x0; // default to online + d->ssiManager = new SSIManager( this ); + d->settings = new Oscar::Settings(); + d->errorTask = 0L; + d->onlineNotifier = 0L; + d->ownStatusTask = 0L; + d->messageReceiverTask = 0L; + d->ssiAuthTask = 0L; + d->icqInfoTask = 0L; + d->userInfoTask = 0L; + d->stage = ClientPrivate::StageOne; + d->typingNotifyTask = 0L; + d->ssiModifyTask = 0L; + d->awayMsgRequestTimer = new QTimer(); + d->codecProvider = &defaultCodecProvider; + + connect( this, SIGNAL( redirectionFinished( WORD ) ), + this, SLOT( checkRedirectionQueue( WORD ) ) ); + connect( d->awayMsgRequestTimer, SIGNAL( timeout() ), + this, SLOT( nextICQAwayMessageRequest() ) ); +} + +Client::~Client() +{ + + //delete the connections differently than in deleteConnections() + //deleteLater() seems to cause destruction order issues + deleteStaticTasks(); + delete d->settings; + delete d->ssiManager; + delete d->awayMsgRequestTimer; + delete d; +} + +Oscar::Settings* Client::clientSettings() const +{ + return d->settings; +} + +void Client::connectToServer( Connection *c, const QString& server, bool auth ) +{ + d->connections.append( c ); + if ( auth == true ) + { + m_loginTask = new StageOneLoginTask( c->rootTask() ); + connect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) ); + } + + connect( c, SIGNAL( socketError( int, const QString& ) ), this, SLOT( determineDisconnection( int, const QString& ) ) ); + c->connectToServer(server, auth); +} + +void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass ) +{ + Q_UNUSED( host ); + Q_UNUSED( port ); + d->user = userId; + d->pass = pass; + d->stage = ClientPrivate::StageOne; + d->active = false; +} + +void Client::close() +{ + d->active = false; + d->awayMsgRequestTimer->stop(); + d->awayMsgRequestQueue.clear(); + d->connections.clear(); + deleteStaticTasks(); + + //don't clear the stored status between stage one and two + if ( d->stage == ClientPrivate::StageTwo ) + { + d->connectAsStatus = 0x0; + d->connectWithMessage = QString::null; + } + + d->exchanges.clear(); + d->redirectRequested = false; + d->currentRedirect = 0; + d->redirectionServices.clear(); + d->ssiManager->clear(); +} + +void Client::setStatus( AIMStatus status, const QString &_message ) +{ + // AIM: you're away exactly when your away message isn't empty. + // can't use QString::null as a message either; ProfileTask + // interprets null as "don't change". + QString message; + if ( status == Online ) + message = QString::fromAscii(""); + else + { + if ( _message.isEmpty() ) + message = QString::fromAscii(" "); + else + message = _message; + } + + Connection* c = d->connections.connectionForFamily( 0x0002 ); + if ( !c ) + return; + ProfileTask* pt = new ProfileTask( c->rootTask() ); + pt->setAwayMessage( message ); + pt->go( true ); +} + +void Client::setStatus( DWORD status, const QString &message ) +{ + // remember the message to reply with, when requested + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting status message to "<< message << endl; + d->statusMessage = message; + // ICQ: if we're active, set status. otherwise, just store the status for later. + if ( d->active ) + { + //the first connection is always the BOS connection + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; //TODO trigger an error of some sort? + + ChangeVisibilityTask* cvt = new ChangeVisibilityTask( c->rootTask() ); + if ( ( status & 0x0100 ) == 0x0100 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting invisible" << endl; + cvt->setVisible( false ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting visible" << endl; + cvt->setVisible( true ); + } + cvt->go( true ); + c = d->connections.connectionForFamily( 0x0002 ); + if ( !c ) + return; + + SendDCInfoTask* sdcit = new SendDCInfoTask( c->rootTask(), status ); + sdcit->go( true ); //autodelete + // TODO: send away message + } + else + { + d->connectAsStatus = status; + d->connectWithMessage = message; + } +} + +UserDetails Client::ourInfo() const +{ + return d->ourDetails; +} + +QString Client::host() +{ + return d->host; +} + +int Client::port() +{ + return d->port; +} + +SSIManager* Client::ssiManager() const +{ + return d->ssiManager; +} + +const Oscar::ClientVersion* Client::version() const +{ + return d->version; +} + +// SLOTS // + +void Client::streamConnected() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + d->stage = ClientPrivate::StageTwo; + if ( m_loginTaskTwo ) + m_loginTaskTwo->go(); +} + +void Client::lt_loginFinished() +{ + /* Check for stage two login first, since we create the stage two + * task when we finish stage one + */ + if ( d->stage == ClientPrivate::StageTwo ) + { + //we've finished logging in. start the services setup + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage two done. setting up services" << endl; + initializeStaticTasks(); + ServiceSetupTask* ssTask = new ServiceSetupTask( d->connections.defaultConnection()->rootTask() ); + connect( ssTask, SIGNAL( finished() ), this, SLOT( serviceSetupFinished() ) ); + ssTask->go( true ); //fire and forget + m_loginTaskTwo->deleteLater(); + m_loginTaskTwo = 0; + } + else if ( d->stage == ClientPrivate::StageOne ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage one login done" << endl; + disconnect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) ); + + if ( m_loginTask->statusCode() == 0 ) //we can start stage two + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no errors from stage one. moving to stage two" << endl; + + //cache these values since they'll be deleted when we close the connections (which deletes the tasks) + d->host = m_loginTask->bosServer(); + d->port = m_loginTask->bosPort().toUInt(); + d->cookie = m_loginTask->loginCookie(); + close(); + QTimer::singleShot( 100, this, SLOT(startStageTwo() ) ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "errors reported. not moving to stage two" << endl; + close(); //deletes the connections for us + } + + m_loginTask->deleteLater(); + m_loginTask = 0; + } + +} + +void Client::startStageTwo() +{ + //create a new connection and set it up + Connection* c = createConnection( d->host, QString::number( d->port ) ); + new CloseConnectionTask( c->rootTask() ); + + //create the new login task + m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() ); + m_loginTaskTwo->setCookie( d->cookie ); + QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) ); + + + //connect + QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) ); + connectToServer( c, d->host, false ) ; + +} + +void Client::serviceSetupFinished() +{ + d->active = true; + + if ( isIcq() ) + setStatus( d->connectAsStatus, d->connectWithMessage ); + + d->ownStatusTask->go(); + + if ( isIcq() ) + { + //retrieve offline messages + Connection* c = d->connections.connectionForFamily( 0x0015 ); + if ( !c ) + return; + + OfflineMessagesTask *offlineMsgTask = new OfflineMessagesTask( c->rootTask() ); + connect( offlineMsgTask, SIGNAL( receivedOfflineMessage(const Oscar::Message& ) ), + this, SIGNAL( messageReceived(const Oscar::Message& ) ) ); + offlineMsgTask->go( true ); + } + + emit haveSSIList(); + emit loggedIn(); +} + +void Client::receivedIcqInfo( const QString& contact, unsigned int type ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "received icq info for " << contact + << " of type " << type << endl; + + if ( type == ICQUserInfoRequestTask::Short ) + emit receivedIcqShortInfo( contact ); + else + emit receivedIcqLongInfo( contact ); +} + +void Client::receivedInfo( Q_UINT16 sequence ) +{ + UserDetails details = d->userInfoTask->getInfoFor( sequence ); + emit receivedUserInfo( details.userId(), details ); +} + +void Client::offlineUser( const QString& user, const UserDetails& ) +{ + emit userIsOffline( user ); +} + +void Client::haveOwnUserInfo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << endl; + UserDetails ud = d->ownStatusTask->getInfo(); + d->ourDetails = ud; + emit haveOwnInfo(); +} + +void Client::setCodecProvider( Client::CodecProvider* codecProvider ) +{ + d->codecProvider = codecProvider; +} + +void Client::setVersion( const Oscar::ClientVersion* version ) +{ + d->version = version; +} + +// INTERNALS // + +QString Client::userId() const +{ + return d->user; +} + +QString Client::password() const +{ + return d->pass; +} + +QString Client::statusMessage() const +{ + return d->statusMessage; +} + +void Client::setStatusMessage( const QString &message ) +{ + d->statusMessage = message; +} + +QCString Client::ipAddress() const +{ + //!TODO determine ip address + return "127.0.0.1"; +} + +void Client::notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal ) +{ + emit taskError( s, errCode, fatal ); +} + +void Client::notifySocketError( int errCode, const QString& msg ) +{ + emit socketError( errCode, msg ); +} + +void Client::sendMessage( const Oscar::Message& msg, bool isAuto) +{ + Connection* c = 0L; + if ( msg.type() == 0x0003 ) + { + c = d->connections.connectionForChatRoom( msg.exchange(), msg.chatRoom() ); + if ( !c ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending message to chat room" << endl; + ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), msg.exchange(), msg.chatRoom() ); + cst->setMessage( msg ); + cst->setEncoding( d->codecProvider->codecForAccount()->name() ); + cst->go( true ); + } + else + { + c = d->connections.connectionForFamily( 0x0004 ); + if ( !c ) + return; + SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() ); + // Set whether or not the message is an automated response + sendMsgTask->setAutoResponse( isAuto ); + sendMsgTask->setMessage( msg ); + sendMsgTask->go( true ); + } +} + +void Client::receivedMessage( const Oscar::Message& msg ) +{ + if ( msg.type() == 2 && !msg.hasProperty( Oscar::Message::AutoResponse ) ) + { + // type 2 message needs an autoresponse, regardless of type + Connection* c = d->connections.connectionForFamily( 0x0004 ); + if ( !c ) + return; + + Oscar::Message response ( msg ); + if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) ) + { + QTextCodec* codec = d->codecProvider->codecForContact( msg.sender() ); + response.setText( Oscar::Message::UserDefined, statusMessage(), codec ); + } + else + { + response.setEncoding( Oscar::Message::UserDefined ); + response.setTextArray( QByteArray() ); + } + response.setReceiver( msg.sender() ); + response.addProperty( Oscar::Message::AutoResponse ); + SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() ); + sendMsgTask->setMessage( response ); + sendMsgTask->go( true ); + } + if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) ) + { + if ( msg.hasProperty( Oscar::Message::AutoResponse ) ) + { + // we got a response to a status message request. + QString awayMessage( msg.text( d->codecProvider->codecForContact( msg.sender() ) ) ); + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received an away message: " << awayMessage << endl; + emit receivedAwayMessage( msg.sender(), awayMessage ); + } + } + else if ( ! msg.hasProperty( Oscar::Message::AutoResponse ) ) + { + // Filter out miranda's invisible check + if ( msg.messageType() == 0x0004 && msg.textArray().isEmpty() ) + return; + + // let application handle it + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Emitting receivedMessage" << endl; + emit messageReceived( msg ); + } +} + +void Client::requestAuth( const QString& contactid, const QString& reason ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + d->ssiAuthTask->sendAuthRequest( contactid, reason ); +} + +void Client::sendAuth( const QString& contactid, const QString& reason, bool auth ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + d->ssiAuthTask->sendAuthReply( contactid, reason, auth ); +} + +bool Client::isActive() const +{ + return d->active; +} + +bool Client::isIcq() const +{ + return d->isIcq; +} + +void Client::setIsIcq( bool isIcq ) +{ + d->isIcq = isIcq; +} + +void Client::debug( const QString& str ) +{ + Q_UNUSED(str); +// qDebug( "CLIENT: %s", str.ascii() ); +} + +void Client::initializeStaticTasks() +{ + //set up the extra tasks + Connection* c = d->connections.defaultConnection(); + if ( !c ) + return; + d->errorTask = new ErrorTask( c->rootTask() ); + d->onlineNotifier = new OnlineNotifierTask( c->rootTask() ); + d->ownStatusTask = new OwnUserInfoTask( c->rootTask() ); + d->messageReceiverTask = new MessageReceiverTask( c->rootTask() ); + d->ssiAuthTask = new SSIAuthTask( c->rootTask() ); + d->icqInfoTask = new ICQUserInfoRequestTask( c->rootTask() ); + d->userInfoTask = new UserInfoTask( c->rootTask() ); + d->typingNotifyTask = new TypingNotifyTask( c->rootTask() ); + d->ssiModifyTask = new SSIModifyTask( c->rootTask(), true ); + + connect( d->onlineNotifier, SIGNAL( userIsOnline( const QString&, const UserDetails& ) ), + this, SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ) ); + connect( d->onlineNotifier, SIGNAL( userIsOffline( const QString&, const UserDetails& ) ), + this, SLOT( offlineUser( const QString&, const UserDetails & ) ) ); + + connect( d->ownStatusTask, SIGNAL( gotInfo() ), this, SLOT( haveOwnUserInfo() ) ); + connect( d->ownStatusTask, SIGNAL( buddyIconUploadRequested() ), this, + SIGNAL( iconNeedsUploading() ) ); + + connect( d->messageReceiverTask, SIGNAL( receivedMessage( const Oscar::Message& ) ), + this, SLOT( receivedMessage( const Oscar::Message& ) ) ); + + connect( d->ssiAuthTask, SIGNAL( authRequested( const QString&, const QString& ) ), + this, SIGNAL( authRequestReceived( const QString&, const QString& ) ) ); + connect( d->ssiAuthTask, SIGNAL( authReplied( const QString&, const QString&, bool ) ), + this, SIGNAL( authReplyReceived( const QString&, const QString&, bool ) ) ); + + connect( d->icqInfoTask, SIGNAL( receivedInfoFor( const QString&, unsigned int ) ), + this, SLOT( receivedIcqInfo( const QString&, unsigned int ) ) ); + + connect( d->userInfoTask, SIGNAL( receivedProfile( const QString&, const QString& ) ), + this, SIGNAL( receivedProfile( const QString&, const QString& ) ) ); + connect( d->userInfoTask, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ), + this, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ) ); + connect( d->typingNotifyTask, SIGNAL( typingStarted( const QString& ) ), + this, SIGNAL( userStartedTyping( const QString& ) ) ); + connect( d->typingNotifyTask, SIGNAL( typingFinished( const QString& ) ), + this, SIGNAL( userStoppedTyping( const QString& ) ) ); +} + +void Client::removeGroup( const QString& groupName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing group " << groupName << " from SSI" << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->removeGroup( groupName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::addGroup( const QString& groupName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group " << groupName << " to SSI" << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->addGroup( groupName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::addContact( const QString& contactName, const QString& groupName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact " << contactName << " to SSI in group " << groupName << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->addContact( contactName, groupName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::removeContact( const QString& contactName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing contact " << contactName << " from SSI" << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->removeContact( contactName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Renaming group " << oldGroupName << " to " << newGroupName << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->renameGroup( oldGroupName, newGroupName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem ) +{ + int action = 0; //0 modify, 1 add, 2 remove TODO cleanup! + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + if ( !oldItem && newItem ) + action = 1; + if ( oldItem && !newItem ) + action = 2; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Add/Mod/Del item on server" << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + switch ( action ) + { + case 0: + if ( ssimt->modifyItem( oldItem, newItem ) ) + ssimt->go( true ); + else + delete ssimt; + break; + case 1: + if ( ssimt->addItem( newItem ) ) + ssimt->go( true ); + else + delete ssimt; + break; + case 2: + if ( ssimt->removeItem( oldItem ) ) + ssimt->go( true ); + else + delete ssimt; + break; + } +} + +void Client::changeContactGroup( const QString& contact, const QString& newGroupName ) +{ + Connection* c = d->connections.connectionForFamily( 0x0013 ); + if ( !c ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Changing " << contact << "'s group to " + << newGroupName << endl; + SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() ); + if ( ssimt->changeGroup( contact, newGroupName ) ) + ssimt->go( true ); + else + delete ssimt; +} + +void Client::requestFullInfo( const QString& contactId ) +{ + Connection* c = d->connections.connectionForFamily( 0x0015 ); + if ( !c ) + return; + d->icqInfoTask->setUser( contactId ); + d->icqInfoTask->setType( ICQUserInfoRequestTask::Long ); + d->icqInfoTask->go(); +} + +void Client::requestShortInfo( const QString& contactId ) +{ + Connection* c = d->connections.connectionForFamily( 0x0015 ); + if ( !c ) + return; + d->icqInfoTask->setUser( contactId ); + d->icqInfoTask->setType( ICQUserInfoRequestTask::Short ); + d->icqInfoTask->go(); +} + +void Client::sendWarning( const QString& contact, bool anonymous ) +{ + Connection* c = d->connections.connectionForFamily( 0x0004 ); + if ( !c ) + return; + WarningTask* warnTask = new WarningTask( c->rootTask() ); + warnTask->setContact( contact ); + warnTask->setAnonymous( anonymous ); + QObject::connect( warnTask, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ), + this, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ) ); + warnTask->go( true ); +} + +ICQGeneralUserInfo Client::getGeneralInfo( const QString& contact ) +{ + return d->icqInfoTask->generalInfoFor( contact ); +} + +ICQWorkUserInfo Client::getWorkInfo( const QString& contact ) +{ + return d->icqInfoTask->workInfoFor( contact ); +} + +ICQEmailInfo Client::getEmailInfo( const QString& contact ) +{ + return d->icqInfoTask->emailInfoFor( contact ); +} + +ICQMoreUserInfo Client::getMoreInfo( const QString& contact ) +{ + return d->icqInfoTask->moreInfoFor( contact ); +} + +ICQInterestInfo Client::getInterestInfo( const QString& contact ) +{ + return d->icqInfoTask->interestInfoFor( contact ); +} + +ICQShortInfo Client::getShortInfo( const QString& contact ) +{ + return d->icqInfoTask->shortInfoFor( contact ); +} + +QValueList<int> Client::chatExchangeList() const +{ + return d->exchanges; +} + +void Client::setChatExchangeList( const QValueList<int>& exchanges ) +{ + d->exchanges = exchanges; +} + +void Client::requestAIMProfile( const QString& contact ) +{ + d->userInfoTask->requestInfoFor( contact, UserInfoTask::Profile ); +} + +void Client::requestAIMAwayMessage( const QString& contact ) +{ + d->userInfoTask->requestInfoFor( contact, UserInfoTask::AwayMessage ); +} + +void Client::requestICQAwayMessage( const QString& contact, ICQStatus contactStatus ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting away message for " << contact << endl; + Oscar::Message msg; + msg.setType( 2 ); + msg.setReceiver( contact ); + msg.addProperty( Oscar::Message::StatusMessageRequest ); + switch ( contactStatus ) + { + case ICQAway: + msg.setMessageType( 0xE8 ); // away + break; + case ICQOccupied: + msg.setMessageType( 0xE9 ); // occupied + break; + case ICQNotAvailable: + msg.setMessageType( 0xEA ); // not awailable + break; + case ICQDoNotDisturb: + msg.setMessageType( 0xEB ); // do not disturb + break; + case ICQFreeForChat: + msg.setMessageType( 0xEC ); // free for chat + break; + default: + // may be a good way to deal with possible error and lack of online status message? + emit receivedAwayMessage( contact, "Sorry, this protocol does not support this type of status message" ); + return; + } + sendMessage( msg ); +} + +void Client::addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding away message request for " + << contact << " to queue" << endl; + + //remove old request if still exists + removeICQAwayMessageRequest( contact ); + + ClientPrivate::AwayMsgRequest amr = { contact, contactStatus }; + d->awayMsgRequestQueue.prepend( amr ); + + if ( !d->awayMsgRequestTimer->isActive() ) + d->awayMsgRequestTimer->start( 1000 ); +} + +void Client::removeICQAwayMessageRequest( const QString& contact ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "removing away message request for " + << contact << " from queue" << endl; + + QValueList<ClientPrivate::AwayMsgRequest>::iterator it = d->awayMsgRequestQueue.begin(); + while ( it != d->awayMsgRequestQueue.end() ) + { + if ( (*it).contact == contact ) + it = d->awayMsgRequestQueue.erase( it ); + else + it++; + } +} + +void Client::nextICQAwayMessageRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "request queue count " << d->awayMsgRequestQueue.count() << endl; + + if ( d->awayMsgRequestQueue.empty() ) + { + d->awayMsgRequestTimer->stop(); + return; + } + else + { + Connection* c = d->connections.connectionForFamily( 0x0004 ); + if ( !c ) + return; + + SNAC s = { 0x0004, 0x0006, 0x0000, 0x00000000 }; + //get time needed to restore level to initial + //for some reason when we are long under initial level + //icq server will start to block our messages + int time = c->rateManager()->timeToInitialLevel( s ); + if ( time > 0 ) + { + d->awayMsgRequestTimer->changeInterval( time ); + return; + } + else + { + d->awayMsgRequestTimer->changeInterval( 5000 ); + } + } + + ClientPrivate::AwayMsgRequest amr; + + amr = d->awayMsgRequestQueue.back(); + d->awayMsgRequestQueue.pop_back(); + requestICQAwayMessage( amr.contact, amr.contactStatus ); +} + +void Client::requestStatusInfo( const QString& contact ) +{ + d->userInfoTask->requestInfoFor( contact, UserInfoTask::General ); +} + +void Client::whitePagesSearch( const ICQWPSearchInfo& info ) +{ + Connection* c = d->connections.connectionForFamily( 0x0015 ); + if ( !c ) + return; + UserSearchTask* ust = new UserSearchTask( c->rootTask() ); + connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ), + this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) ); + connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) ); + ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works + ust->searchWhitePages( info ); +} + +void Client::uinSearch( const QString& uin ) +{ + Connection* c = d->connections.connectionForFamily( 0x0015 ); + if ( !c ) + return; + UserSearchTask* ust = new UserSearchTask( c->rootTask() ); + connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ), + this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) ); + connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) ); + ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works + ust->searchUserByUIN( uin ); +} + +void Client::updateProfile( const QString& profile ) +{ + Connection* c = d->connections.connectionForFamily( 0x0002 ); + if ( !c ) + return; + ProfileTask* pt = new ProfileTask( c->rootTask() ); + pt->setProfileText( profile ); + pt->go(true); +} + +void Client::sendTyping( const QString & contact, bool typing ) +{ + Connection* c = d->connections.connectionForFamily( 0x0004 ); + if ( !c ) + return; + d->typingNotifyTask->setParams( contact, ( typing ? TypingNotifyTask::Begin : TypingNotifyTask::Finished ) ); + d->typingNotifyTask->go( false ); // don't delete the task after sending +} + +void Client::connectToIconServer() +{ + Connection* c = d->connections.connectionForFamily( 0x0010 ); + if ( c ) + return; + + requestServerRedirect( 0x0010 ); + return; +} + +void Client::setIgnore( const QString& user, bool ignore ) +{ + Oscar::SSI item = ssiManager()->findItem( user, ROSTER_IGNORE ); + if ( item && !ignore ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from ignore list" << endl; + this->modifySSIItem( item, Oscar::SSI() ); + } + else if ( !item && ignore ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to ignore list" << endl; + Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_IGNORE, QValueList<TLV>() ); + this->modifySSIItem( Oscar::SSI(), s ); + } +} + +void Client::setVisibleTo( const QString& user, bool visible ) +{ + Oscar::SSI item = ssiManager()->findItem( user, ROSTER_VISIBLE ); + if ( item && !visible ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from visible list" << endl; + this->modifySSIItem( item, Oscar::SSI() ); + } + else if ( !item && visible ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to visible list" << endl; + Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE, QValueList<TLV>() ); + this->modifySSIItem( Oscar::SSI(), s ); + } +} + +void Client::setInvisibleTo( const QString& user, bool invisible ) +{ + Oscar::SSI item = ssiManager()->findItem( user, ROSTER_INVISIBLE ); + if ( item && !invisible ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from invisible list" << endl; + this->modifySSIItem( item, Oscar::SSI() ); + } + else if ( !item && invisible ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to invisible list" << endl; + Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE, QValueList<TLV>() ); + this->modifySSIItem( Oscar::SSI(), s ); + } +} + +void Client::requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType ) +{ + Connection* c = d->connections.connectionForFamily( 0x0010 ); + if ( !c ) + return; + + BuddyIconTask* bit = new BuddyIconTask( c->rootTask() ); + connect( bit, SIGNAL( haveIcon( const QString&, QByteArray ) ), + this, SIGNAL( haveIconForContact( const QString&, QByteArray ) ) ); + bit->requestIconFor( user ); + bit->setHashType( hashType ); + bit->setHash( hash ); + bit->go( true ); +} + +void Client::requestServerRedirect( WORD family, WORD exchange, + QByteArray cookie, WORD instance, + const QString& room ) +{ + //making the assumption that family 2 will always be the BOS connection + //use it instead since we can't query for family 1 + Connection* c = d->connections.connectionForFamily( family ); + if ( c && family != 0x000E ) + return; //we already have the connection + + c = d->connections.connectionForFamily( 0x0002 ); + if ( !c ) + return; + + if ( d->redirectionServices.findIndex( family ) == -1 ) + d->redirectionServices.append( family ); //don't add families twice + + if ( d->currentRedirect != 0 ) + return; //we're already doing one redirection + + d->currentRedirect = family; + + //FIXME. this won't work if we have to defer the connection because we're + //already connecting to something + ServerRedirectTask* srt = new ServerRedirectTask( c->rootTask() ); + if ( family == 0x000E ) + { + srt->setChatParams( exchange, cookie, instance ); + srt->setChatRoom( room ); + } + + connect( srt, SIGNAL( haveServer( const QString&, const QByteArray&, WORD ) ), + this, SLOT( haveServerForRedirect( const QString&, const QByteArray&, WORD ) ) ); + srt->setService( family ); + srt->go( true ); +} + +void Client::haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD ) +{ + //nasty sender() usage to get the task with chat room info + QObject* o = const_cast<QObject*>( sender() ); + ServerRedirectTask* srt = dynamic_cast<ServerRedirectTask*>( o ); + + //create a new connection and set it up + int colonPos = host.find(':'); + QString realHost, realPort; + if ( colonPos != -1 ) + { + realHost = host.left( colonPos ); + realPort = host.right(4); //we only need 4 bytes + } + else + { + realHost = host; + realPort = QString::fromLatin1("5190"); + } + + Connection* c = createConnection( realHost, realPort ); + //create the new login task + m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() ); + m_loginTaskTwo->setCookie( cookie ); + QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( serverRedirectFinished() ) ); + + //connect + connectToServer( c, d->host, false ); + QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) ); + + if ( srt ) + d->connections.addChatInfoForConnection( c, srt->chatExchange(), srt->chatRoomName() ); +} + +void Client::serverRedirectFinished() +{ + if ( m_loginTaskTwo->statusCode() == 0 ) + { //stage two was successful + Connection* c = d->connections.connectionForFamily( d->currentRedirect ); + if ( !c ) + return; + ClientReadyTask* crt = new ClientReadyTask( c->rootTask() ); + crt->setFamilies( c->supportedFamilies() ); + crt->go( true ); + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "redirection finished for service " + << d->currentRedirect << endl; + + if ( d->currentRedirect == 0x0010 ) + emit iconServerConnected(); + + if ( d->currentRedirect == 0x000D ) + { + connect( this, SIGNAL( chatNavigationConnected() ), + this, SLOT( requestChatNavLimits() ) ); + emit chatNavigationConnected(); + } + + if ( d->currentRedirect == 0x000E ) + { + //HACK! such abuse! think of a better way + if ( !m_loginTaskTwo ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "no login task to get connection from!" << endl; + emit redirectionFinished( d->currentRedirect ); + return; + } + + Connection* c = m_loginTaskTwo->client(); + QString roomName = d->connections.chatRoomForConnection( c ); + WORD exchange = d->connections.exchangeForConnection( c ); + if ( c ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting up chat connection" << endl; + ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), exchange, roomName ); + connect( cst, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ), + this, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ) ); + connect( cst, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ), + this, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ) ); + connect( cst, SIGNAL( newChatMessage( const Oscar::Message& ) ), + this, SIGNAL( messageReceived( const Oscar::Message& ) ) ); + } + emit chatRoomConnected( exchange, roomName ); + } + + emit redirectionFinished( d->currentRedirect ); + +} + +void Client::checkRedirectionQueue( WORD family ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checking redirection queue" << endl; + d->redirectionServices.remove( family ); + d->currentRedirect = 0; + if ( !d->redirectionServices.isEmpty() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "scheduling new redirection" << endl; + requestServerRedirect( d->redirectionServices.front() ); + } +} + + +void Client::requestChatNavLimits() +{ + Connection* c = d->connections.connectionForFamily( 0x000D ); + if ( !c ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting chat nav service limits" << endl; + ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() ); + cnst->setRequestType( ChatNavServiceTask::Limits ); + QObject::connect( cnst, SIGNAL( haveChatExchanges( const QValueList<int>& ) ), + this, SLOT( setChatExchangeList( const QValueList<int>& ) ) ); + cnst->go( true ); //autodelete + +} + +void Client::determineDisconnection( int code, const QString& string ) +{ + if ( !sender() ) + return; + + //yay for the sender() hack! + QObject* obj = const_cast<QObject*>( sender() ); + Connection* c = dynamic_cast<Connection*>( obj ); + if ( !c ) + return; + + if ( c->isSupported( 0x0002 ) || + d->stage == ClientPrivate::StageOne ) //emit on login + { + emit socketError( code, string ); + } + + //connection is deleted. deleteLater() is used + d->connections.remove( c ); + c = 0; +} + +void Client::sendBuddyIcon( const QByteArray& iconData ) +{ + Connection* c = d->connections.connectionForFamily( 0x0010 ); + if ( !c ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icon length is " << iconData.size() << endl; + BuddyIconTask* bit = new BuddyIconTask( c->rootTask() ); + bit->uploadIcon( iconData.size(), iconData ); + bit->go( true ); +} + +void Client::joinChatRoom( const QString& roomName, int exchange ) +{ + Connection* c = d->connections.connectionForFamily( 0x000D ); + if ( !c ) + return; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "joining the chat room '" << roomName + << "' on exchange " << exchange << endl; + ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() ); + connect( cnst, SIGNAL( connectChat( WORD, QByteArray, WORD, const QString& ) ), + this, SLOT( setupChatConnection( WORD, QByteArray, WORD, const QString& ) ) ); + cnst->createRoom( exchange, roomName ); + +} + +void Client::setupChatConnection( WORD exchange, QByteArray cookie, WORD instance, const QString& room ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is:" << cookie << endl; + QByteArray realCookie( cookie ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "connection to chat room" << endl; + requestServerRedirect( 0x000E, exchange, realCookie, instance, room ); +} + +void Client::disconnectChatRoom( WORD exchange, const QString& room ) +{ + Connection* c = d->connections.connectionForChatRoom( exchange, room ); + if ( !c ) + return; + + d->connections.remove( c ); + c = 0; +} + + +Connection* Client::createConnection( const QString& host, const QString& port ) +{ + KNetworkConnector* knc = new KNetworkConnector( 0 ); + knc->setOptHostPort( host, port.toUInt() ); + ClientStream* cs = new ClientStream( knc, 0 ); + cs->setNoopTime( 60000 ); + Connection* c = new Connection( knc, cs, "BOS" ); + cs->setConnection( c ); + c->setClient( this ); + return c; +} + +void Client::deleteStaticTasks() +{ + delete d->errorTask; + delete d->onlineNotifier; + delete d->ownStatusTask; + delete d->messageReceiverTask; + delete d->ssiAuthTask; + delete d->icqInfoTask; + delete d->userInfoTask; + delete d->typingNotifyTask; + delete d->ssiModifyTask; + + d->errorTask = 0; + d->onlineNotifier = 0; + d->ownStatusTask = 0; + d->messageReceiverTask = 0; + d->ssiAuthTask = 0; + d->icqInfoTask = 0; + d->userInfoTask = 0; + d->typingNotifyTask = 0; + d->ssiModifyTask = 0; +} + +bool Client::hasIconConnection( ) const +{ + Connection* c = d->connections.connectionForFamily( 0x0010 ); + return c; +} + +#include "client.moc" +//kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/client.h b/kopete/protocols/oscar/liboscar/client.h new file mode 100644 index 00000000..f5dc531e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/client.h @@ -0,0 +1,521 @@ +/* + Kopete Oscar Protocol + client.h - The main interface for the Oscar protocol + + Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + + +#ifndef LIBOSCAR_CLIENT_H +#define LIBOSCAR_CLIENT_H + +#include <qobject.h> +#include <qstring.h> +#include "kopete_export.h" +#include "rtf2html.h" +#include "transfer.h" +#include "icquserinfo.h" +#include "userdetails.h" +#include "oscartypeclasses.h" +#include "oscarmessage.h" + +class Connection; +class StageOneLoginTask; +class StageTwoLoginTask; +class SSIManager; +class UserDetails; +class QString; +class Task; +class QTextCodec; + +namespace Oscar +{ +class Settings; +} + +class KOPETE_EXPORT Client : public QObject +{ +Q_OBJECT + +public: + + class CodecProvider { + public: + virtual ~CodecProvider() {} + virtual QTextCodec* codecForContact( const QString& contactName ) const = 0; + virtual QTextCodec* codecForAccount() const = 0; + }; + + enum ErrorCodes { + NoError = 0, + NotConnectedError = 1, + NonFatalProtocolError = 2, + FatalProtocolError = 3 + }; + + enum AIMStatus { Online = 0, Away }; + enum ICQStatus { ICQOnline = 0, ICQAway, ICQNotAvailable, ICQOccupied, ICQDoNotDisturb, ICQFreeForChat }; + + /************* + EXTERNAL API + *************/ + + Client(QObject *parent=0); + ~Client(); + + /** + * Get the settings object for this client instance + */ + Oscar::Settings* clientSettings() const; + + /** + * Start a connection to the server using the supplied @ref ClientStream. + * This is only a transport layer connection. + * @param s initialised connection object to use for the connection. + * @param server the server to connect to - but this is also set on the connector used to construct the clientstream?? + * @param auth indicate whether we're connecting to the authorizer or the bos server + */ + void connectToServer( Connection *c, const QString& server, bool auth = true ); + + /** + * Start the login process for Oscar + * @param host - probably could obtain this back from the connector - used for outgoing tasks to determine destination + * @param user The user name to log in as. + * @param pass The password to use when logging in + */ + void start( const QString &host, const uint port, const QString &userId, const QString &pass ); + + /** Logout and disconnect */ + void close(); + /** Set our status for AIM */ + void setStatus( AIMStatus status, const QString &message = QString::null ); + /** Set our status for ICQ */ + void setStatus( DWORD status, const QString &message = QString::null ); + + /** Retrieve our user info */ + UserDetails ourInfo() const; + + /** + * Remove a group to the contact list + * \param groupName the name of the group to remove + * \return true if the group removal was successful + */ + void removeGroup( const QString& groupName ); + + /** + * Add a group from the contact list + * \param groupName the name of the group to add + * \return true if the group addition was successful + */ + void addGroup( const QString& groupName ); + + /** + * Add a contact to the contact list + * \param contactName the screen name of the new contact to add + * \return true if the contact addition was successful + */ + void addContact( const QString& contactName, const QString& groupName ); + + /** + * Remove a contact from the contact list + * \param contactName the screen name of the contact to remove + * \return true if the contact removal was successful + */ + void removeContact( const QString &contactName ); + + /** + * Rename a group on the contact list + * \param oldGroupName the old group name + * \param newGroupName the new group name + */ + void renameGroup( const QString& oldGroupName, const QString& newGroupName ); + + /** + * Modify an SSI item on the SSI list + * \param item the item to send to the server + */ + void modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem ); + + /** + * Change a contact's group on the server + * \param contact the contact to change + * \param newGroup the new group to move the contact to + */ + void changeContactGroup( const QString& contact, const QString& newGroupName ); + + /** + * Send a message to a contact + * \param msg the message to be sent + * \param auto the message is an autoresponse message, default to false + */ + void sendMessage( const Oscar::Message& msg, bool isAuto = false ); + + /** + * Request authorization from a contact + * \param contactid the id of the contact to request auth from + * \param reason the reason for this authorization request + */ + void requestAuth( const QString& contactid, const QString& reason ); + + /** + * Grant or decline authorization to a contact + * \param contactid the id of the contact to grant/decline authorization + * \param reason the reason to grant/decline authorization + * \param auth grant or decline authorization + */ + void sendAuth( const QString& contactid, const QString& reason, bool auth=true ); + + /** + * Request full user info from an ICQ contact + * \param contactId the UIN of the contact to get info for + */ + void requestFullInfo( const QString& contactId ); + + /** + * Request short info for an ICQ contact + * \param contactId the UIN of the contact to get info for + */ + void requestShortInfo( const QString& contactId ); + + /** + * Send a warning to the OSCAR servers about a contact + * \param contact the contact to send the warning to + * \param anon indicate whether to do it anonymously + */ + void sendWarning( const QString& contact, bool anonymous ); + + /** + * Get the general ICQ info for a client + * \param contact the contact to get info for + */ + ICQGeneralUserInfo getGeneralInfo( const QString& contact ); + + /** + * Get the work info for a contact + * \param contact the contact to get info for + */ + ICQWorkUserInfo getWorkInfo( const QString& contact ); + + /** + * Get the email info for a contact + * \param contact the contact to get info for + */ + ICQEmailInfo getEmailInfo( const QString& contact ); + + /** + * Get the additional info available for a contact + * \param contact the contact to get info for + */ + ICQMoreUserInfo getMoreInfo( const QString& contact ); + + /** + * Get the interest info available for a contact + * \param contact the contact to get info for + */ + ICQInterestInfo getInterestInfo( const QString& contact ); + + /** + * Get the short info available for an icq contact + * \param contact the contact to get info for + */ + ICQShortInfo getShortInfo( const QString& contact ); + + /** + * Get the list of chat room exchanges we have + */ + QValueList<int> chatExchangeList() const; + + /** + * Request the aim profile + * \param contact the contact to get info for + */ + void requestAIMProfile( const QString& contact ); + + /** + * Request the aim away message + * \param contact the contact to get info for + */ + void requestAIMAwayMessage( const QString& contact ); + + /** + * Add the icq away message request to queue + * \param contact the contact to get info for + */ + void addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus ); + + /** + * Remove the icq away message request from queue + * \param contact the contact to get info for + */ + void removeICQAwayMessageRequest( const QString& contact ); + + /** Request the extended status info */ + void requestStatusInfo( const QString& contact ); + + //! Run a whitepages search + void whitePagesSearch( const ICQWPSearchInfo& info ); + + //! Run a UIN search + void uinSearch( const QString& uin ); + + //! Update the user's AIM profile + void updateProfile( const QString& profile ); + + //! Get buddy icon information for a person + void requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType ); + + //! Start a server redirect for a different service + void requestServerRedirect( WORD family, WORD e = 0, QByteArray c = QByteArray(), + WORD instance = 0, const QString& room = QString::null ); + + //! Start uploading a buddy icon + void sendBuddyIcon( const QByteArray& imageData ); + + void joinChatRoom( const QString& roomName, int exchange ); + + void setIgnore( const QString& user, bool ignore ); + + void setVisibleTo( const QString& user, bool visible ); + + void setInvisibleTo( const QString& user, bool invisible ); + + /** Accessors needed for login */ + QString host(); + int port(); + + /** Send a typing notification */ + void sendTyping( const QString & contact, bool typing ); + + /** Make a connection to the icon server */ + void connectToIconServer(); + + bool hasIconConnection() const; + + /** We've finished chatting in a chat room, disconnect from it */ + void disconnectChatRoom( WORD exchange, const QString& room ); + + /** Set codec provider */ + void setCodecProvider( CodecProvider* codecProvider ); + + /** Set pointer to version info */ + void setVersion( const Oscar::ClientVersion* version ); + + /************* + INTERNAL (FOR USE BY TASKS OR CONNECTIONS) METHODS + *************/ + /** + * Print a debug statement + */ + void debug( const QString &str ); + + /** Have we logged in yet? */ + bool isActive() const; + + /** Accessor for the SSI Manager */ + SSIManager* ssiManager() const; + + /** Return version info */ + const Oscar::ClientVersion* version() const; + + /** The current user's user ID */ + QString userId() const; + + /** The current user's password */ + QString password() const; + + /** The current status message (a.k.a. away message) */ + QString statusMessage() const; + + /** Change the current status message w/o changing status */ + void setStatusMessage( const QString &message ); + + /** ICQ Settings */ + bool isIcq() const; + void setIsIcq( bool isIcq ); + + /** Host's IP address */ + QCString ipAddress() const; + + /** Notify that a task error was received */ + void notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal ); + + /** Notify that a socket error has occured */ + void notifySocketError( int errCode, const QString& msg ); + +signals: + /** CONNECTION EVENTS */ + + /** Notifies that the login process has succeeded. */ + void loggedIn(); + + /** Notifies that the login process has failed */ + void loginFailed(); + + /** Notifies tasks and account so they can react properly */ + void disconnected(); + + /** We were disconnected because we connected elsewhere */ + void connectedElsewhere(); + + /** We have our own user info */ + void haveOwnInfo(); + + /** We have our SSI list */ + void haveSSIList(); + + /** a user is online. */ + void userIsOnline( const QString& ); + + /** a user is offline. */ + void userIsOffline( const QString& ); + + /** we've received a message */ + void messageReceived( const Oscar::Message& ); + + /** we've received an authorization request */ + void authRequestReceived( const QString& contact, const QString& reason ); + + /** we've received an authorization reply */ + void authReplyReceived( const QString& contact, const QString& reason, bool auth ); + + /** + * we've received an error from a task and need to notify somebody + */ + void taskError( const Oscar::SNAC& s, int errCode, bool fatal ); + + /** + * we've received a socket error and need to notify somebody + */ + void socketError( int errCode, const QString& msg ); + + void receivedIcqShortInfo( const QString& contact ); + void receivedIcqLongInfo( const QString& contact ); + + void receivedProfile( const QString& contact, const QString& profile ); + void receivedAwayMessage( const QString& contact, const QString& message ); + void receivedAwayMessage( const Oscar::Message& message ); + void receivedUserInfo( const QString& contact, const UserDetails& details ); + + /** We warned a user */ + void userWarned( const QString& contact, Q_UINT16 increase, Q_UINT16 newLevel ); + + /** Search signals */ + void gotSearchResults( const ICQSearchResult& ); + void endOfSearch( int); + + /* Typing signals */ + void userStartedTyping( const QString& contact ); + void userStoppedTyping( const QString& contact ); + + /* Buddy icons */ + void haveIconForContact( const QString&, QByteArray iconData ); + void iconServerConnected(); + void iconNeedsUploading(); + + /* Chat rooms */ + void chatNavigationConnected(); + void chatRoomConnected( WORD, const QString& ); + void userJoinedChat( Oscar::WORD, const QString& room, const QString& contact ); + void userLeftChat( Oscar::WORD, const QString& room, const QString& contact ); + + /* service redirection */ + void redirectionFinished( WORD ); + + +protected slots: + // INTERNAL, FOR USE BY TASKS' finished() SIGNALS // + + /** Singleshot timer to start stage two login */ + void startStageTwo(); + + /** + * A login task finished. For stage one, this means we've either errored + * out, or gotten a cookie. For stage two, this means we've either done + * something wrong, or we're successfully connected + */ + void lt_loginFinished(); + + /** Stream connected for stage two login */ + void streamConnected(); + + /** We have our own user info */ + void haveOwnUserInfo(); + + /** Service setup finished */ + void serviceSetupFinished(); + + /** we have icq info for a contact */ + void receivedIcqInfo( const QString& contact, unsigned int type ); + + /** we have normal user info for a contact */ + void receivedInfo( Q_UINT16 sequence ); + + /** received a message of some kind */ + void receivedMessage( const Oscar::Message& msg ); + + void offlineUser( const QString&, const UserDetails& ); + + void haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD family ); + void serverRedirectFinished(); + void checkRedirectionQueue( WORD ); + + void requestChatNavLimits(); + /** + * Set the list of chat room exchanges we have + */ + void setChatExchangeList( const QValueList<int>& exchanges ); + + /** + * set up the connection to a chat room + */ + void setupChatConnection( WORD, QByteArray, WORD, const QString& ); + + + void determineDisconnection( int, const QString& ); + + void nextICQAwayMessageRequest(); + +private: + + /** Initialize some static tasks */ + void initializeStaticTasks(); + + /** Delete the static tasks */ + void deleteStaticTasks(); + + Connection* createConnection( const QString& host, const QString& port ); + + /** + * Request the icq away message + * \param contact the contact to get info for + */ + //TODO only made a default for testing w/o frontend + void requestICQAwayMessage( const QString& contact, ICQStatus contactStatus = ICQAway ); + +private: + class ClientPrivate; + ClientPrivate* d; + + StageOneLoginTask* m_loginTask; + StageTwoLoginTask* m_loginTaskTwo; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; + + diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.cpp b/kopete/protocols/oscar/liboscar/clientreadytask.cpp new file mode 100644 index 00000000..3338f7b3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/clientreadytask.cpp @@ -0,0 +1,109 @@ +/* + Kopete Oscar Protocol + $FILENAME.cpp + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "clientreadytask.h" + +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "rateclass.h" +#include "rateclassmanager.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +using namespace Oscar; + +ClientReadyTask::ClientReadyTask(Task* parent): Task(parent) +{ + m_classList = client()->rateManager()->classList(); +} + + +ClientReadyTask::~ClientReadyTask() +{ +} + +void ClientReadyTask::setFamilies( const QValueList<int>& families ) +{ + m_familyList = families; +} + + +void ClientReadyTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending client ready, end of login" << endl; + //nasty nasty nasty hack to get all the packets to work + QValueList<int>::const_iterator rcEnd = m_familyList.constEnd(); + for ( QValueList<int>::const_iterator it = m_familyList.constBegin(); it != rcEnd; ++it ) + { + //I have no idea what any of these values mean. I just copied them from oscarsocket + int i = ( *it ); + buffer->addWord( i ); + switch ( i ) + { + case 0x0001: + buffer->addWord( 0x0003 ); + break; + case 0x0013: + buffer->addWord( client()->isIcq() ? 0x0002 : 0x0003 ); + break; + default: + buffer->addWord( 0x0001 ); + }; + + if ( client()->isIcq() ) + { + if ( i == 0x0002 ) + buffer->addWord( 0x0101 ); + else + buffer->addWord( 0x0110 ); + + //always add 0x047B + buffer->addWord( 0x047B ); + } + else //we're AIM so AOL has us do something completely different! *sigh* + { + switch( i ) + { + case 0x0008: + case 0x000B: + case 0x000C: + buffer->addWord( 0x0104 ); + buffer->addWord( 0x0001 ); + break; + default: + buffer->addWord( 0x0110 ); + buffer->addWord( 0x059B ); + break; + }; + } + } + + //send the damn thing so we can finally be finished + //with the hell that is oscar login. (just wait until you get a message) + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); + +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.h b/kopete/protocols/oscar/liboscar/clientreadytask.h new file mode 100644 index 00000000..4a9ea941 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/clientreadytask.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + + Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef CLIENTREADYTASK_H +#define CLIENTREADYTASK_H + +#include "task.h" + +#include "rateclass.h" +#include "qvaluelist.h" + +/** +Fire and forget task to let the server know we're ready + +@author Matt Rogers +*/ +class ClientReadyTask : public Task +{ +public: + ClientReadyTask( Task* parent ); + ~ClientReadyTask(); + virtual void onGo(); + + void setFamilies( const QValueList<int>& families ); + +private: + QValueList<RateClass*> m_classList; + QValueList<int> m_familyList; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp new file mode 100644 index 00000000..54926949 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp @@ -0,0 +1,146 @@ +/* + Kopete Oscar Protocol + closeconnectiontask.h - Handles the closing of the connection to the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "closeconnectiontask.h" + +#include <qstring.h> +#include <qvaluelist.h> +#include <kdebug.h> +#include <klocale.h> +#include "connection.h" +#include "transfer.h" +#include "oscarutils.h" + +using namespace Oscar; + +CloseConnectionTask::CloseConnectionTask( Task* parent ) + : Task(parent) +{ +} + + +CloseConnectionTask::~CloseConnectionTask() +{ +} + +const QByteArray& CloseConnectionTask::cookie() const +{ + return m_cookie; +} + +const QString& CloseConnectionTask::bosHost() const +{ + return m_bosHost; +} + +const QString& CloseConnectionTask::bosPort() const +{ + return m_bosPort; +} + +bool CloseConnectionTask::take( Transfer* transfer ) +{ + QString errorReason; + WORD errorNum = 0; + if ( forMe( transfer ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV (DISCONNECT)" << endl; + + FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer ); + + if ( !ft ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "Could not convert transfer object to type FlapTransfer!!" << endl; + return false; + } + + QValueList<TLV> tlvList = ft->buffer()->getTLVList(); + + TLV uin = findTLV( tlvList, 0x0001 ); + if ( uin ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [UIN], uin=" << QString( uin.data ) << endl; + } + + TLV err = findTLV( tlvList, 0x0008 ); + if ( !err ) + err = findTLV( tlvList, 0x0009 ); + + if ( err.type == 0x0008 || err.type == 0x0009 ) + { + errorNum = ( ( err.data[0] << 8 ) | err.data[1] ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(8) [ERROR] error= " << errorNum << endl; + + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, errorNum ); + return true; //if there's an error, we'll need to disconnect anyways + } + + TLV server = findTLV( tlvList, 0x0005 ); + if ( server ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << QString( server.data ) << endl; + QString ip = server.data; + int index = ip.find( ':' ); + m_bosHost = ip.left( index ); + ip.remove( 0 , index+1 ); //get rid of the colon and everything before it + m_bosPort = ip; + } + + TLV cookie = findTLV( tlvList, 0x0006 ); + if ( cookie ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl; + m_cookie.duplicate( cookie.data ); + } + + tlvList.clear(); + + if ( m_bosHost.isEmpty() ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Empty host address!" << endl; + + Oscar::SNAC s = { 0, 0, 0, 0 }; + client()->fatalTaskError( s, 0 ); + return true; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "We should reconnect to server '" + << m_bosHost << "' on port " << m_bosPort << endl; + setSuccess( errorNum, errorReason ); + return true; + } + return false; +} + +bool CloseConnectionTask::forMe( const Transfer* transfer ) const +{ + const FlapTransfer* ft = dynamic_cast<const FlapTransfer*> ( transfer ); + + if (!ft) + return false; + + if ( ft && ft->flapChannel() == 4 ) + return true; + else + return false; +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.h b/kopete/protocols/oscar/liboscar/closeconnectiontask.h new file mode 100644 index 00000000..b241b07e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.h @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + closeconnectiontask.h - Handles the closing of the connection to the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CLOSECONNECTIONTASK_H +#define CLOSECONNECTIONTASK_H + +#include <task.h> +#include <qcstring.h> + +class Transfer; +class QString; + +/** +@author Matt Rogers +*/ +class CloseConnectionTask : public Task +{ +public: + CloseConnectionTask(Task* parent); + + ~CloseConnectionTask(); + + virtual bool take(Transfer* transfer); + + //Protocol specific stuff + const QByteArray& cookie() const; + const QString& bosHost() const; + const QString& bosPort() const; + + +protected: + virtual bool forMe(const Transfer* transfer) const; + +private: + bool parseDisconnectCode( int error, QString& reason ); + +private: + QByteArray m_cookie; + QString m_bosHost; + QString m_bosPort; + + +}; + +#endif + +//kate: indent-mode csands; space-indent off; tab-width 4; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/connection.cpp b/kopete/protocols/oscar/liboscar/connection.cpp new file mode 100644 index 00000000..c7cbc0fe --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connection.cpp @@ -0,0 +1,248 @@ +/* + Kopete Oscar Protocol + connection.cpp - independent protocol encapsulation + + Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "connection.h" +#include "client.h" +#include "connector.h" +#include "oscarclientstream.h" +#include "rateclassmanager.h" +#include "task.h" +#include "transfer.h" +#include <kapplication.h> +#include <kdebug.h> + +#include "oscartypeclasses.h" + + +class ConnectionPrivate +{ +public: + DWORD snacSequence; + WORD flapSequence; + + QValueList<int> familyList; + RateClassManager* rateClassManager; + + ClientStream* clientStream; + Connector* connector; + Client* client; + + Task* root; +}; + + + +Connection::Connection( Connector* connector, ClientStream* cs, const char* name ) +: QObject( 0, name ) +{ + d = new ConnectionPrivate(); + d->clientStream = cs; + d->client = 0; + d->connector = connector; + d->rateClassManager = new RateClassManager( this ); + d->root = new Task( this, true /* isRoot */ ); + m_loggedIn = false; + initSequence(); + +} + +Connection::~Connection() +{ + + delete d->rateClassManager; + delete d->clientStream; + delete d->connector; + delete d->root; + delete d; +} + +void Connection::setClient( Client* c ) +{ + d->client = c; + connect( c, SIGNAL( loggedIn() ), this, SLOT( loggedIn() ) ); +} + +void Connection::connectToServer( const QString& host, bool auth ) +{ + connect( d->clientStream, SIGNAL( error( int ) ), this, SLOT( streamSocketError( int ) ) ); + connect( d->clientStream, SIGNAL( readyRead() ), this, SLOT( streamReadyRead() ) ); + connect( d->clientStream, SIGNAL( connected() ), this, SIGNAL( connected() ) ); + d->clientStream->connectToServer( host, auth ); +} + +void Connection::close() +{ + d->clientStream->close(); + reset(); +} + +bool Connection::isSupported( int family ) const +{ + return ( d->familyList.findIndex( family ) != -1 ); +} + +QValueList<int> Connection::supportedFamilies() const +{ + return d->familyList; +} + +void Connection::addToSupportedFamilies( const QValueList<int>& familyList ) +{ + d->familyList += familyList; +} + +void Connection::addToSupportedFamilies( int family ) +{ + d->familyList.append( family ); +} + +void Connection::taskError( const Oscar::SNAC& s, int errCode ) +{ + d->client->notifyTaskError( s, errCode, false /*fatal*/ ); +} + +void Connection::fatalTaskError( const Oscar::SNAC& s, int errCode ) +{ + d->client->notifyTaskError( s, errCode, true /* fatal */ ); +} + +Oscar::Settings* Connection::settings() const +{ + return d->client->clientSettings(); +} + +Q_UINT16 Connection::flapSequence() +{ + d->flapSequence++; + if ( d->flapSequence >= 0x8000 ) //the max flap sequence is 0x8000 ( HEX ) + d->flapSequence = 1; + + return d->flapSequence; +} + +Q_UINT32 Connection::snacSequence() +{ + d->snacSequence++; + return d->snacSequence; +} + +QString Connection::userId() const +{ + return d->client->userId(); +} + +QString Connection::password() const +{ + return d->client->password(); +} + +bool Connection::isIcq() const +{ + return d->client->isIcq(); +} + +Task* Connection::rootTask() const +{ + return d->root; +} + +SSIManager* Connection::ssiManager() const +{ + return d->client->ssiManager(); +} + +const Oscar::ClientVersion* Connection::version() const +{ + return d->client->version(); +} + +bool Connection::isLoggedIn() const +{ + return m_loggedIn; +} + +RateClassManager* Connection::rateManager() const +{ + return d->rateClassManager; +} + +void Connection::send( Transfer* request ) const +{ + if( !d->clientStream ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on!" << endl; + return; + } + d->rateClassManager->queue( request ); + +} + +void Connection::forcedSend( Transfer* request ) const +{ + if ( !d->clientStream ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on" << endl; + return; + } + d->clientStream->write( request ); +} + +void Connection::initSequence() +{ + d->snacSequence = ( KApplication::random() & 0xFFFF ); + d->flapSequence = ( KApplication::random() & 0xFFFF ); +} + +void Connection::distribute( Transfer * transfer ) const +{ + //d->rateClassManager->recalcRateLevels(); + if( !rootTask()->take( transfer ) ) + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "root task refused transfer" << endl; + + delete transfer; +} + +void Connection::reset() +{ + //clear the family list + d->familyList.clear(); + d->rateClassManager->reset(); +} + +void Connection::streamReadyRead() +{ + // take the incoming transfer and distribute it to the task tree + Transfer * transfer = d->clientStream->read(); + distribute( transfer ); +} + +void Connection::loggedIn() +{ + m_loggedIn = true; +} + +void Connection::streamSocketError( int code ) +{ + emit socketError( code, d->clientStream->errorText() ); +} + +#include "connection.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/connection.h b/kopete/protocols/oscar/liboscar/connection.h new file mode 100644 index 00000000..4170857e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connection.h @@ -0,0 +1,209 @@ +/* +Kopete Oscar Protocol +connection.h - independent protocol encapsulation + +Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org> + +Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + +************************************************************************* +* * +* 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 of the License, or (at your option) any later version. * +* * +************************************************************************* +*/ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include <qobject.h> +#include <qvaluelist.h> +#include "oscartypes.h" +#include "rateclass.h" + +class ConnectionPrivate; +class Client; +class ClientStream; +class Connector; +class ByteStream; +class Transfer; +class RateClassManager; +class SSIManager; +class Task; + + +namespace Oscar +{ +class Settings; +} + +/** + * This class encapsulates both the low level network layer code and the high + * level OSCAR protocol code required to create a single independent + * connection to an OSCAR server + * @author Matt Rogers + */ +class Connection : public QObject +{ +Q_OBJECT +public: + + Connection( Connector* connector, ClientStream* cs, const char* name = 0 ); + ~Connection(); + + void setClient( Client* ); + + void connectToServer( const QString& server, bool auth = true ); + /** + * Close the connection and reset the connection data + */ + void close(); + + /** + * Check to see if the family specified by @p family is supported by this + * connection. + * @param family the family number to check + */ + bool isSupported( int family ) const; + + /** + * Get the list of supported families + * @return The list of families supported on this connection + */ + QValueList<int> supportedFamilies() const; + + /** + * Add the SNAC families in \p familyList to the list of supported families for + * this connection + * \param familyList the list of families to add + */ + void addToSupportedFamilies( const QValueList<int>& familyList ); + + /** + * Add the SNAC family in \p family to the list of supported families for + * this connection + * \overload + * \param family the single family to add to the list + */ + void addToSupportedFamilies( int family ); + + /** + * Add the rate classes in \p rateClassList to the list of rate classes packets + * need to be filtered on + * \param rateClassList the list of rate classes to add + */ + void addToRateClasses( const QValueList<RateClass*> rateClassList ); + + /** + * Add the rate class in \p rc to the list of rate classes packets + * need to be filtered on + * \overload + * \param rc the list rate class to add + */ + void addToRateClasses( RateClass* rc ); + + /** + * Indicate to the connection that there has been an error in a task. The + * error won't require us to go offline, but the user should be notified + * about the error + * \param s the SNAC the error occured from + * \param errCode the error code + */ + void taskError( const Oscar::SNAC& s, int errCode ); + + /** + * Indicate to the connection that there has been a fatal error in a task. + * This error will require a disconnection from the OSCAR service and if + * necessary, the user should be prompted to reconnect manually or an + * automatic reconnection should be attempted. + * \param s the SNAC the error occured from + * \param errCode the error code + */ + void fatalTaskError( const Oscar::SNAC& s, int errCode ); + + /** + * Get the chat room name for this connection. + * @return the name of the room or QString::null if not connected to a room + */ + + /** Get the user settings object */ + Oscar::Settings* settings() const; + + /** Get the current FLAP sequence for this connection */ + Q_UINT16 flapSequence(); + + /** Get the current SNAC sequence for this connection */ + Q_UINT32 snacSequence(); + + /** Get the cookie for this connection */ + QByteArray cookie() const; + + QString userId() const; + QString password() const; + bool isIcq() const; + SSIManager* ssiManager() const; + const Oscar::ClientVersion* version() const; + RateClassManager* rateManager() const; + bool isLoggedIn() const; + + /** Convenience function to get the root task for use in Tasks */ + Task* rootTask() const; + + /** Get the raw connector for this connection, in case we need it */ + Connector* connector(); + + /** Get the byte stream for this connection, in case we need it */ + ByteStream* byteStream(); + + void send( Transfer* t ) const; + void forcedSend( Transfer* t ) const; + +signals: + + /** There's data ready to read */ + void readyRead(); + + /** We've connected */ + void connected(); + + /** We were disconnected */ + void disconnected(); + + /** + * There was an error on the socket and we've disconnected + * \param errCode the error code from the operating system + * \param errString the i18n'ed string that describes the error + */ + void socketError( int errCode, const QString& errString ); + + +private: + /** Seed the sequence numbers with random values */ + void initSequence(); + + /** Distribute the transfer among the connection's tasks */ + void distribute( Transfer* t ) const; + +private slots: + /** Reset the data for the connection.*/ + void reset(); + + /** We've got something from the stream */ + void streamReadyRead(); + + /** We've finished logging in */ + void loggedIn(); + + void streamSocketError( int ); + +private: + + ConnectionPrivate* d; + bool m_loggedIn; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; auto-insert-doxygen on; diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.cpp b/kopete/protocols/oscar/liboscar/connectionhandler.cpp new file mode 100644 index 00000000..bf5706ee --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connectionhandler.cpp @@ -0,0 +1,174 @@ +/* + Kopete Oscar Protocol + Oscar Multiple Connection Handling + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "connectionhandler.h" +#include <qvaluelist.h> +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" + +class ConnectionHandler::Private +{ +public: + QValueList<Connection*> connections; + QMap<Connection*, ConnectionRoomInfo> chatRoomConnections; +}; + +ConnectionHandler::ConnectionHandler() +{ + d = new Private; +} + + +ConnectionHandler::~ConnectionHandler() +{ + delete d; +} + +void ConnectionHandler::append( Connection* c ) +{ + d->connections.append( c ); +} + +void ConnectionHandler::remove( Connection* c ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing connection " + << c << endl; + d->connections.remove( c ); + c->deleteLater(); +} + +void ConnectionHandler::remove( int family ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing all connections " << + "supporting family " << family << endl; + QValueList<Connection*>::iterator it = d->connections.begin(); + QValueList<Connection*>::iterator itEnd = d->connections.end(); + for ( ; it != itEnd; ++it ) + { + if ( ( *it )->isSupported( family ) ) + { + Connection* c = ( *it ); + it = d->connections.remove( it ); + c->deleteLater(); + } + } +} + +void ConnectionHandler::clear() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing all connections" + << endl; + while ( !d->connections.isEmpty() ) + { + Connection *c = d->connections.front(); + d->connections.pop_front(); + c->deleteLater(); + } +} + +Connection* ConnectionHandler::connectionForFamily( int family ) const +{ + QValueList<Connection*>::iterator it = d->connections.begin(); + QValueList<Connection*>::iterator itEnd = d->connections.end(); + int connectionCount = 0; + Connection* lastConnection = 0; + for ( ; it != itEnd; ++it ) + { + if ( ( *it )->isSupported( family ) ) + { + connectionCount++; + lastConnection = ( *it ); + } + } + if ( connectionCount == 1 ) + return lastConnection; + + return 0; +} + +Connection* ConnectionHandler::defaultConnection() const +{ + if ( d->connections.isEmpty() || d->connections.count() > 1 ) + return 0; + + return d->connections.first(); +} + +void ConnectionHandler::addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room ) +{ + if ( d->connections.findIndex( c ) == -1 ) + d->connections.append( c ); + + ConnectionRoomInfo info = qMakePair( exchange, room ); + d->chatRoomConnections[c] = info; +} + +Connection* ConnectionHandler::connectionForChatRoom( Oscar::WORD exchange, const QString& room ) +{ + ConnectionRoomInfo infoToFind = qMakePair( exchange, room ); + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.data() == infoToFind ) + { + Connection* c = it.key(); + return c; + } + } + + return 0; +} + +QString ConnectionHandler::chatRoomForConnection( Connection* c ) +{ + if ( d->connections.findIndex( c ) == -1 ) + return QString::null; + + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.key() == c ) + { + QString room = it.data().second; + return room; + } + } + + return QString::null; +} + +Oscar::WORD ConnectionHandler::exchangeForConnection( Connection* c ) +{ + + if ( d->connections.findIndex( c ) == -1 ) + return 0xFFFF; + + QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end(); + for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it ) + { + if ( it.key() == c ) + { + Oscar::WORD exchange = it.data().first; + return exchange; + } + } + + return 0xFFFF; +} + diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.h b/kopete/protocols/oscar/liboscar/connectionhandler.h new file mode 100644 index 00000000..6094cab3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connectionhandler.h @@ -0,0 +1,118 @@ +/* + Kopete Oscar Protocol + Oscar Multiple Connection Handling + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CONNECTIONHANDLER_H +#define CONNECTIONHANDLER_H + +#include "oscartypes.h" +#include <qstring.h> +#include <qpair.h> + + +class Connection; + +typedef QPair<Oscar::WORD, QString> ConnectionRoomInfo; + +/** +@author Kopete Developers +*/ +class ConnectionHandler +{ +public: + ConnectionHandler(); + ~ConnectionHandler(); + + /** + * Add a connection to the handler so that it can be + * tracked and queried for later. + * @param c The connection to add to the handler + */ + void append( Connection* c ); + + /** + * Remove a connection from the handler + * @param c The connection object to remove + */ + void remove( Connection* c ); + + /** + * Remove a connection from the handler + * @param family The SNAC family for the connection to remove + */ + void remove( int family ); + + /** + * Clear all the connections. + */ + void clear(); + + /** + * Get the connection for a particular SNAC family. If there is + * more than one connection for a particular family or there is no + * connection, then zero is returned. + * @return A valid connection object for the family or 0 + */ + Connection* connectionForFamily( int family ) const; + + /** + * Get the default connection. Returns zero when we're handling more than + * one connection. + * @return The only connection object we're tracking or zero if we have + * more than one. + */ + Connection* defaultConnection() const; + + /** + * Add chat room information to a connection so that we can track + * connections by chat room + * @param c The connection to add information to + * @param exchange the exchange the chat room is in + * @param room the name of the chat room + */ + void addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room ); + + /** + * Get the connection for a particular room name and exchange number. + * @param exchange the chat room exchange the room is on + * @param room the name of the chat room to find a connection for + * @return a Connection for the chat room or 0L if no connection for that room + */ + Connection* connectionForChatRoom( Oscar::WORD exchange, const QString& room ); + + /** + * Get the room name for the chat room based the connection + * @return The name of the chat room that this connection is connected to + * If the connection passed in by @p c is not a chat room connection then + * QString::null is returned. + */ + QString chatRoomForConnection( Connection* c ); + + /** + * Get the exchange number for the chat room based on the connection + * @return The exchange of the chat room that this connection is connected + * to. If the connection passed in by @p c is not a chat room connection + * then 0xFFFF is returned + */ + Oscar::WORD exchangeForConnection( Connection* c ); + +private: + class Private; + Private* d; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/connector.cpp b/kopete/protocols/oscar/liboscar/connector.cpp new file mode 100644 index 00000000..03a61882 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connector.cpp @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + connector.cpp - the Oscar socket connector + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "connector.h" + +Connector::Connector(QObject *parent) +:QObject(parent) +{ + setPeerAddressNone(); +} + +Connector::~Connector() +{ +} + +bool Connector::havePeerAddress() const +{ + return haveaddr; +} + +QHostAddress Connector::peerAddress() const +{ + return addr; +} + +Q_UINT16 Connector::peerPort() const +{ + return port; +} + +void Connector::setPeerAddressNone() +{ + haveaddr = false; + addr = QHostAddress(); + port = 0; +} + +void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port) +{ + haveaddr = true; + addr = _addr; + port = _port; +} + +#include "connector.moc" diff --git a/kopete/protocols/oscar/liboscar/connector.h b/kopete/protocols/oscar/liboscar/connector.h new file mode 100644 index 00000000..fd673163 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/connector.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + connector.h - the Oscar socket connector + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LIBOSCAR_CONNECTOR_H +#define LIBOSCAR_CONNECTOR_H + + +#include <qobject.h> +#include "qhostaddress.h" + +class ByteStream; + +class Connector : public QObject +{ + Q_OBJECT +public: + Connector(QObject *parent=0); + virtual ~Connector(); + + virtual void connectToServer(const QString &server)=0; + virtual ByteStream *stream() const=0; + virtual void done()=0; + + bool havePeerAddress() const; + QHostAddress peerAddress() const; + Q_UINT16 peerPort() const; + +signals: + void connected(); + void error(); + +protected: + void setPeerAddressNone(); + void setPeerAddress(const QHostAddress &addr, Q_UINT16 port); + +private: + bool haveaddr; + QHostAddress addr; + Q_UINT16 port; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.cpp b/kopete/protocols/oscar/liboscar/coreprotocol.cpp new file mode 100644 index 00000000..4f114039 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/coreprotocol.cpp @@ -0,0 +1,285 @@ +/* + Kopete Oscar Protocol + coreprotocol.h- the core Oscar protocol + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + url_escape_string from Gaim src/protocols/novell/nmconn.c + Copyright (c) 2004 Novell, Inc. All Rights Reserved + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "coreprotocol.h" + +#include <qdatastream.h> +#include <qdatetime.h> +#include <qtextstream.h> + +#include <kdebug.h> +#include <ctype.h> + +#include "oscartypes.h" +#include "transfer.h" +#include "flapprotocol.h" +#include "snacprotocol.h" + +static QString toString( const QByteArray& buffer ) +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + for ( it = buffer.begin(); it != buffer.end(); ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if ( c < 0x10 ) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + + +using namespace Oscar; + +CoreProtocol::CoreProtocol() : QObject() +{ + m_snacProtocol = new SnacProtocol( this, "snacprotocol" ); + m_flapProtocol = new FlapProtocol( this, "flapprotocol" ); +} + +CoreProtocol::~CoreProtocol() +{ +} + +int CoreProtocol::state() +{ + return m_state; +} + +void CoreProtocol::addIncomingData( const QByteArray & incomingBytes ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received " << incomingBytes.count() << " bytes. " << endl; + // store locally + int oldsize = m_in.size(); + m_in.resize( oldsize + incomingBytes.size() ); + memcpy( m_in.data() + oldsize, incomingBytes.data(), incomingBytes.size() ); + m_state = Available; + + // convert every event in the chunk to a Transfer, signalling it back to the clientstream + int parsedBytes = 0; + int transferCount = 0; + // while there is data left in the input buffer, and we are able to parse something out of it + while ( m_in.size() && ( parsedBytes = wireToTransfer( m_in ) ) ) + { + transferCount++; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsed transfer #" << transferCount << " in chunk" << endl; + int size = m_in.size(); + if ( parsedBytes < size ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "more data in chunk!" << endl; + // copy the unparsed bytes into a new qbytearray and replace m_in with that + QByteArray remainder( size - parsedBytes ); + memcpy( remainder.data(), m_in.data() + parsedBytes, remainder.size() ); + m_in = remainder; + } + else + m_in.truncate( 0 ); + } + + if ( m_state == NeedMore ) + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message was incomplete, waiting for more..." << endl; + + if ( m_snacProtocol->state() == OutOfSync || m_flapProtocol->state() == OutOfSync ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "protocol thinks it's out of sync. " + << "discarding the rest of the buffer and hoping the server regains sync soon..." << endl; + m_in.truncate( 0 ); + } +// kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "done processing chunk" << endl; +} + +Transfer* CoreProtocol::incomingTransfer() +{ + if ( m_state == Available ) + { + m_state = NoData; + return m_inTransfer; + m_inTransfer = 0; + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "we shouldn't be here!" << kdBacktrace() << endl; + return 0; + } +} + +void cp_dump( const QByteArray &bytes ) +{ +#ifdef OSCAR_COREPROTOCOL_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << "contains: " << bytes.count() << " bytes" << endl; + for ( uint i = 0; i < bytes.count(); ++i ) + { + printf( "%02x ", bytes[ i ] ); + } + printf( "\n" ); +#else + Q_UNUSED( bytes ); +#endif +} + +void CoreProtocol::outgoingTransfer( Transfer* outgoing ) +{ + //kdDebug(OSCAR_RAW_DEBUG) << "CoreProtocol::outgoingTransfer()" << endl; + // Convert the outgoing data into wire format + // pretty leet, eh? + emit outgoingData( outgoing->toWire() ); + delete outgoing; + + return; +} + +int CoreProtocol::wireToTransfer( const QByteArray& wire ) +{ + // processing incoming data and reassembling it into transfers + // may be an event or a response + + BYTE flapStart, flapChannel = 0; + WORD flapLength = 0; + WORD s1, s2 = 0; + uint bytesParsed = 0; + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current packet" << toString(wire) << endl; + if ( wire.size() < 6 ) //check for valid flap length + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "packet not long enough! couldn't parse FLAP!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << wire.size() << endl; + m_state = NeedMore; + return bytesParsed; + } + + QDataStream din( wire, IO_ReadOnly ); + + // look at first four bytes and decide what to do with the chunk + if ( okToProceed( din ) ) + { + din >> flapStart; + QByteArray packet; + packet.duplicate( wire ); + if ( flapStart == 0x2A ) + { + din >> flapChannel; + din >> flapLength; //discard the first one it's not really the flap length + din >> flapLength; + if ( wire.size() < flapLength ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "Not enough bytes to make a correct transfer. Have " << wire.size() + << " bytes. need " << flapLength + 6 << " bytes" << endl; + m_state = NeedMore; + return bytesParsed; + } + + if ( flapChannel != 2 ) + { + Transfer *t = m_flapProtocol->parse( packet, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + m_state = Available; + emit incomingData(); + } + else + bytesParsed = 0; + } + + if ( flapChannel == 2 ) + { + din >> s1; + din >> s2; + + Transfer * t = m_snacProtocol->parse( packet, bytesParsed ); + if ( t ) + { + m_inTransfer = t; + m_state = Available; + emit incomingData(); + } + else + { + bytesParsed = 0; + m_state = NeedMore; + } + } + } + else + { //unknown wire format + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown wire format detected!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << flapStart << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet is " << endl << toString( wire ) << endl; + } + + } + return bytesParsed; +} + +void CoreProtocol::reset() +{ + m_in.resize( 0 ); +} + +void CoreProtocol::slotOutgoingData( const QCString &out ) +{ + kdDebug(OSCAR_RAW_DEBUG) << out.data() << endl; +} + +bool CoreProtocol::okToProceed( const QDataStream &din ) +{ + if ( din.atEnd() ) + { + m_state = NeedMore; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Server message ended prematurely!" << endl; + return false; + } + else + return true; +} + +#include "coreprotocol.moc" +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.h b/kopete/protocols/oscar/liboscar/coreprotocol.h new file mode 100644 index 00000000..f49396af --- /dev/null +++ b/kopete/protocols/oscar/liboscar/coreprotocol.h @@ -0,0 +1,108 @@ +/* + Kopete Groupwise Protocol + coreprotocol.h- the core GroupWise protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef GW_CORE_PROTOCOL_H +#define GW_CORE_PROTOCOL_H + +#include <qcstring.h> +#include <qobject.h> +#include <qptrlist.h> + +class FlapProtocol; +class SnacProtocol; +class Transfer; + +class CoreProtocol : public QObject +{ +Q_OBJECT +public: + enum State { NeedMore, Available, NoData, OutOfSync }; + + CoreProtocol(); + + virtual ~CoreProtocol(); + + /** + * Reset the protocol, clear buffers + */ + void reset(); + + /** + * Accept data from the network, and buffer it into a useful message + * This requires parsing out each FLAP, etc. from the incoming data + * @param incomingBytes Raw data in wire format. + */ + void addIncomingData( const QByteArray& incomingBytes ); + + /** + * @return the incoming transfer or 0 if none is available. + */ + Transfer* incomingTransfer(); + + /** + * Convert a request into an outgoing transfer + * emits @ref outgoingData() with each part of the transfer + */ + void outgoingTransfer( Transfer* outgoing ); + + /** + * Get the state of the protocol + */ + int state(); + +signals: + /** + * Emitted as the core protocol converts fields to wire ready data + */ + void outgoingData( const QByteArray& ); + + /** + * Emitted when there is incoming data, parsed into a Transfer + */ + void incomingData(); +protected slots: + /** + * Just a debug method to test emitting to the socket, atm - should go to the ClientStream + */ + void slotOutgoingData( const QCString & ); + +protected: + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed( const QDataStream &din ); + /** + * Convert incoming wire data into a Transfer object and queue it + * @return number of bytes from the input that were parsed into a Transfer + */ + int wireToTransfer( const QByteArray& wire ); + +private: + QByteArray m_in; // buffer containing unprocessed bytes we received + int m_error; + Transfer* m_inTransfer; // the transfer that is being received + int m_state; // represents the protocol's overall state + SnacProtocol* m_snacProtocol; + FlapProtocol* m_flapProtocol; + +}; + +#endif + diff --git a/kopete/protocols/oscar/liboscar/errortask.cpp b/kopete/protocols/oscar/liboscar/errortask.cpp new file mode 100644 index 00000000..9e9ce95b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/errortask.cpp @@ -0,0 +1,66 @@ +/* + Kopete Oscar Protocol + errortask.cpp - handle OSCAR protocol errors + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "errortask.h" +#include <kdebug.h> +#include "oscartypes.h" +#include "transfer.h" + +ErrorTask::ErrorTask( Task* parent ) + : Task( parent ) +{ +} + + +ErrorTask::~ErrorTask() +{ +} + + +bool ErrorTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->flapChannel() == 2 && st->snacSubtype() == 1 ) + return true; + else + return false; +} + +bool ErrorTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + Buffer* buffer = transfer->buffer(); + //get the error code + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Error code is " << buffer->getWord() << endl; + TLV t = buffer->getTLV(); + if ( t.type == 0x0008 && t.length > 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV error subcode is " + << t.data << endl; + } + return true; + } + else + return false; +} + +//kate indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/errortask.h b/kopete/protocols/oscar/liboscar/errortask.h new file mode 100644 index 00000000..f74152db --- /dev/null +++ b/kopete/protocols/oscar/liboscar/errortask.h @@ -0,0 +1,39 @@ +/* + Kopete Oscar Protocol + errortask.h - handle OSCAR protocol errors + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef ERRORTASK_H +#define ERRORTASK_H + +#include <task.h> + +/** +Handles OSCAR protocol errors received from the server on snac subtype 0x01 +@author Matt Rogers +*/ +class ErrorTask : public Task +{ +public: + ErrorTask( Task* parent ); + ~ErrorTask(); + bool take( Transfer* transfer ); + +protected: + bool forMe( const Transfer* transfer ) const; + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.cpp b/kopete/protocols/oscar/liboscar/flapprotocol.cpp new file mode 100644 index 00000000..c5dc04ed --- /dev/null +++ b/kopete/protocols/oscar/liboscar/flapprotocol.cpp @@ -0,0 +1,72 @@ +/* + Kopete Oscar Protocol + flapprotocol.cpp - reads the protocol used by Oscar for signaling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "flapprotocol.h" + +#include <qcstring.h> +#include <qdatastream.h> +#include <qobject.h> +#include <kdebug.h> + +#include "transfer.h" + +using namespace Oscar; + +FlapProtocol::FlapProtocol(QObject *parent, const char *name) + : InputProtocolBase(parent, name) +{ +} + +FlapProtocol::~FlapProtocol() +{ +} + +Transfer* FlapProtocol::parse( const QByteArray & packet, uint& bytes ) +{ + QDataStream* m_din = new QDataStream( packet, IO_ReadOnly ); + + BYTE b; + WORD w; + + FLAP f; + *m_din >> b; //this should be the start byte + *m_din >> b; + f.channel = b; + *m_din >> w; + f.sequence = w; + *m_din >> w; + f.length = w; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel: " << f.channel + << " sequence: " << f.sequence << " length: " << f.length << endl; + //use pointer arithmatic to skip the flap and snac headers + //so we don't have to do double parsing in the tasks + char* charPacket = packet.data(); + char* snacData = charPacket + 6; + Buffer *snacBuffer = new Buffer( snacData, f.length ); + + FlapTransfer* ft = new FlapTransfer( f, snacBuffer ); + bytes = snacBuffer->length() + 6; + delete m_din; + m_din = 0; + return ft; +} + + +#include "flapprotocol.moc" diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.h b/kopete/protocols/oscar/liboscar/flapprotocol.h new file mode 100644 index 00000000..d61cf6c4 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/flapprotocol.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + flapprotocol.h - reads the protocol used by Oscar for signaling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_FLAPPROTOCOL_H +#define OSCAR_FLAPPROTOCOL_H + +#include "inputprotocolbase.h" + +class FlapTransfer; + + +class FlapProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + FlapProtocol( QObject *parent = 0, const char *name = 0 ); + ~FlapProtocol(); + + /** + * Attempt to parse the supplied data into an @ref SnacTransfer object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an FlapTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.cpp b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp new file mode 100644 index 00000000..960d4ee5 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp @@ -0,0 +1,143 @@ +/* + Kopete Oscar Protocol + icbmparamstask.cpp - Get the ICBM parameters + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "icbmparamstask.h" + +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" +#include "oscarutils.h" +#include "buffer.h" + +ICBMParamsTask::ICBMParamsTask( Task* parent ) + : Task( parent ) +{} + + +ICBMParamsTask::~ICBMParamsTask() +{} + + +bool ICBMParamsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 4 && st->snacSubtype() == 5 ) + return true; + else + return false; +} + +bool ICBMParamsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleICBMParameters(); + setTransfer( 0 ); + return true; + } + return false; +} + +void ICBMParamsTask::onGo() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM Parameters request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0004, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* st = createTransfer( f, s, buffer ); + send( st ); +} + +void ICBMParamsTask::handleICBMParameters() +{ + Buffer* buffer = transfer()->buffer(); + + WORD channel = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel=" << channel << endl; + + /** + * bit1: messages allowed for specified channel + * bit2: missed calls notifications enabled for specified channel + * bit4: client supports typing notifications + */ + DWORD messageFlags = buffer->getDWord(); + WORD maxMessageSnacSize = buffer->getWord(); + WORD maxSendWarnLvl = buffer->getWord(); // max sender Warning Level + WORD maxRecvWarnLvl = buffer->getWord(); // max Receiver Warning Level + WORD minMsgInterval = buffer->getWord(); // minimum message interval (msec) + + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "messageFlags = " << messageFlags << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxMessageSnacSize = " << maxMessageSnacSize << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxSendWarnLvl = " << maxSendWarnLvl << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxRecvWarnLvl = " << maxRecvWarnLvl << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "minMsgInterval = " << minMsgInterval << endl; + + /*WORD unknown = */buffer->getWord(); + + // Now we set our own parameters. + // The ICBM parameters have to be set up seperately for each channel. + // Some clients (namely Trillian) might refuse sending on channels that were not set up. + sendMessageParams( 0x01 ); + sendMessageParams( 0x02 ); + sendMessageParams( 0x04 ); +} + +void ICBMParamsTask::sendMessageParams( int channel ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM parameters for channel " << channel << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + // the channel for which to set up the parameters + buffer->addWord( channel ); + + //these are all read-write + // channel-flags + // bit 1 : messages allowed (always 1 or you cannot send IMs) + // bit 2 : missed call notifications enabled + // bit 4 : typing notifications enabled + if ( channel == 1 ) + buffer->addDWord(0x0000000B); + else + buffer->addDWord(0x00000003); + + //max message length (8000 bytes) + buffer->addWord(0x1f40); + //max sender warning level (999) + buffer->addWord(0x03e7); + //max receiver warning level (999) + buffer->addWord(0x03e7); + //min message interval limit (0 msec) + buffer->addWord(0x0000); + // unknown parameter + buffer->addWord(0x0000); + + Transfer* st = createTransfer( f, s, buffer ); + send( st ); + setSuccess( 0, QString::null ); +} +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.h b/kopete/protocols/oscar/liboscar/icbmparamstask.h new file mode 100644 index 00000000..c7bdfb16 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icbmparamstask.h @@ -0,0 +1,56 @@ +/* + Kopete Oscar Protocol + icbmparamstask.h - Get the ICBM parameters + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef ICBMPARAMSTASK_H +#define ICBMPARAMSTASK_H + +#include <task.h> + +/** +Get the parameters we need to follow for instant messages + +@author Matt Rogers +*/ +class ICBMParamsTask : public Task +{ +public: + ICBMParamsTask( Task* parent ); + ~ICBMParamsTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + + /** + * Handle the ICBM Parameters we get back from SNAC 0x04, 0x05 + */ + void handleICBMParameters(); + + /** + * Send the message parameters we want back to the server. Only + * appears to occur during login + * @param channel the channel to set up + */ + void sendMessageParams( int channel ); + +protected: + void onGo(); + +}; + +#endif + +//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.cpp b/kopete/protocols/oscar/liboscar/icqlogintask.cpp new file mode 100644 index 00000000..c9f5cd52 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqlogintask.cpp @@ -0,0 +1,117 @@ +/* + Kopete Oscar Protocol + icqlogintask.cpp - Handles logging into to the ICQ service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "icqlogintask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "connection.h" +#include "oscarutils.h" + +using namespace Oscar; + +IcqLoginTask::IcqLoginTask( Task* parent ) + : Task( parent ) +{ +} + +IcqLoginTask::~IcqLoginTask() +{ +} + +bool IcqLoginTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +bool IcqLoginTask::forMe( Transfer* transfer ) const +{ + //there shouldn't be a incoming transfer for this task + Q_UNUSED( transfer ); + return false; +} + +void IcqLoginTask::onGo() +{ + FLAP f = { 0x01, 0, 0 }; + DWORD flapVersion = 0x00000001; + Buffer *outbuf = new Buffer(); + + QString encodedPassword = encodePassword( client()->password() ); + const Oscar::ClientVersion* version = client()->version(); + + outbuf->addDWord( flapVersion ); + outbuf->addTLV( 0x0001, client()->userId().length(), client()->userId().latin1() ); + outbuf->addTLV( 0x0002, encodedPassword.length(), encodedPassword.latin1() ); + outbuf->addTLV( 0x0003, version->clientString.length(), version->clientString.latin1() ); + outbuf->addTLV16( 0x0016, version->clientId ); + outbuf->addTLV16( 0x0017, version->major ); + outbuf->addTLV16( 0x0018, version->minor ); + outbuf->addTLV16( 0x0019, version->point ); + outbuf->addTLV16(0x001a, version->build ); + outbuf->addDWord( 0x00140004 ); //TLV type 0x0014, length 0x0004 + outbuf->addDWord( version->other ); //TLV data for type 0x0014 + outbuf->addTLV( 0x000f, version->lang.length(), version->lang.latin1() ); + outbuf->addTLV( 0x000e, version->country.length(), version->country.latin1() ); + + Transfer* ft = createTransfer( f, outbuf ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICQ channel 0x01 login packet" << endl; + send( ft ); + emit finished(); +} + + +QString IcqLoginTask::encodePassword( const QString& loginPassword ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Called." << endl; + + // TODO: check if latin1 is the right conversion + const char *password = loginPassword.latin1(); + unsigned int i = 0; + QString encodedPassword = QString::null; + + //encoding table used in ICQ password XOR transformation + unsigned char encoding_table[] = + { + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c + }; + + for (i = 0; i < 8; i++) + { + if(password[i] == 0) + break; //found a null in the password. don't encode it + encodedPassword.append( password[i] ^ encoding_table[i] ); + } + +#ifdef OSCAR_PWDEBUG + kdDebug(OSCAR_RAW_DEBUG) << " plaintext pw='" << loginPassword << "', length=" << + loginPassword.length() << endl; + + kdDebug(OSCAR_RAW_DEBUG) << " encoded pw='" << encodedPassword << "', length=" << + encodedPassword.length() << endl; +#endif + + return encodedPassword; +} + +#include "icqlogintask.moc" diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.h b/kopete/protocols/oscar/liboscar/icqlogintask.h new file mode 100644 index 00000000..45fb3eb6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqlogintask.h @@ -0,0 +1,47 @@ +/* + Kopete Oscar Protocol + icqlogintask.h - Handles logging into to the ICQ service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_ICQLOGINTASK_H_ +#define _OSCAR_ICQLOGINTASK_H_ + +#include <oscartypes.h> +#include <task.h> + +class QString; +class Transfer; + +using namespace Oscar; + +class IcqLoginTask : public Task +{ +Q_OBJECT +public: + IcqLoginTask( Task* parent ); + ~IcqLoginTask(); + bool take( Transfer* transfer ); + virtual void onGo(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + QString encodePassword( const QString& pw ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icqtask.cpp b/kopete/protocols/oscar/liboscar/icqtask.cpp new file mode 100644 index 00000000..a383922f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqtask.cpp @@ -0,0 +1,151 @@ +/* + Kopete Oscar Protocol + icqtask.cpp - SNAC 0x15 parsing + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "icqtask.h" +#include "buffer.h" +#include "connection.h" + +#include <qstring.h> + +#include <kdebug.h> + +ICQTask::ICQTask( Task * parent ) + : Task( parent ) +{ + m_icquin = client()->userId().toULong(); + m_sequence = 0; + m_requestType = 0xFFFF; + m_requestSubType = 0xFFFF; +} + +ICQTask::~ ICQTask() +{ +} + +void ICQTask::onGo() +{ +} + +bool ICQTask::forMe( const Transfer *t ) const +{ + Q_UNUSED( t ); + return false; +} + +bool ICQTask::take( Transfer* t ) +{ + Q_UNUSED( t ); + return false; +} + +void ICQTask::parseInitialData( Buffer buf ) +{ + int tlvLength = 0; + WORD sequence = 0; + TLV tlv1 = buf.getTLV(); + Buffer tlv1Buffer(tlv1.data, tlv1.length); + tlvLength = tlv1Buffer.getLEWord(); //FIXME handle the data chunk size + m_icquin = tlv1Buffer.getLEDWord(); //nice ICQ UIN + m_requestType = tlv1Buffer.getLEWord(); //request type + sequence = tlv1Buffer.getLEWord(); + if ( m_requestType == 0x07DA ) //there's an extra data subtype + m_requestSubType = tlv1Buffer.getLEWord(); + else + m_requestSubType = 0xFFFF; + +/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence + <<" request type: 0x" << QString::number( m_requestType, 16 ) + << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl;*/ +} + +Buffer* ICQTask::addInitialData( Buffer* buf ) const +{ + if ( m_requestType == 0xFFFF ) + { //something very wrong here + return 0; + } + + Buffer* tlvData = new Buffer(); + tlvData->addLEDWord( m_icquin ); // UIN + tlvData->addLEWord( m_requestType ); // request type + tlvData->addLEWord( m_sequence ); + + if ( m_requestSubType != 0xFFFF ) + tlvData->addLEWord( m_requestSubType ); + + /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence + <<" request type: 0x" << QString::number( m_requestType, 16 ) + << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl; */ + if ( buf != 0 ) + tlvData->addString( buf->buffer(), buf->length() ); + + Buffer* newBuffer = new Buffer(); + //add TLV 1 + newBuffer->addWord( 0x0001 ); //TLV 1 + newBuffer->addWord( tlvData->length() + 2 ); //TLV length + newBuffer->addLEWord( tlvData->length() ); // data chunk size + newBuffer->addString( tlvData->buffer(), tlvData->length() ); + + delete tlvData; + + return newBuffer; +} + +DWORD ICQTask::uin() const +{ + return m_icquin; +} + +void ICQTask::setUin( DWORD uin ) +{ + m_icquin = uin; +} + +WORD ICQTask::sequence() const +{ + return m_sequence; +} + +void ICQTask::setSequence( WORD sequence ) +{ + m_sequence = sequence; +} + +DWORD ICQTask::requestType() const +{ + return m_requestType; +} + +void ICQTask::setRequestType( WORD type ) +{ + m_requestType = type; +} + +DWORD ICQTask::requestSubType() const +{ + return m_requestSubType; +} + +void ICQTask::setRequestSubType( WORD subType ) +{ + m_requestSubType = subType; +} + +#include "icqtask.moc" + +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icqtask.h b/kopete/protocols/oscar/liboscar/icqtask.h new file mode 100644 index 00000000..7d134b49 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icqtask.h @@ -0,0 +1,63 @@ +/* + Kopete Oscar Protocol + icqtask.h - SNAC 0x15 parsing + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef ICQTASK_H +#define ICQTASK_H + +#include "task.h" + +using namespace Oscar; + +class Buffer; + +class ICQTask : public Task +{ +Q_OBJECT +public: + ICQTask( Task* parent ); + ~ICQTask(); + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + void parseInitialData( Buffer buf ); + Buffer* addInitialData( Buffer* buf = 0 ) const; + + DWORD uin() const; + void setUin( DWORD uin ); + + WORD sequence() const; + void setSequence( WORD seqeunce ); + + DWORD requestType() const; + void setRequestType( WORD type ); + + DWORD requestSubType() const; + void setRequestSubType( WORD subType ); + +private: + DWORD m_icquin; //little endian + WORD m_sequence; + WORD m_requestType; //little endian + WORD m_requestSubType; //little endian +}; + +//kate: tab-width 4; indent-mode csands; + +#endif diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.cpp b/kopete/protocols/oscar/liboscar/icquserinfo.cpp new file mode 100644 index 00000000..f853c045 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfo.cpp @@ -0,0 +1,262 @@ +/* + Kopete Oscar Protocol + icquserinfo.h - ICQ User Info Data Types + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "icquserinfo.h" +#include "buffer.h" + +#include <kdebug.h> + +ICQShortInfo::ICQShortInfo() +{ + uin = 0; + needsAuth = false; + gender = 0; +} + +void ICQShortInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ short user info packet" << endl; + nickname = buffer->getLELNTS(); + firstName = buffer->getLELNTS(); + lastName = buffer->getLELNTS(); + email = buffer->getLELNTS(); + needsAuth = buffer->getByte(); + buffer->skipBytes( 1 ); //skip the unknown byte + gender = buffer->getByte(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ short user info packet" << endl; +} + +ICQGeneralUserInfo::ICQGeneralUserInfo() +{ + uin = 0; + country = 0; + timezone = 0; + publishEmail = false; + webaware = false; + allowsDC = false; +} + +void ICQGeneralUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ basic user info packet" << endl; + nickname = buffer->getLELNTS(); + firstName = buffer->getLELNTS(); + lastName = buffer->getLELNTS(); + email = buffer->getLELNTS(); + city = buffer->getLELNTS(); + state = buffer->getLELNTS(); + phoneNumber = buffer->getLELNTS(); + faxNumber = buffer->getLELNTS(); + address = buffer->getLELNTS(); + cellNumber = buffer->getLELNTS(); + zip = buffer->getLELNTS(); + country = buffer->getLEWord(); + timezone = buffer->getLEByte(); // UTC+(tzcode * 30min) + webaware = ( buffer->getByte() == 0x01 ); + allowsDC = ( buffer->getByte() == 0x01 ); //taken from sim + publishEmail = ( buffer->getByte() == 0x01 ); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ basic user info packet" << endl; +} + +ICQWorkUserInfo::ICQWorkUserInfo() +{ + country = 0; + occupation = 0; +} + +void ICQWorkUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + city = buffer->getLELNTS(); + state = buffer->getLELNTS(); + phone = buffer->getLELNTS(); + fax = buffer->getLELNTS(); + address = buffer->getLELNTS(); + zip = buffer->getLELNTS(); + country = buffer->getLEWord(); + company = buffer->getLELNTS(); + department = buffer->getLELNTS(); + position = buffer->getLELNTS(); + occupation = buffer->getLEWord(); + homepage = buffer->getLELNTS(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl; +} + +ICQMoreUserInfo::ICQMoreUserInfo() +{ + age = 0; + gender = 0; + lang1 = 0; + lang2 = 0; + lang3 = 0; + ocountry = 0; + marital = 0; +} + +void ICQMoreUserInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + age = buffer->getLEWord(); + gender = buffer->getByte(); + homepage = buffer->getLELNTS(); + WORD year = buffer->getLEWord(); + BYTE month = buffer->getByte(); + BYTE day = buffer->getByte(); + + // set birthday to NULL if at least one of the values in the buffer is 0 + if ( year == 0 || month == 0 || day == 0 ) + birthday = QDate(); + else + birthday = QDate( year, month, day ); + + lang1 = buffer->getByte(); + lang2 = buffer->getByte(); + lang3 = buffer->getByte(); + buffer->getLEWord(); //emtpy field + ocity = buffer->getLELNTS(); + ostate = buffer->getLELNTS(); + ocountry = buffer->getLEWord(); + marital = buffer->getLEWord(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl; +} + +ICQEmailInfo::ICQEmailInfo() +{ +} + +void ICQEmailInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + int numEmails = buffer->getByte(); + QString email; + for ( int i = 0; i < numEmails; i++ ) + { + if ( buffer->getByte() == 0x00 ) + email = buffer->getLELNTS(); + } + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ email user info packet" << endl; +} + +ICQInterestInfo::ICQInterestInfo() +{ + count=0; +} + +void ICQInterestInfo::fill( Buffer* buffer ) +{ + if ( buffer->getByte() == 0x0A ) + { + count=0; //valid interests + int len= buffer->getByte(); //interests we get + for ( int i = 0; i < len; i++ ) + { + int t=buffer->getLEWord(); + QCString d = buffer->getLELNTS(); + if (t>0) { //there is some topic + if (count<4) { //i think this could not happen, i have never seen more + topics[count]=t; + descriptions[count]=d; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got topic: "<<topics[count]<<" desc: " << topics[count] << endl; + count++; + } else { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got more than four interest infos" << endl; + } + } + } + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "LEN: "<< len << " COUNT: " << count<< endl; + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ interest user info packet" << endl; +} + +ICQSearchResult::ICQSearchResult() +{ + auth = false; + online = false; + gender = 'U'; +} + +void ICQSearchResult::fill( Buffer* buffer ) +{ + WORD datalength = buffer->getLEWord(); // data length + WORD len = 0; + uin = buffer->getLEDWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found UIN " << QString::number( uin ) << endl; + len = buffer->getLEWord(); + if ( len > 0 ) + nickName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + firstName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + lastName = QCString( buffer->getBlock( len ) ); + + len = buffer->getLEWord(); + if ( len > 0 ) + email = QCString( buffer->getBlock( len ) ); + + auth = ( buffer->getByte() != 0x01 ); + online = ( buffer->getLEWord() == 0x0001 ); + switch ( buffer->getByte() ) + { + case 0x00: + gender = 'M'; + break; + case 0x01: + gender = 'F'; + break; + default: + gender = 'U'; + break; + } + age = buffer->getLEWord(); +} + +ICQWPSearchInfo::ICQWPSearchInfo() +{ + age = 0; + gender = 0; + language = 0; + country = 0; + occupation = 0; + onlineOnly = false; +} + + + +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.h b/kopete/protocols/oscar/liboscar/icquserinfo.h new file mode 100644 index 00000000..ac054721 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfo.h @@ -0,0 +1,213 @@ +/* + Kopete Oscar Protocol + icquserinfo.h - ICQ User Info Data Types + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _ICQUSERINFO_H_ +#define _ICQUSERINFO_H_ + +#include <qcstring.h> +#include <qvaluelist.h> +#include <qdatetime.h> +#include "kopete_export.h" + +class Buffer; + +/** + * @file icquserinfo.h + * Classes encapsulating user data retrieved from the server + */ + +class KOPETE_EXPORT ICQInfoBase +{ +public: + + ICQInfoBase() : m_sequence( 0 ) {} + virtual ~ICQInfoBase() {} + virtual void fill( Buffer* buffer ) = 0; + + void setSequenceNumber( int number ) { m_sequence = number; } + int sequenceNumber() { return m_sequence; } + +private: + int m_sequence; +}; + + +class KOPETE_EXPORT ICQShortInfo : public ICQInfoBase +{ +public: + ICQShortInfo(); + ~ICQShortInfo() {} + void fill( Buffer* buffer ); + +public: + unsigned long uin; + QCString nickname; + QCString firstName; + QCString lastName; + QCString email; + bool needsAuth; + unsigned int gender; // 0=offline, 1=online, 2=not webaware +}; + +class KOPETE_EXPORT ICQGeneralUserInfo : public ICQInfoBase +{ +public: + ICQGeneralUserInfo(); + ~ICQGeneralUserInfo() {} + void fill( Buffer* buffer ); + +public: + unsigned long uin; + QCString nickname; + QCString firstName; + QCString lastName; + QCString email; + QCString city; + QCString state; + QCString phoneNumber; + QCString faxNumber; + QCString address; + QCString cellNumber; + QCString zip; + int country; + char timezone; + bool publishEmail; + bool allowsDC; + bool webaware; +}; + +class KOPETE_EXPORT ICQWorkUserInfo : public ICQInfoBase +{ +public: + ICQWorkUserInfo(); + ~ICQWorkUserInfo() {} + void fill( Buffer* buffer ); + +public: + QCString city; + QCString state; + QCString phone; + QCString fax; + QCString address; + QCString zip; + int country; + QCString company; + QCString department; + QCString position; + int occupation; + QCString homepage; +}; + +class KOPETE_EXPORT ICQMoreUserInfo : public ICQInfoBase +{ +public: + ICQMoreUserInfo(); + ~ICQMoreUserInfo() {} + void fill( Buffer* buffer ); + +public: + int age; + unsigned int gender; + QCString homepage; + QDate birthday; + unsigned int lang1; + unsigned int lang2; + unsigned int lang3; + QCString ocity; + QCString ostate; + int ocountry; + int marital; +}; + +class KOPETE_EXPORT ICQEmailInfo : public ICQInfoBase +{ +public: + ICQEmailInfo(); + ~ICQEmailInfo() {} + void fill( Buffer* buffer ); + +public: + QValueList<QCString> emailList; +}; + +class KOPETE_EXPORT ICQInterestInfo : public ICQInfoBase +{ +public: + ICQInterestInfo(); + ~ICQInterestInfo() {} + void fill( Buffer* buffer ); + +public: + int count; + int topics[4]; + QCString descriptions[4]; +}; + + +class KOPETE_EXPORT ICQSearchResult +{ +public: + ICQSearchResult(); + void fill( Buffer* buffer ); + Q_UINT32 uin; + QCString firstName; + QCString lastName; + QCString nickName; + QCString email; + bool auth; + bool online; + char gender; + Q_UINT16 age; +}; + +class KOPETE_EXPORT ICQWPSearchInfo +{ +public: + ICQWPSearchInfo(); + + QCString firstName; + QCString lastName; + QCString nickName; + QCString email; + int age; + int gender; + int language; + QCString city; + QCString state; + int country; + QCString company; + QCString department; + QCString position; + int occupation; + bool onlineOnly; +}; + +/* +class ICQInfoItem +{ +public: + int category; + QCString description; +}; + + +typedef QValueList<ICQInfoItem> ICQInfoItemList; +*/ + +#endif +//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.cpp b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp new file mode 100644 index 00000000..068ac273 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp @@ -0,0 +1,234 @@ +/* + Kopete Oscar Protocol + icqtask.h - SNAC 0x15 parsing + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "icquserinfotask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "buffer.h" + + +ICQUserInfoRequestTask::ICQUserInfoRequestTask( Task* parent ) : ICQTask( parent ) +{ + //by default, request short info. it saves bandwidth + m_type = Short; +} + + +ICQUserInfoRequestTask::~ICQUserInfoRequestTask() +{ +} + + +bool ICQUserInfoRequestTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( *( st->buffer() ) ); + const_cast<ICQUserInfoRequestTask*>( this )->parseInitialData( buf ); + + if ( requestType() == 0x07DA ) + { + switch ( requestSubType() ) + { + case 0x00C8: + case 0x00D2: + case 0x00DC: + case 0x00E6: + case 0x00EB: + case 0x00F0: + case 0x00FA: + case 0x0104: + case 0x010E: + return true; + default: + return false; + } + } + + return false; +} + +bool ICQUserInfoRequestTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + ICQGeneralUserInfo genInfo; + ICQWorkUserInfo workInfo; + ICQMoreUserInfo moreInfo; + ICQEmailInfo emailInfo; + ICQShortInfo shortInfo; + ICQInterestInfo interestInfo; + + setTransfer( transfer ); + TLV tlv1 = transfer->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + //FIXME this is silly. parseInitialData should take care of this for me. + buffer->skipBytes( 8 ); + WORD seq = buffer->getLEWord(); // request sequence number + buffer->getLEWord(); // request data sub type + QString contactId = m_contactSequenceMap[seq]; + + switch ( requestSubType() ) + { + case 0x00C8: //basic user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received basic info" << endl; + genInfo.setSequenceNumber( seq ); + genInfo.fill( buffer ); + m_genInfoMap[seq] = genInfo; + break; + case 0x00D2: //work user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received work info" << endl; + workInfo.setSequenceNumber( seq ); + workInfo.fill( buffer ); + m_workInfoMap[seq] = workInfo; + break; + case 0x00DC: //more user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received more info" << endl; + moreInfo.setSequenceNumber( seq ); + moreInfo.fill( buffer ); + m_moreInfoMap[seq] = moreInfo; + break; + case 0x00E6: //notes user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got Notes info, but we don't support it yet" << endl; + break; + case 0x00EB: //email user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received email info" << endl; + emailInfo.setSequenceNumber( seq ); + emailInfo.fill( buffer ); + m_emailInfoMap[seq] = emailInfo; + break; + case 0x00F0: //interests user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received interest info" << endl; + interestInfo.setSequenceNumber( seq ); + interestInfo.fill( buffer ); + m_interestInfoMap[seq] = interestInfo; + break; + case 0x00FA: //affliations user info + //affliations seems to be the last info we get, so be hacky and only emit the signal once + emit receivedInfoFor( contactId, Long ); + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got affliations info, but we don't support it yet" << endl; + break; + case 0x0104: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received short user info" << endl; + shortInfo.setSequenceNumber( seq ); + shortInfo.fill( buffer ); + m_shortInfoMap[seq] = shortInfo; + break; + case 0x010E: //homepage category user info + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got homepage category info, but we don't support it yet" << endl; + break; + default: + break; + } + + + if ( m_type == Short ) + emit receivedInfoFor( contactId, Short ); + + setTransfer( 0 ); + return true; + } + return false; +} + +void ICQUserInfoRequestTask::onGo() +{ + if ( m_userToRequestFor.isNull() ) + return; + + Buffer* sendBuf = 0L; + Buffer b; + if ( m_type != Short ) + { + setRequestSubType( 0x04D0 ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting full user info for " << m_userToRequestFor << endl; + } + else + { + setRequestSubType( 0x04BA ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting short user info for " << m_userToRequestFor << endl; + } + + setSequence( client()->snacSequence() ); + setRequestType( 0x07D0 ); + b.addLEDWord( m_userToRequestFor.toULong() ); + sendBuf = addInitialData( &b ); + + m_contactSequenceMap[sequence()] = m_userToRequestFor; + m_reverseContactMap[m_userToRequestFor] = sequence(); + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0, client()->snacSequence() }; + Transfer* t = createTransfer( f, s, sendBuf ); + send( t ); +} + +ICQGeneralUserInfo ICQUserInfoRequestTask::generalInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_genInfoMap[seq]; +} + +ICQWorkUserInfo ICQUserInfoRequestTask::workInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_workInfoMap[seq]; +} + +ICQMoreUserInfo ICQUserInfoRequestTask::moreInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_moreInfoMap[seq]; +} + +ICQEmailInfo ICQUserInfoRequestTask::emailInfoFor(const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_emailInfoMap[seq]; +} + +ICQShortInfo ICQUserInfoRequestTask::shortInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_shortInfoMap[seq]; +} + +ICQInterestInfo ICQUserInfoRequestTask::interestInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_interestInfoMap[seq]; +} + +QString ICQUserInfoRequestTask::notesInfoFor( const QString& contact ) +{ + int seq = m_reverseContactMap[contact]; + return m_notesInfoMap[seq]; +} + + +#include "icquserinfotask.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.h b/kopete/protocols/oscar/liboscar/icquserinfotask.h new file mode 100644 index 00000000..eba81b57 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/icquserinfotask.h @@ -0,0 +1,77 @@ +/* + Kopete Oscar Protocol + icquserinfotask.h - SNAC 0x15 parsing for user info + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef ICQUSERINFOTASK_H +#define ICQUSERINFOTASK_H + +#include <qmap.h> +#include <qstring.h> + +#include "icqtask.h" +#include "icquserinfo.h" + +class Transfer; + +/** +@author Kopete Developers +*/ +class ICQUserInfoRequestTask : public ICQTask +{ +Q_OBJECT +public: + ICQUserInfoRequestTask( Task* parent ); + ~ICQUserInfoRequestTask(); + + enum { Long = 0, Short }; + + void setUser( const QString& user ) { m_userToRequestFor = user; } + void setType( unsigned int type ) { m_type = type; } + void setInfoToRequest( unsigned int type ); + + ICQGeneralUserInfo generalInfoFor( const QString& contact ); + ICQEmailInfo emailInfoFor( const QString& contact ); + ICQMoreUserInfo moreInfoFor( const QString& contact ); + ICQWorkUserInfo workInfoFor( const QString& contact ); + QString notesInfoFor( const QString& contact ); + ICQShortInfo shortInfoFor( const QString& contact ); + ICQInterestInfo interestInfoFor( const QString& contact ); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +signals: + void receivedInfoFor( const QString& contact, unsigned int type ); + +private: + QMap<int, ICQGeneralUserInfo> m_genInfoMap; + QMap<int, ICQEmailInfo> m_emailInfoMap; + QMap<int, ICQMoreUserInfo> m_moreInfoMap; + QMap<int, ICQWorkUserInfo> m_workInfoMap; + QMap<int, ICQShortInfo> m_shortInfoMap; + QMap<int, ICQInterestInfo> m_interestInfoMap; + QMap<int, QString> m_notesInfoMap; + QMap<int, QString> m_contactSequenceMap; + QMap<QString, int> m_reverseContactMap; + unsigned int m_type; + QString m_userToRequestFor; + +}; +#endif + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp new file mode 100644 index 00000000..abd34e53 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp @@ -0,0 +1,100 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "inputprotocolbase.h" + +InputProtocolBase::InputProtocolBase(QObject *parent, const char *name) + : QObject(parent, name) +{ + m_state = NeedMore; + m_bytes = 0; +} + + +InputProtocolBase::~InputProtocolBase() +{ +} + +uint InputProtocolBase::state() const +{ + return m_state; +} + +bool InputProtocolBase::readString( QString &message ) +{ + uint len; + QCString rawData; + if ( !safeReadBytes( rawData, len ) ) + return false; + message = QString::fromUtf8( rawData.data(), len - 1 ); + return true; +} + + +bool InputProtocolBase::okToProceed() +{ + if ( m_din ) + { + if ( m_din->atEnd() ) + { + m_state = NeedMore; + qDebug( "InputProtocol::okToProceed() - Server message ended prematurely!" ); + } + else + return true; + } + return false; +} + +bool InputProtocolBase::safeReadBytes( QCString & data, uint & len ) +{ + // read the length of the bytes + Q_UINT32 val; + if ( !okToProceed() ) + return false; + *m_din >> val; + m_bytes += sizeof( Q_UINT32 ); + if ( val > 1024 ) + return false; + //qDebug( "EventProtocol::safeReadBytes() - expecting %i bytes", val ); + QCString temp( val ); + if ( val != 0 ) + { + if ( !okToProceed() ) + return false; + // if the server splits packets here we are in trouble, + // as there is no way to see how much data was actually read + m_din->readRawBytes( temp.data(), val ); + // the rest of the string will be filled with FF, + // so look for that in the last position instead of \0 + // this caused a crash - guessing that temp.length() is set to the number of bytes actually read... + // if ( (Q_UINT8)( * ( temp.data() + ( temp.length() - 1 ) ) ) == 0xFF ) + if ( temp.length() < ( val - 1 ) ) + { + qDebug( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %i bytes out of %i", temp.length(), val ); + m_state = NeedMore; + return false; + } + } + data = temp; + len = val; + m_bytes += val; + return true; +} + +#include "inputprotocolbase.moc" diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.h b/kopete/protocols/oscar/liboscar/inputprotocolbase.h new file mode 100644 index 00000000..7bea895f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.h @@ -0,0 +1,72 @@ +/* + Kopete Groupwise Protocol + inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef INPUTPROTOCOLBASE_H +#define INPUTPROTOCOLBASE_H + +#include <qobject.h> + +class Transfer; +/** +Defines a basic interface for protocols dealing with input from the GroupWise server. + +@author Matt Rogers +*/ +class InputProtocolBase : public QObject +{ +Q_OBJECT +public: + enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError }; + InputProtocolBase(QObject *parent = 0, const char *name = 0); + ~InputProtocolBase(); + /** + * Returns a value describing the state of the object. + * If the object is given data to parse that does not begin with a recognised event code, + * it will become OutOfSync, to indicate that the input data probably contains leftover data not processed during a previous parse. + */ + uint state() const; + /** + * Attempt to parse the supplied data into a Transfer object + * @param bytes this will be set to the number of bytes that were successfully parsed. It is no indication of the success of the whole procedure + * @return On success, a Transfer object that the caller is responsible for deleting. It will be either an EventTransfer or a Response, delete as appropriate. On failure, returns 0. + */ + virtual Transfer * parse( const QByteArray &, uint & bytes ) = 0 ; +protected: + /** + * Reads an arbitrary string + * updates the bytes parsed counter + */ + bool readString( QString &message ); + /** + * Check that there is data to read, and set the protocol's state if there isn't any. + */ + bool okToProceed(); + /** + * read a Q_UINT32 giving the number of following bytes, then a string of that length + * updates the bytes parsed counter + * @return false if the string was broken or there was no data available at all + */ + bool safeReadBytes( QCString & data, uint & len ); + +protected: + uint m_state; + uint m_bytes; + QDataStream * m_din; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/locationrightstask.cpp b/kopete/protocols/oscar/liboscar/locationrightstask.cpp new file mode 100644 index 00000000..5aae9a5e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/locationrightstask.cpp @@ -0,0 +1,87 @@ +/* + Kopete Oscar Protocol + locationrightstask.cpp - Set up the service limitations + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "locationrightstask.h" +#include <kdebug.h> +#include "buffer.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "connection.h" + +using namespace Oscar; + +LocationRightsTask::LocationRightsTask( Task* parent ) + : Task( parent ) +{ +} + + +LocationRightsTask::~LocationRightsTask() +{ +} + + +bool LocationRightsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 2 && st->snacSubtype() == 3 ) + return true; + else + return false; +} + +bool LocationRightsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleLocationRightsResponse(); + setTransfer( 0 ); + return true; + } + else + return false; +} + +void LocationRightsTask::onGo() +{ + sendLocationRightsRequest(); +} + +void LocationRightsTask::sendLocationRightsRequest() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + Transfer* st = createTransfer( f, s, b ); + send( st ); +} + +void LocationRightsTask::handleLocationRightsResponse() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring location rights response" << endl; + setSuccess( 0, QString::null ); +} + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/locationrightstask.h b/kopete/protocols/oscar/liboscar/locationrightstask.h new file mode 100644 index 00000000..a3e82767 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/locationrightstask.h @@ -0,0 +1,57 @@ +/* + Kopete Oscar Protocol + locationrightstask.h - Set up the service limitations + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef LOCATIONRIGHTSTASK_H +#define LOCATIONRIGHTSTASK_H + +#include <task.h> + +class Transfer; + +using namespace Oscar; + +/** +This task handles location rights. +This task implements the following SNACS: + \li 0x02, 0x02 + \li 0x02, 0x03 + +@author Kopete Developers +*/ +class LocationRightsTask : public Task +{ +public: + LocationRightsTask( Task* parent ); + ~LocationRightsTask(); + bool take( Transfer* transfer ); + +protected: + bool forMe( const Transfer* transfer ) const; + void onGo(); + +private: + //! Send the location rights request ( SNAC 0x02, 0x02 ) + void sendLocationRightsRequest(); + + //! Handle the location rights reply ( SNAC 0x02, 0x03 ) + void handleLocationRightsResponse(); +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/logintask.cpp b/kopete/protocols/oscar/liboscar/logintask.cpp new file mode 100644 index 00000000..962b2e1a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/logintask.cpp @@ -0,0 +1,218 @@ +/* + Kopete Oscar Protocol + logintask.cpp - Handles logging into to the AIM or ICQ service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "logintask.h" + +#include <qtimer.h> +#include <kdebug.h> +#include <klocale.h> + +#include "aimlogintask.h" +#include "connection.h" +#include "closeconnectiontask.h" +#include "icqlogintask.h" +#include "oscarutils.h" +#include "rateinfotask.h" +#include "serverversionstask.h" +#include "transfer.h" + + + +/** + * Stage One Task Implementation + */ + +StageOneLoginTask::StageOneLoginTask( Task* parent ) + : Task ( parent ) +{ + m_aimTask = 0L; + m_icqTask = 0L; + m_closeTask = 0L; +} + +StageOneLoginTask::~StageOneLoginTask() +{ + delete m_aimTask; + delete m_icqTask; + delete m_closeTask; +} + +bool StageOneLoginTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + if ( client()->isIcq() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting ICQ login" << endl; + m_icqTask = new IcqLoginTask( client()->rootTask() ); + m_closeTask = new CloseConnectionTask( client()->rootTask() ); + + //connect finished signal + connect( m_closeTask, SIGNAL( finished() ), this, SLOT( closeTaskFinished() ) ); + m_icqTask->go( true ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting AIM login" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the FLAP version back" << endl; + + //send the flap version response + FLAP f = { 0x01, 0 , 0 }; + Buffer *outbuf = new Buffer; + outbuf->addDWord(0x00000001); //flap version + f.length = outbuf->length(); + Transfer* ft = createTransfer( f, outbuf ); + send( ft ); + + m_aimTask = new AimLoginTask( client()->rootTask() ); + connect( m_aimTask, SIGNAL( finished() ), this, SLOT( aimTaskFinished() ) ); + m_aimTask->go( true ); + } + return true; + } + return false; +} + +void StageOneLoginTask::closeTaskFinished() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + m_cookie = m_closeTask->cookie(); + m_bosPort = m_closeTask->bosPort(); + m_bosServer = m_closeTask->bosHost(); + m_closeTask->safeDelete(); + setSuccess( m_closeTask->statusCode(), m_closeTask->statusString() ); +} + +void StageOneLoginTask::aimTaskFinished() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + m_cookie = m_aimTask->cookie(); + m_bosPort = m_aimTask->bosPort(); + m_bosServer = m_aimTask->bosHost(); + + setSuccess( m_aimTask->statusCode(), m_aimTask->statusString() ); +} + +bool StageOneLoginTask::forMe( Transfer* transfer ) const +{ + FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer ); + + if (!ft) + return false; + + return ( ft && ft->flapChannel() == 1 ); +} + +const QByteArray& StageOneLoginTask::loginCookie() const +{ + return m_cookie; +} + +const QString& StageOneLoginTask::bosServer() const +{ + return m_bosServer; +} + +const QString& StageOneLoginTask::bosPort() const +{ + return m_bosPort; +} + + +/** + * Stage Two Task Implementation + */ +StageTwoLoginTask::StageTwoLoginTask( Task* parent ) + : Task( parent ) +{ + //Create our tasks + Task* rootTask = client()->rootTask(); + m_versionTask = new ServerVersionsTask( rootTask ); + m_rateTask = new RateInfoTask( rootTask ); + + QObject::connect( m_versionTask, SIGNAL( finished() ), this, SLOT( versionTaskFinished() ) ); + QObject::connect( m_rateTask, SIGNAL( finished() ), this, SLOT( rateTaskFinished() ) ); +} + +StageTwoLoginTask::~StageTwoLoginTask() +{ + delete m_versionTask; +} + +bool StageTwoLoginTask::take( Transfer* transfer ) +{ + bool yes = forMe( transfer ); + return yes; +} + +bool StageTwoLoginTask::forMe( Transfer* transfer ) const +{ + FlapTransfer* ft = dynamic_cast<FlapTransfer*>( transfer ); + + if (!ft) + return false; + + int channel = ft->flapChannel(); + if ( channel == 1 ) + return true; + else + return false; +} + +void StageTwoLoginTask::onGo() +{ + if ( !m_cookie.isEmpty() ) + { + //send the flap back + FLAP f = { 0x01, 0, 0 }; + Buffer* outbuf = new Buffer(); + outbuf->addDWord( 0x00000001 ); + outbuf->addTLV( 0x06, m_cookie.size(), m_cookie.data() ); + Transfer* ft = createTransfer( f, outbuf ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the login cookie back" << endl; + send( ft ); + } + else + setError( -1, QString::null ); + return; +} + +void StageTwoLoginTask::setCookie( const QByteArray& newCookie ) +{ + m_cookie.duplicate( newCookie ); +} + +const QByteArray& StageTwoLoginTask::cookie() +{ + return m_cookie; +} + +void StageTwoLoginTask::versionTaskFinished() +{ + //start the rate info task + m_rateTask->go(true); +} + +void StageTwoLoginTask::rateTaskFinished() +{ + setSuccess( 0, QString::null ); +} + +#include "logintask.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/logintask.h b/kopete/protocols/oscar/liboscar/logintask.h new file mode 100644 index 00000000..12061821 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/logintask.h @@ -0,0 +1,144 @@ +/* + Kopete Oscar Protocol + logintask.h - Handles logging into to the AIM or ICQ service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCAR_LOGINTASK_H_ +#define _OSCAR_LOGINTASK_H_ + +#include <qcstring.h> +#include "oscartypes.h" +#include "task.h" + +#include "aimlogintask.h" +#include "icqlogintask.h" +#include "closeconnectiontask.h" + +using namespace Oscar; + +class QString; +class Transfer; + + +/** + * \short Handle OSCAR login - stage 1 + * + * OSCAR login is divided into two stages. The first stage handles the connection negotiation + * so that we can get a BOS server to connect to and start stage two of the login process + * This class handles the first stage of the OSCAR login process + * For more info about the OSCAR login process, visit http://iserverd1.khstu.ru/oscar + */ +class StageOneLoginTask : public Task +{ +Q_OBJECT +public: + StageOneLoginTask( Task* parent ); + ~StageOneLoginTask(); + bool take( Transfer* transfer ); + + //Protocol specific stuff + + //! Get the BOS cookie + const QByteArray& loginCookie() const; + + //! Get the BOS server + const QString& bosServer() const; + + //! Get the BOS port + const QString& bosPort() const; + + //! Get the error code, if there is one + int errorCode() const; + + //! Get the error reason so it can be displayed + const QString& errorReason() const; + + +public slots: + void closeTaskFinished(); + void aimTaskFinished(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + + //Tasks we want to control + AimLoginTask* m_aimTask; + IcqLoginTask* m_icqTask; + CloseConnectionTask* m_closeTask; + + //Private data we get from the tasks + QByteArray m_cookie; + QString m_bosServer; + QString m_bosPort; + +}; + +/** + * \short Handle OSCAR Login - stage 2 + * + * Oscar login is divided into two stages. The first stage handles the connection negotiation + * so that we can get a BOS server to connect to for the second stage. This class handles the + * second stage of Oscar login that establishes various things like rate limits, contact lists, + * and SNAC family versions + */ + +class ServerVersionsTask; +class RateInfoTask; + +class StageTwoLoginTask : public Task +{ +Q_OBJECT +public: + StageTwoLoginTask( Task* parent ); + ~StageTwoLoginTask(); + bool take( Transfer* transfer ); + void onGo(); + + //protocol specifics + //! Set the cookie to send to the server + void setCookie( const QByteArray& newCookie ); + + //! Get the cookie to send to the server + const QByteArray& cookie(); + + QString host() const; + QString port() const; + +public slots: + + //! Start the rate info task + void versionTaskFinished(); + + //! The rate info task is finished. Start the other ones + void rateTaskFinished(); + +protected: + bool forMe( Transfer* transfer ) const; + +private: + QByteArray m_cookie; + QString m_host, m_port; + + //tasks + ServerVersionsTask* m_versionTask; + RateInfoTask* m_rateTask; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/md5.c b/kopete/protocols/oscar/liboscar/md5.c new file mode 100644 index 00000000..e6273585 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/md5.c @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include <string.h> +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include <math.h> +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/kopete/protocols/oscar/liboscar/md5.h b/kopete/protocols/oscar/liboscar/md5.h new file mode 100644 index 00000000..501cdbe1 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/md5.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +#ifdef P1 +void md5_init(P1(md5_state_t *pms)); +#else +void md5_init(md5_state_t *pms); +#endif + +/* Append a string to the message. */ +#ifdef P3 +void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes)); +#else +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); +#endif + +/* Finish the message and return the digest. */ +#ifdef P2 +void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16])); +#else +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.cpp b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp new file mode 100644 index 00000000..2db05eb1 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp @@ -0,0 +1,461 @@ +/* + messagereceivertask.cpp - Incoming OSCAR Messaging Handler + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "messagereceivertask.h" + +#include <qtextcodec.h> +#include <kdebug.h> +#include "transfer.h" +#include "buffer.h" +#include "connection.h" +#include "oscarutils.h" +#include "userdetails.h" + + +MessageReceiverTask::MessageReceiverTask( Task* parent ) : Task( parent ) +{ +} + + +MessageReceiverTask::~MessageReceiverTask() +{ +} + + +bool MessageReceiverTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0004 ) + { + WORD subtype = st->snacSubtype(); + switch ( subtype ) + { + case 0x0007: + case 0x000B: + return true; + break; + default: + return false; + break; + } + } + else + return false; +} + +bool MessageReceiverTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + m_currentSnacSubtype = st->snacSubtype(); + + Buffer* b = transfer->buffer(); + m_icbmCookie = b->getBlock( 8 ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icbm cookie is " << m_icbmCookie << endl; + m_channel = b->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel is " << m_channel << endl; + + if ( m_currentSnacSubtype == 0x0007 ) + { + UserDetails ud; + ud.fill( b ); + m_fromUser = ud.userId(); + + switch( m_channel ) + { + case 0x0001: + setTransfer( transfer ); + handleType1Message(); + setTransfer( 0 ); + return true; + break; + case 0x0002: + setTransfer( transfer ); + handleType2Message(); + setTransfer( 0 ); + return true; + break; + case 0x0004: + setTransfer( transfer ); + handleType4Message(); + setTransfer( 0 ); + return true; + break; + default: + kdWarning(OSCAR_RAW_DEBUG) << "A message was received on an unknown channel. Channel is " << m_channel << endl; + return false; + break; + } + } + else + { + int screenNameLength = b->getByte(); + m_fromUser = QString( b->getBlock( screenNameLength ) ); + setTransfer( transfer ); + handleAutoResponse(); + setTransfer( 0 ); + return true; + } + } + return false; +} + +void MessageReceiverTask::handleType1Message() +{ + Oscar::Message msg; + QValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList(); + TLV t = Oscar::findTLV( messageTLVList, 0x0002 ); + if ( !t ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a message packet with no message!" << endl; + return; + } + Buffer messageBuffer( t.data ); + QValueList<TLV> innerTLVList = messageBuffer.getTLVList(); + QValueList<TLV>::iterator it = innerTLVList.begin(), listEnd = innerTLVList.end(); + for ( ; (*it); ++it ) + { + switch ( ( *it ).type ) + { + case 0x0501: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got features tlv. length: " + << ( *it ).length << " data: " << ( *it ).data << endl; + break; + case 0x0101: + { + Buffer message( ( *it ).data ); + m_charSet = message.getWord(); + m_subCharSet = message.getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message charset: " << m_charSet + << " message subcharset: " << m_subCharSet << endl; + if ( m_charSet == 0x0002 ) + msg.setEncoding( Oscar::Message::UCS2 ); + else + msg.setEncoding( Oscar::Message::UserDefined ); + + //message length is buffer length - length of ( charset + subcharset ) */ + int msgLength = ( *it ).length - 4; + QByteArray msgArray( message.getBlock( msgLength ) ); + msg.setTextArray( msgArray ); + + break; + } //end case + default: + kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << ( *it ).type << endl; + break; + } //end switch + } + + TLV autoResponse = Oscar::findTLV( messageTLVList, 0x0004 ); + if ( autoResponse ) + { + kdDebug(OSCAR_RAW_DEBUG) << "auto response message" << endl; + msg.addProperty( Oscar::Message::AutoResponse ); + } + else + msg.addProperty( Oscar::Message::Normal ); + + msg.setSender( m_fromUser ); + msg.setReceiver( client()->userId() ); + msg.setTimestamp( QDateTime::currentDateTime() ); + msg.setType( 0x01 ); + + emit receivedMessage( msg ); +} + +void MessageReceiverTask::handleType2Message() +{ + kdDebug(14151) << k_funcinfo << "Received Type 2 message. Trying to handle it..." << endl; + + Oscar::Message msg; + QValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList(); + TLV t = Oscar::findTLV( messageTLVList, 0x0005 ); + if ( !t ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a channel 2 message packet with no message!" << endl; + return; + } + Buffer messageBuffer( t.data ); + kdDebug(14151) << k_funcinfo << "Buffer length is " << messageBuffer.length() << endl; + + // request type + int requestType = messageBuffer.getWord(); + kdDebug(14151) << k_funcinfo << "Request type (0 - request, 1 - cancel, 2 - accept): " << requestType << endl; + + // skip the message id cookie, already handled above + messageBuffer.skipBytes( 8 ); + + // next is capability identifier (GUID). skip for now + messageBuffer.skipBytes( 16 ); + + while( messageBuffer.length() > 0 ) + { + TLV tlv = messageBuffer.getTLV(); + switch ( tlv.type ) + { + case 0x0004: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got external ip: " + << tlv.length << " data: " << tlv.data << endl; + break; + case 0x0005: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got listening port: " + << tlv.length << " data: " << tlv.data << endl; + break; + case 0x000A: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got Acktype: " // 0x0001 normal message, 2 Abort Request, 3 Acknowledge request + << tlv.length << " data: " << tlv.data << endl; + break; + case 0x000B: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown TLV 0x000B: " + << tlv.length << " data: " << tlv.data << endl; + break; + case 0x000F: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown empty TLV 0x000F" << endl; + break; + case 0x2711: + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got a TLV 2711" << endl; + Buffer tlv2711Buffer( tlv.data ); + parseRendezvousData( &tlv2711Buffer, &msg ); + if ( msg.messageType() == 0x1A ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received plugin message" << endl; + break; + } + + switch ( requestType ) + { + case 0x00: // some request + emit receivedMessage( msg ); + break; + case 0x01: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received Abort Mesage" << endl; + break; + case 0x02: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received OK Message" << endl; + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received unknown request type: " << requestType << endl; + break; + } + + break; + } //end case + default: + kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << tlv.type << endl; + break; + } //end switch + }//end while +} + +void MessageReceiverTask::handleType4Message() +{ + TLV tlv5 = transfer()->buffer()->getTLV(); + kdDebug(14151) << k_funcinfo << "The first TLV is of type " << tlv5.type << endl; + if (tlv5.type != 0x0005) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Aborting because first TLV != TLV(5)" << endl; + return; + } + + Buffer tlv5buffer(tlv5.data, tlv5.length); + + DWORD uin = tlv5buffer.getLEDWord(); // little endian for no sane reason! + if ( QString::number(uin) != m_fromUser ) + kdWarning(14151) << k_funcinfo << "message uin does not match uin found in packet header!" << endl; + + BYTE msgType = tlv5buffer.getByte(); + BYTE msgFlags = tlv5buffer.getByte(); + + kdDebug(14151) << k_funcinfo << "Received server message. type = " << msgType + << ", flags = " << msgFlags << endl; + + //handle the special user types + Oscar::Message msg; + QString msgSender; + switch ( msgType ) + { + case 0x0D: + msgSender = "ICQ Web Express"; + msg.addProperty( Oscar::Message::WWP ); + break; + case 0x0E: + msgSender = "ICQ Email Express"; + msg.addProperty( Oscar::Message::EMail ); + break; + default: + msgSender = m_fromUser; + break; + }; + + QCString msgText = tlv5buffer.getLNTS(); + int msgLength = msgText.size(); + if ( msgType == 0x0D || msgType == 0x0E ) + { + for ( int i = 0; i < msgLength; i++ ) + { + if ( msgText[i] == (char)0xFE ) + msgText[i] = 0x20; + } + } + + switch ( msgFlags ) + { + case 0x03: + msg.addProperty( Oscar::Message::AutoResponse ); + break; + case 0x01: + msg.addProperty( Oscar::Message::Normal ); + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not handling message flag " << msgFlags << endl; + break; + } + + msg.setType( 0x04 ); + msg.setTimestamp( QDateTime::currentDateTime() ); + msg.setSender( msgSender ); + msg.setReceiver( client()->userId() ); + msg.setEncoding( Oscar::Message::UserDefined ); + msg.setTextArray( msgText ); + emit receivedMessage( msg ); +} + +void MessageReceiverTask::handleAutoResponse() +{ + kdDebug(14151) << k_funcinfo << "Received auto response. Trying to handle it..." << endl; + + Oscar::Message msg; + msg.addProperty( Oscar::Message::AutoResponse ); + Buffer* b = transfer()->buffer(); + + // reason code + int reasonCode = b->getWord(); + kdDebug(14151) << k_funcinfo << "Reason code (1 - channel not supported, 2 - busted payload, 3 - channel specific data): " << reasonCode << endl; + + parseRendezvousData( b, &msg ); + emit receivedMessage( msg ); +} + +void MessageReceiverTask::parseRendezvousData( Buffer* b, Oscar::Message* msg ) +{ + int length1 = b->getLEWord(); + if ( length1 != 0x001B ) + { // all real messages (actually their header) seem to have length 0x1B + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Weired Message length. Bailing out!" << endl; + return; + } + + int protocolVersion = b->getLEWord(); // the extended data protocol version, there are quite a few... + + // plugin (for file transfer & stuff, all zeros for regular message + b->skipBytes( 16 ); + // unknown + b->skipBytes( 2 ); + // client capablities + b->skipBytes( 4 ); + // unknown + b->skipBytes( 1 ); + + // (down)counter: basically just some number, ICQ counts down, miranda up, doesnt matter. + // BUT: when sending auto response on channel 2, like with the icbm cookie, we need to send the same value! + int channel2Counter = b->getLEWord(); + + // the next one is length (of a counter + following all-zero field), but also seems to indicate what type of message this is + int length2 = b->getLEWord(); + + // the only length usable ATM is 0x000E, which is a message + switch( length2 ) + { + case 0x000E: + { + int cookie = b->getLEWord(); + for ( int i = 0; i < 12; i++ ) + { // 12 bytes all zeros + b->getByte(); + } + + // now starts the real message + // TODO if type is PLAIN, there is (might be?) an additional TLV with color and font information at the end... + + uint messageType = b->getByte(); + int flags = b->getByte(); + int status = b->getLEWord(); // don't know what status this is or what to use it for + int priority = b->getLEWord(); // don't know what that's good for either + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message type is: " << messageType << endl; + + QCString msgText( b->getLELNTS() ); + Oscar::Message::Encoding encoding = Oscar::Message::UserDefined; + int fgcolor = 0x00000000; + int bgcolor = 0x00ffffff; + + // Don't parse plugin message + if ( b->length() >= 8 && messageType != 0x1A ) + { + fgcolor = b->getLEDWord(); + bgcolor = b->getLEDWord(); + + while ( b->length() >= 4 ) + { + int capLength = b->getLEDWord(); + if ( b->length() < capLength ) + break; + + QByteArray cap( b->getBlock( capLength ) ); + if ( qstrncmp ( cap.data(), "{0946134E-4C7F-11D1-8222-444553540000}", capLength ) == 0 ) + encoding = Oscar::Message::UTF8; + } + } + + msg->setEncoding( encoding ); + msg->setTextArray( msgText ); + + if ( ( messageType & 0xF0 ) == 0xE0 ) // check higher byte for value E -> status message request + msg->addProperty( Oscar::Message::StatusMessageRequest ); + else + msg->addProperty( Oscar::Message::Request ); + + msg->setSender( m_fromUser ); + msg->setReceiver( client()->userId() ); + msg->setTimestamp( QDateTime::currentDateTime() ); + msg->setType( 0x02 ); + msg->setIcbmCookie( m_icbmCookie ); + msg->setProtocolVersion( protocolVersion ); + msg->setChannel2Counter( channel2Counter ); + msg->setMessageType( messageType ); + + break; + } + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown message with length2 " << length2 << endl; + } +} + +QTextCodec* MessageReceiverTask::guessCodec( const QCString& string ) +{ + Q_UNUSED( string ); + return 0; +} + +#include "messagereceivertask.moc" +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.h b/kopete/protocols/oscar/liboscar/messagereceivertask.h new file mode 100644 index 00000000..b50a133f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/messagereceivertask.h @@ -0,0 +1,79 @@ +/* + messagereceivertask.h - Incoming OSCAR Messaging Handler + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#ifndef MESSAGERECEIVERTASK_H +#define MESSAGERECEIVERTASK_H + +#include "task.h" +#include <qstring.h> +#include <qcstring.h> +#include "oscarmessage.h" +#include "oscartypeclasses.h" +#include "oscarmessage.h" + +class QTextCodec; + +/** + * Handles receiving messages. + * @author Matt Rogers +*/ +class MessageReceiverTask : public Task +{ +Q_OBJECT +public: + + MessageReceiverTask( Task* parent ); + ~MessageReceiverTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + +signals: + + void receivedMessage( const Oscar::Message& ); + +private: + + //!Handles messages from channel 1 (type 1 messages) + void handleType1Message(); + + //!Handles messages from channel 2 (type 2 messages) + void handleType2Message(); + + //!Handles messages from channel 4 (type 4 messages) + void handleType4Message(); + + //!Handles client auto responses (SNAC 0x04/0x0B) + void handleAutoResponse(); + + //!Parses Rendezvous data in Buffer and puts the information into Message + void parseRendezvousData( Buffer* b, Oscar::Message* msg ); + + QTextCodec* guessCodec( const QCString& string ); + +private: + + QByteArray m_icbmCookie; + int m_channel; + QString m_fromUser; + int m_currentSnacSubtype; + int m_charSet; + int m_subCharSet; + +}; + +#endif + +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp new file mode 100644 index 00000000..d97da7a3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp @@ -0,0 +1,166 @@ +/* + Kopete Oscar Protocol + offlinemessagestask.cpp - Offline messages handling + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "config.h" +#include "offlinemessagestask.h" + +#include <time.h> + +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + +#include <kdebug.h> + +OfflineMessagesTask::OfflineMessagesTask( Task* parent ) + : ICQTask( parent ) +{ + tzset(); + m_sequence = 0; +} + +OfflineMessagesTask::~OfflineMessagesTask() +{ +} + +void OfflineMessagesTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Requesting offline messages" << endl; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x003c ); //offline message request + setSequence( f.sequence ); + Buffer* buf = addInitialData(); + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +bool OfflineMessagesTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( st->buffer()->buffer(), st->buffer()->length() ); + const_cast<OfflineMessagesTask*>(this)->parseInitialData( buf ); + + if ( requestType() == 0x0041 || requestType() == 0x0042 ) + return true; + + return false; +} + +bool OfflineMessagesTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + + if ( requestType() == 0x0041 ) // Offline message + handleOfflineMessage(); + else if ( requestType() == 0x0042 ) // end-of-offline messages + endOfMessages(); + + setTransfer( 0 ); + return true; + } + return false; +} + +void OfflineMessagesTask::handleOfflineMessage() +{ + TLV tlv1 = transfer()->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + buffer->getLEWord(); // data chunk size + DWORD receiverUin = buffer->getLEDWord(); // target uin + buffer->getLEWord(); // request type + buffer->getLEWord(); // request sequence number: 0x0002 + + DWORD senderUin = buffer->getLEDWord(); + WORD year = buffer->getLEWord(); + BYTE month = buffer->getByte(); + BYTE day = buffer->getByte(); + BYTE hour = buffer->getByte(); + BYTE minute = buffer->getByte(); + + BYTE type = buffer->getByte(); // msg type + BYTE flags = buffer->getByte(); // msg flags + + WORD msgLength = buffer->getLEWord(); + QByteArray msg = buffer->getBlock( msgLength ); + + QDate date(year, month, day); + QTime time(hour,minute); +#ifndef HAVE_TM_GMTOFF + int tz = -( ::timezone ); +#else + int tz; + time_t now; + struct tm *tm; + now = ::time(NULL); + tm = ::localtime(&now); + /* daylight = tm->tm_isdst; // another linuxism */ + tz = (tm->tm_gmtoff) / (60 * 60); +#endif + time = time.addSecs( tz ); + + QDateTime hackyTime( date, time ); + Oscar::Message message( Oscar::Message::UserDefined, msg, type, flags, hackyTime ); + message.setSender( QString::number( senderUin ) ); + message.setReceiver( QString::number( receiverUin ) ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received offline message '" << msg.data() << "' from " << senderUin << endl; + + emit receivedOfflineMessage( message ); +} + +void OfflineMessagesTask::endOfMessages() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "End of Offline Messages" << endl; + + TLV tlv1 = transfer()->buffer()->getTLV(); + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + + buffer->skipBytes( 8 ); + m_sequence = buffer->getLEWord(); + + deleteOfflineMessages(); + + setSuccess( true ); +} + +void OfflineMessagesTask::deleteOfflineMessages() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + + setRequestType( 0x003E ); //delete offline messages + setSequence( m_sequence ); + Buffer* buf = addInitialData(); + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +#include "offlinemessagestask.moc" diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.h b/kopete/protocols/oscar/liboscar/offlinemessagestask.h new file mode 100644 index 00000000..da2454d3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.h @@ -0,0 +1,54 @@ +/* + Kopete Oscar Protocol + offlinemessagestask.h - Offline messages handling + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef OFFLINEMESSAGESTASK_H +#define OFFLINEMESSAGESTASK_H + +#include "icqtask.h" +#include "oscarmessage.h" + +/** +ICQ Offline messages handling + +@author Gustavo Pichorim Boiko +*/ +class OfflineMessagesTask : public ICQTask +{ +Q_OBJECT +public: + OfflineMessagesTask( Task* parent ); + + ~OfflineMessagesTask(); + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + +signals: + void receivedOfflineMessage( const Oscar::Message& msg ); + +private: + void handleOfflineMessage(); + void endOfMessages(); + void deleteOfflineMessages(); + +private: + int m_sequence; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp new file mode 100644 index 00000000..785e23f7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp @@ -0,0 +1,99 @@ +/* + Kopete Oscar Protocol + onlinenotifiertask.cpp - handles all the status notifications + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "onlinenotifiertask.h" +#include "buffer.h" +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" + +#include <kdebug.h> + +OnlineNotifierTask::OnlineNotifierTask( Task* parent ) : Task( parent ) +{ +} + + +OnlineNotifierTask::~OnlineNotifierTask() +{ +} + + +bool OnlineNotifierTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0003 ) + { + switch ( st->snacSubtype() ) + { + case 0x000B: + case 0x000C: + return true; + }; + } + return false; +} + +bool OnlineNotifierTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + { + setTransfer( transfer ); + + if ( st->snacSubtype() == 0x000B ) + userOnline(); + else + userOffline(); + + setTransfer( 0 ); + } + return true; + } + return false; +} + +void OnlineNotifierTask::userOnline() +{ + Buffer* buffer = transfer()->buffer(); + UserDetails ud; + ud.fill( buffer ); + QString user = ud.userId(); + //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now online" << endl; + emit userIsOnline( user, ud ); +} + +void OnlineNotifierTask::userOffline() +{ + Buffer* buffer = transfer()->buffer(); + UserDetails ud; + ud.fill( buffer ); + QString user = ud.userId(); + //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now offline" << endl; + emit userIsOffline( user, ud ); +} + +#include "onlinenotifiertask.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.h b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h new file mode 100644 index 00000000..2ec58e5a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h @@ -0,0 +1,60 @@ +/* + Kopete Oscar Protocol + onlinenotifiertask.h - handles all the status notifications + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef ONLINENOTIFIERTASK_H +#define ONLINENOTIFIERTASK_H + +#include <task.h> +#include "userdetails.h" + +class Transfer; +class QString; +/** +Tracks status notifications (online, offline, etc.) for contacts +Implements SNACS (0x03, 0x11) and (0x03, 0x12) + +@author Matt Rogers +*/ +class OnlineNotifierTask : public Task +{ +Q_OBJECT +public: + OnlineNotifierTask( Task* parent ); + + ~OnlineNotifierTask(); + + virtual bool take( Transfer* transfer ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + +signals: + void userIsOnline( const QString& user, const UserDetails& ud ); + void userIsOffline( const QString& user, const UserDetails& ud ); + +private: + void userOnline(); + void userOffline(); + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.cpp b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp new file mode 100644 index 00000000..ea090442 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp @@ -0,0 +1,141 @@ + +/*************************************************************************** + gwbytestream.cpp - Byte Stream using KNetwork sockets + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 Kopete developers <kopete-devel@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include <qobject.h> +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <kresolver.h> + +#include "oscarbytestream.h" + +KNetworkByteStream::KNetworkByteStream( QObject *parent, const char */*name*/ ) + : ByteStream ( parent ) +{ + kdDebug( 14151 ) << k_funcinfo << "Instantiating new KNetwork byte stream." << endl; + + // reset close tracking flag + mClosing = false; + + mSocket = new KNetwork::KBufferedSocket; + + // make sure we get a signal whenever there's data to be read + mSocket->enableRead( true ); + + // connect signals and slots + QObject::connect( mSocket, SIGNAL ( gotError ( int ) ), this, SLOT ( slotError ( int ) ) ); + QObject::connect( mSocket, SIGNAL ( connected ( const KResolverEntry& ) ), this, SLOT ( slotConnected () ) ); + QObject::connect( mSocket, SIGNAL ( closed () ), this, SLOT ( slotConnectionClosed () ) ); + QObject::connect( mSocket, SIGNAL ( readyRead () ), this, SLOT ( slotReadyRead () ) ); + QObject::connect( mSocket, SIGNAL ( bytesWritten ( int ) ), this, SLOT ( slotBytesWritten ( int ) ) ); +} + +bool KNetworkByteStream::connect( QString host, QString service ) +{ + kdDebug( 14151 ) << k_funcinfo << "Connecting to " << host << ", service " << service << endl; + + return socket()->connect( host, service ); +} + +bool KNetworkByteStream::isOpen() const +{ + // determine if socket is open + return socket()->isOpen(); +} + +void KNetworkByteStream::close () +{ +#ifdef OSCAR_EXCESSIVE_DEBUG + kdDebug ( 14151 ) << k_funcinfo << "Closing stream." << endl; +#endif + // close the socket and set flag that we are closing it ourselves + mClosing = true; + socket()->close(); +} + +int KNetworkByteStream::tryWrite () +{ + // send all data from the buffers to the socket + QByteArray writeData = takeWrite(); +#ifdef OSCAR_EXCESSIVE_DEBUG + kdDebug(14151) << k_funcinfo << "writing " << writeData.size() << " bytes." << endl; +#endif + socket()->writeBlock( writeData.data (), writeData.size () ); + return writeData.size(); +} + +KNetwork::KBufferedSocket *KNetworkByteStream::socket() const +{ + return mSocket; +} + +KNetworkByteStream::~KNetworkByteStream() +{ + delete mSocket; +} + +void KNetworkByteStream::slotConnected() +{ + emit connected(); +} + +void KNetworkByteStream::slotConnectionClosed() +{ + kdDebug( 14151 ) << k_funcinfo << "Socket has been closed." << endl; + + // depending on who closed the socket, emit different signals + if ( mClosing ) + { + kdDebug( 14151 ) << "..by ourselves!" << endl; + kdDebug( 14151 ) << "socket error is " << socket()->errorString( socket()->error() ) << endl; + emit connectionClosed (); + } + else + { + kdDebug( 14151 ) << "..by the other end" << endl; + emit delayedCloseFinished (); + } +} + +void KNetworkByteStream::slotReadyRead() +{ + // stuff all available data into our buffers + QByteArray readBuffer( socket()->bytesAvailable () ); + + socket()->readBlock( readBuffer.data (), readBuffer.size () ); + + appendRead( readBuffer ); + + emit readyRead(); +} + +void KNetworkByteStream::slotBytesWritten( int bytes ) +{ + emit bytesWritten( bytes ); +} + +void KNetworkByteStream::slotError( int code ) +{ + kdDebug( 14151 ) << k_funcinfo << "Socket error " << code << endl; + + emit error( code ); +} + +#include "oscarbytestream.moc" + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.h b/kopete/protocols/oscar/liboscar/oscarbytestream.h new file mode 100644 index 00000000..bd618666 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarbytestream.h @@ -0,0 +1,72 @@ + +/*************************************************************************** + gwbytestream.h - Byte Stream using KNetwork sockets + adapted from jabberbytestream.h + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 Kopete developers <kopete-devel@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef KNETWORKBYTESTREAM_H +#define KNETWORKBYTESTREAM_H + +#include <kbufferedsocket.h> + +#include "bytestream.h" + + +/** + * Low level socket class, using KDE's KNetwork socket classes + * @author Till Gerken + */ + +class KNetworkByteStream : public ByteStream +{ + +Q_OBJECT + +public: + KNetworkByteStream ( QObject *parent = 0, const char *name = 0 ); + + ~KNetworkByteStream (); + + bool connect ( QString host, QString service ); + virtual bool isOpen () const; + virtual void close (); + + KNetwork::KBufferedSocket *socket () const; + +signals: + void connected (); + +protected: + virtual int tryWrite (); + +private slots: + void slotConnected (); + void slotConnectionClosed (); + void slotReadyRead (); + void slotBytesWritten ( int ); + void slotError ( int ); + +private: + KNetwork::KBufferedSocket *mSocket; + bool mClosing; + +}; + +#endif + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; + diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.cpp b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp new file mode 100644 index 00000000..e607a24b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp @@ -0,0 +1,437 @@ +/* + oscarclientstream.cpp - Kopete Oscar Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "oscarclientstream.h" + +#include <qguardedptr.h> +#include <qobject.h> +#include <qptrqueue.h> +#include <qtimer.h> + +#include <kdebug.h> + +#include "bytestream.h" +#include "connection.h" +#include "connector.h" +#include "coreprotocol.h" +#include "rateclassmanager.h" +#include "transfer.h" + +#define LIBOSCAR_DEBUG 0 + +void cs_dump( const QByteArray &bytes ); + +enum { + Idle, + Connecting, + Active, + Closing +}; + +enum { + ClientMode, + ServerMode +}; + +class ClientStream::Private +{ +public: + Private() + { + conn = 0; + bs = 0; + connection = 0; + + username = QString::null; + password = QString::null; + server = QString::null; + haveLocalAddr = false; + doBinding = true; + + reset(); + } + void reset() + { + state = Idle; + notify = 0; + newTransfers = false; + } + + QString username; + QString password; + QString server; + bool doAuth; //send the initial login sequences to get the cookie + bool haveLocalAddr; + QHostAddress localAddr; + Q_UINT16 localPort; + bool doBinding; + + Connector *conn; + ByteStream *bs; + CoreProtocol client; + Connection* connection; + + QString defRealm; + + int mode; + int state; + int notify; + bool newTransfers; + + int errCond; + QString errText; + + QPtrQueue<Transfer> in; + + QTimer noopTimer; // used to send icq keepalive + int noop_time; +}; + +ClientStream::ClientStream(Connector *conn, QObject *parent) +:Stream(parent) +{ + //qDebug("CLIENTSTREAM::ClientStream"); + + d = new Private; + d->mode = ClientMode; + d->conn = conn; + connect( d->conn, SIGNAL(connected()), SLOT(cr_connected()) ); + connect( d->conn, SIGNAL(error()), SLOT(cr_error()) ); + connect( &d->client, SIGNAL( outgoingData( const QByteArray& ) ), SLOT ( cp_outgoingData( const QByteArray & ) ) ); + connect( &d->client, SIGNAL( incomingData() ), SLOT ( cp_incomingData() ) ); + + d->noop_time = 0; + connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop())); +} + +ClientStream::~ClientStream() +{ + reset(); + delete d; +} + +void ClientStream::reset(bool all) +{ + d->reset(); + d->noopTimer.stop(); + + // client + if(d->mode == ClientMode) + { + // reset connector + if ( d->bs ) + { + d->bs->close(); + d->bs = 0; + } + if ( d->conn ) + d->conn->done(); + + // reset state machine + d->client.reset(); + } + if(all) + d->in.clear(); +} + +void ClientStream::connectToServer(const QString& server, bool auth) +{ + reset(true); + d->state = Connecting; + d->doAuth = auth; + d->server = server; + + d->conn->connectToServer( d->server ); +} + +void ClientStream::continueAfterWarning() +{ +/* unneeded? + if(d->state == WaitVersion) { + d->state = Connecting; + processNext(); + } + else if(d->state == WaitTLS) { + d->state = Connecting; + processNext(); + } +*/ +} + +void ClientStream::accept() +{ + +} + +bool ClientStream::isActive() const +{ + return (d->state != Idle); +} + +bool ClientStream::isAuthenticated() const +{ + return (d->state == Active); +} + +void ClientStream::setNoopTime(int mills) +{ + d->noop_time = mills; + + if(d->noop_time == 0) { + d->noopTimer.stop(); + return; + } + + if( d->state != Active ) + return; + + d->noopTimer.start( d->noop_time ); +} + +void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port) +{ + d->haveLocalAddr = true; + d->localAddr = addr; + d->localPort = port; +} + +int ClientStream::errorCondition() const +{ + return d->errCond; +} + +QString ClientStream::errorText() const +{ + return d->errText; +} + +void ClientStream::close() +{ + if(d->state == Active) { + d->state = Closing; +// d->client.shutdown(); + processNext(); + } + else if(d->state != Idle && d->state != Closing) { + reset(); + } +} + +void ClientStream::setConnection( Connection *c ) +{ + d->connection = c; +} + +Connection* ClientStream::connection() const +{ + return d->connection; +} + + +bool ClientStream::transfersAvailable() const +{ + return ( !d->in.isEmpty() ); +} + +Transfer* ClientStream::read() +{ + if(d->in.isEmpty()) + return 0; //first from queue... + else + return d->in.dequeue(); +} + +void ClientStream::write( Transfer *request ) +{ + d->client.outgoingTransfer( request ); +} + +void cs_dump( const QByteArray &bytes ) +{ +#if 0 + qDebug( "contains: %i bytes ", bytes.count() ); + uint count = 0; + while ( count < bytes.count() ) + { + int dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + printf( "%02x ", bytes[ count + i ] ); + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf(" | "); + dword = 0; + for ( int i = 0; i < 8; ++i ) + { + if ( count + i < bytes.count() ) + { + int j = bytes [ count + i ]; + if ( j >= 0x20 && j <= 0x7e ) + printf( "%2c ", j ); + else + printf( "%2c ", '.' ); + } + else + printf( " " ); + if ( i == 3 ) + printf( " " ); + } + printf( "\n" ); + count += 8; + } + printf( "\n" ); +#endif + Q_UNUSED( bytes ); +} + +void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes ) +{ + // take formatted bytes from CoreProtocol and put them on the wire + d->bs->write( outgoingBytes ); +} + +void ClientStream::cp_incomingData() +{ + Transfer * incoming = d->client.incomingTransfer(); + if ( incoming ) + { + d->in.enqueue( incoming ); + d->newTransfers = true; + doReadyRead(); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "client signalled incomingData but none was available, state is: " << + d->client.state() << endl; +} + +void ClientStream::cr_connected() +{ + d->bs = d->conn->stream(); + connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed())); + connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished())); + connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead())); + connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int))); + connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int))); + + d->state = Active; + if ( d->noop_time ) + d->noopTimer.start( d->noop_time ); + + QByteArray spare = d->bs->read(); + + QGuardedPtr<QObject> self = this; + emit connected(); + if(!self) + return; +} + +void ClientStream::cr_error() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + reset(); + emit error(ErrConnection); +} + +void ClientStream::bs_connectionClosed() +{ + reset(); + emit connectionClosed(); +} + +void ClientStream::bs_delayedCloseFinished() +{ + // we don't care about this (we track all important data ourself) +} + +void ClientStream::bs_error(int) +{ + // TODO +} + +void ClientStream::bs_readyRead() +{ + QByteArray a; + //qDebug( "size of storage for incoming data is %i bytes.", a.size() ); + a = d->bs->read(); + +#if LIBOSCAR_DEBUG + QCString cs(a.data(), a.size()+1); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "recv: " << a.size() << "bytes" << endl; + cs_dump( a ); +#endif + + d->client.addIncomingData(a); +} + +void ClientStream::bs_bytesWritten(int bytes) +{ +#if LIBOSCAR_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << bytes << " bytes written" << endl; + Q_UNUSED( bytes ); +#else + Q_UNUSED( bytes ); +#endif +} + +void ClientStream::srvProcessNext() +{ +} + +void ClientStream::doReadyRead() +{ + emit readyRead(); +} + +void ClientStream::processNext() +{ + if( !d->in.isEmpty() ) + { + QTimer::singleShot(0, this, SLOT(doReadyRead())); + } +} + +bool ClientStream::handleNeed() +{ + return false; +} + +void ClientStream::doNoop() +{ + if ( d->state != Active ) + return; + + FLAP f = { 0x05, d->connection->flapSequence(), 0 }; + Buffer* b = new Buffer(); //deleted in Transfer destructor + Transfer* t = new FlapTransfer( f, b ); //deleted after being sent + write( t ); +} + +void ClientStream::handleError() +{ +} + +#include "oscarclientstream.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.h b/kopete/protocols/oscar/liboscar/oscarclientstream.h new file mode 100644 index 00000000..f8b4d2b6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarclientstream.h @@ -0,0 +1,164 @@ +/* + oscarclientstream.h - Kopete Oscar Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_CLIENTSTREAM_H +#define OSCAR_CLIENTSTREAM_H + +#include "stream.h" + +// forward defines +class ByteStream; +class Client; +class Connector; +class Connection; +class Transfer; +class QHostAddress; + +class ClientStream : public Stream +{ + Q_OBJECT +public: + enum Error { + ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up + ErrNeg, // Negotiation error, see condition + ErrAuth, // Auth error, see condition + ErrBind // Resource binding error + }; + + enum Warning { + WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol // can be customised for novell versions + WarnNoTLS // there is no chance for TLS at this point + }; + + enum NegCond { + HostGone, // host no longer hosted + HostUnknown, // unknown host + RemoteConnectionFailed, // unable to connect to a required remote resource + SeeOtherHost, // a 'redirect', see errorText() for other host + UnsupportedVersion // unsupported XMPP version + }; + + enum AuthCond { + GenericAuthError, // all-purpose "can't login" error + NoMech, // No appropriate auth mech available + BadProto, // Bad SASL auth protocol + BadServ, // Server failed mutual auth + InvalidUserId, // bad user id + InvalidMech, // bad mechanism + InvalidRealm, // bad realm + MechTooWeak, // can't use mech with this authzid + NotAuthorized, // bad user, bad password, bad creditials + TemporaryAuthFailure // please try again later! + }; + + enum BindCond { + BindNotAllowed, // not allowed to bind a resource + BindConflict // resource in-use + }; + + ClientStream(Connector *conn, QObject *parent=0); + ~ClientStream(); + + void connectToServer(const QString& server, bool auth=true); + void accept(); // server + bool isActive() const; + bool isAuthenticated() const; + + // login params + void setUsername(const QString &s); + void setPassword(const QString &s); + + void setLocalAddr(const QHostAddress &addr, Q_UINT16 port); + + void close(); + + /** Connection related stuff */ + void setConnection( Connection* c ); + Connection* connection() const; + + + /** + * Are there any messages waiting to be read + */ + bool transfersAvailable() const; + + /** + * Read a message received from the server + */ + Transfer * read(); + + /** + * Send a message to the server + */ + void write( Transfer* request ); + + int errorCondition() const; + QString errorText() const; + + // extrahttp://bugs.kde.org/show_bug.cgi?id=85158 +/*# void writeDirect(const QString &s); // must be for debug testing*/ + void setNoopTime(int mills); + +signals: + void connected(); + void securityLayerActivated(int); + void authenticated(); // this signal is ordinarily emitted in processNext + void warning(int); +public slots: + void continueAfterWarning(); + +private slots: + void cr_connected(); + void cr_error(); + /** + * collects wire ready outgoing data from the core protocol and sends + */ + void cp_outgoingData( const QByteArray& ); + /** + * collects parsed incoming data as a transfer from the core protocol and queues + */ + void cp_incomingData(); + + void bs_connectionClosed(); + void bs_delayedCloseFinished(); + void bs_error(int); // server only + void bs_readyRead(); + void bs_bytesWritten(int); + + void doNoop(); + void doReadyRead(); + +private: + class Private; + Private *d; + + void reset(bool all=false); + void processNext(); + bool handleNeed(); + void handleError(); + void srvProcessNext(); + + /** + * convert internal method representation to wire + */ + static char* encode_method(Q_UINT8 method); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarconnector.cpp b/kopete/protocols/oscar/liboscar/oscarconnector.cpp new file mode 100644 index 00000000..6fcef197 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarconnector.cpp @@ -0,0 +1,108 @@ + +/*************************************************************************** + gwconnector.cpp - Socket Connector for KNetwork + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + + Kopete (C) 2004 Kopete developers <kopete-devel@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ + +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <kresolver.h> + +#include "oscarconnector.h" +#include "oscarbytestream.h" + +KNetworkConnector::KNetworkConnector( QObject *parent, const char */*name*/ ) + : Connector( parent ) +{ + kdDebug( 14151 ) << k_funcinfo << "New KNetwork connector." << endl; + + mErrorCode = KNetwork::KSocketBase::NoError; + + mByteStream = new KNetworkByteStream( this ); + + connect( mByteStream, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) ); + connect( mByteStream, SIGNAL ( error ( int ) ), this, SLOT ( slotError ( int ) ) ); + mPort = 0; +} + +KNetworkConnector::~KNetworkConnector() +{ + delete mByteStream; +} + +void KNetworkConnector::connectToServer( const QString &server ) +{ + kdDebug( 14151 ) << k_funcinfo << "Initiating connection to " << mHost << endl; + Q_ASSERT( !mHost.isNull() ); + Q_ASSERT( mPort ); + + mErrorCode = KNetwork::KSocketBase::NoError; + + if ( !mByteStream->connect ( mHost, QString::number ( mPort ) ) ) + { + // Houston, we have a problem + mErrorCode = mByteStream->socket()->error(); + emit error(); + } +} + +void KNetworkConnector::slotConnected() +{ + kdDebug( 14151 ) << k_funcinfo << "We are connected." << endl; + + // FIXME: setPeerAddress() is something different, find out correct usage later + //KInetSocketAddress inetAddress = mStreamSocket->address().asInet().makeIPv6 (); + //setPeerAddress ( QHostAddress ( inetAddress.ipAddress().addr () ), inetAddress.port () ); + + emit connected (); +} + +void KNetworkConnector::slotError( int code ) +{ + kdDebug( 14151 ) << k_funcinfo << "Error detected: " << code << endl; + + mErrorCode = code; + emit error (); +} + +int KNetworkConnector::errorCode() +{ + return mErrorCode; +} + +ByteStream *KNetworkConnector::stream() const +{ + return mByteStream; +} + +void KNetworkConnector::done() +{ + kdDebug ( 14151 ) << k_funcinfo << endl; + mByteStream->close (); +} + +void KNetworkConnector::setOptHostPort( const QString &host, Q_UINT16 port ) +{ + kdDebug ( 14151 ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl; + + mHost = host; + mPort = port; + +} + +#include "oscarconnector.moc" + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscarconnector.h b/kopete/protocols/oscar/liboscar/oscarconnector.h new file mode 100644 index 00000000..d130f7da --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarconnector.h @@ -0,0 +1,69 @@ + +/*************************************************************************** + oscarconnector.h - Socket Connector for KNetwork + ------------------- + begin : Wed Jul 7 2004 + copyright : (C) 2004 by Till Gerken <till@tantalo.net> + (C) 2004 by Matt Rogers <mattr@kde.org> + + Kopete (C) 2004 Kopete developers <kopete-devel@kde.org> + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ + +#ifndef OSCARCONNECTOR_H +#define OSCARCONNECTOR_H + +#include "oscarbytestream.h" + +#include "connector.h" + +class ByteStream; +class KNetworkByteStream; +class KResolverEntry; + +/** +@author Till Gerken +@author Matt Rogers +*/ +class KNetworkConnector : public Connector +{ + +Q_OBJECT + +public: + KNetworkConnector( QObject *parent = 0, const char *name = 0 ); + + virtual ~KNetworkConnector(); + + virtual void connectToServer( const QString &server ); + virtual ByteStream *stream() const; + virtual void done(); + + void setOptHostPort( const QString &host, Q_UINT16 port ); + + int errorCode(); + +private slots: + void slotConnected(); + void slotError( int ); + +private: + QString mHost; + Q_UINT16 mPort; + int mErrorCode; + + KNetworkByteStream *mByteStream; + +}; + +#endif + +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off; diff --git a/kopete/protocols/oscar/liboscar/oscardebug.h b/kopete/protocols/oscar/liboscar/oscardebug.h new file mode 100644 index 00000000..9c2d7e16 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscardebug.h @@ -0,0 +1,35 @@ +// oscardebug.h + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301 USA + +#ifndef OSCARDEBUG_H +#define OSCARDEBUG_H + +//OSCAR debugging definitions + +//uncomment to get debug output for user info parsing +//#define OSCAR_USERINFO_DEBUG + +//uncomment to get excessive amounts of debug info from +//various places in the code +//#define OSCAR_EXCESSIVE_DEBUG + +//uncomment to get packet dumps in the debug output +//#define OSCAR_PACKET_PARSING_DEBUG + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.cpp b/kopete/protocols/oscar/liboscar/oscarmessage.cpp new file mode 100644 index 00000000..f4f512d2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarmessage.cpp @@ -0,0 +1,301 @@ +/* + Kopete Oscar Protocol + oscarmessage.cpp - Oscar Message Object + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "oscarmessage.h" + +#include <qdeepcopy.h> +#include <qtextcodec.h> + + +Oscar::Message::Message() +: m_channel( -1 ), + m_properties( -1 ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_encoding( UserDefined ) +{ +} + +Oscar::Message::Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_textArray( messageText ), + m_timestamp( timestamp ), + m_encoding( messageEncoding ) +{ +} + +Oscar::Message::Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_timestamp( timestamp ), + m_encoding( messageEncoding ) +{ + setTextArray( messageText ); +} + +Oscar::Message::Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec ) +: m_channel( channel ), + m_properties( properties ), + m_messageType( 0 ), + m_protocolVersion( 0 ), + m_channel2Counter( 0 ), + m_timestamp( timestamp ) +{ + setText( messageEncoding, messageText, codec ); +} + +QString Oscar::Message::sender() const +{ + return m_sender; +} + +void Oscar::Message::setSender( const QString& sender ) +{ + m_sender = sender; +} + +QString Oscar::Message::receiver() const +{ + return m_receiver; +} + +void Oscar::Message::setReceiver( const QString& receiver ) +{ + m_receiver = receiver; +} + +QByteArray Oscar::Message::textArray() const +{ + return m_textArray; +} + +QString Oscar::Message::text( QTextCodec *codec ) const +{ + switch ( m_encoding ) + { + case Oscar::Message::UserDefined: + return codec->toUnicode( m_textArray ); + case Oscar::Message::UTF8: + return QString::fromUtf8( m_textArray.data(), m_textArray.size() ); + case Oscar::Message::UCS2: + { + uint len = m_textArray.size() / 2; + QString result; + result.setLength( len ); + QByteArray::ConstIterator p = m_textArray.begin(); + for ( uint i = 0; i < len; i++) + { + char row = *p++; + char cell = *p++; + result[i] = QChar( cell, row ); + } + //check if last character isn't null + if ( result[len-1].isNull() ) + result.setLength( len - 1 ); + + return result; + } + default: + break; // Should never happen. + } + return QString::null; + //FIXME: warn at least with kdWarning if an unrecognised encoding style was seen. +} + +void Oscar::Message::setText( Oscar::Message::Encoding newEncoding, const QString& newText, QTextCodec* codec ) +{ + uint len; + switch ( newEncoding ) + { + case Oscar::Message::UserDefined: + // Oscar::Message::setTextArray( const QCString& ) + // strips trailing null byte automatically. + setTextArray( codec->fromUnicode( newText ) ); + break; + case Oscar::Message::UTF8: + // Oscar::Message::setTextArray( const QCString& ) + // strips trailing null byte automatically. + setTextArray( newText.utf8() ); + break; + case Oscar::Message::UCS2: + { + len = newText.length(); + m_textArray.resize( len * 2 ); + QByteArray::Iterator p = m_textArray.begin(); + for ( uint i = 0; i < len; i++) + { + *p++ = newText[i].row(); + *p++ = newText[i].cell(); + } + break; + } + default: + break; // Should never happen. + } + m_encoding = newEncoding; +} + +void Oscar::Message::setTextArray( const QByteArray& newTextArray ) +{ + m_textArray.duplicate( newTextArray ); +} + +void Oscar::Message::setTextArray( const QCString& newTextArray ) +{ + m_textArray.duplicate( newTextArray ); + uint len = m_textArray.size(); + if ( len > 0 ) + { + --len; + if ( m_textArray[len] == '\0' ) + { + // Strip trailing null byte. + m_textArray.resize( len ); + } + } +} + +int Oscar::Message::properties() const +{ + return m_properties; +} + +void Oscar::Message::addProperty( int prop ) +{ + if ( m_properties == -1 ) + m_properties = 0; + + m_properties = m_properties | prop; +} + +bool Oscar::Message::hasProperty( int prop ) const +{ + if ( m_properties == -1 ) + return false; + if ( ( m_properties & prop ) == 0 ) + return false; + else + return true; +} + +int Oscar::Message::type() const +{ + return m_channel; +} + +void Oscar::Message::setType( int newType ) +{ + m_channel = newType; +} + +QDateTime Oscar::Message::timestamp() const +{ + return m_timestamp; +} + +void Oscar::Message::setTimestamp( QDateTime ts ) +{ + m_timestamp = ts; +} + +QByteArray Oscar::Message::icbmCookie() const +{ + return m_icbmCookie; +} + +void Oscar::Message::setIcbmCookie( const QByteArray& cookie ) +{ + m_icbmCookie.duplicate( cookie ); +} + +int Oscar::Message::protocolVersion() const +{ + return m_protocolVersion; +} + +void Oscar::Message::setProtocolVersion( int version ) +{ + m_protocolVersion = version; +} + +int Oscar::Message::channel2Counter() const +{ + return m_channel2Counter; +} + +void Oscar::Message::setChannel2Counter( int value ) +{ + m_channel2Counter = value; +} + +int Oscar::Message::messageType() const +{ + return m_messageType; +} + +void Oscar::Message::setMessageType( int type ) +{ + m_messageType = type; +} + +Oscar::WORD Oscar::Message::exchange() const +{ + return m_exchange; +} + +void Oscar::Message::setExchange( Oscar::WORD exchange ) +{ + m_exchange = exchange; +} + +QString Oscar::Message::chatRoom() const +{ + return m_chatRoom; +} + +void Oscar::Message::setChatRoom( const QString& room ) +{ + m_chatRoom = room; +} + +Oscar::Message::Encoding Oscar::Message::encoding() const +{ + return m_encoding; +} + +void Oscar::Message::setEncoding( Oscar::Message::Encoding newEncoding ) +{ + m_encoding = newEncoding; +} + +Oscar::Message::operator bool() const +{ + return m_channel != -1 && m_properties != -1; +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.h b/kopete/protocols/oscar/liboscar/oscarmessage.h new file mode 100644 index 00000000..7f081054 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarmessage.h @@ -0,0 +1,182 @@ +/* + Kopete Oscar Protocol + oscarmessage.h - Oscar Message Object + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de> + Copyright (c) 2005 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARMESSAGE_H_ +#define _OSCARMESSAGE_H_ + +#include <qglobal.h> +#include <qstring.h> +#include <qcstring.h> +#include <qdatetime.h> +#include "kopete_export.h" +#include "oscartypes.h" + +class QTextCodec; + +namespace Oscar +{ + +/** + * This class is responsible for holding all the details + * of a message and includes the following: + * \li channel ( type ) + * \li encoding + */ + +class KOPETE_EXPORT Message +{ +public: + + enum { + Normal = 0x0000, + AutoResponse = 0x0001, + WWP = 0x0002, + EMail = 0x0004, + ChatRoom = 0x0008, + Request = 0x0100, + StatusMessageRequest = 0x0200 + }; + + enum Encoding { + UserDefined, + UTF8, + UCS2 + }; + + Message(); + + Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp ); + Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp ); + Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec = 0 ); + + /** Get the sender of the message */ + QString sender() const; + + /** Set the sender of the message */ + void setSender( const QString& sender ); + + /** Get the receiver of the message */ + QString receiver() const; + + /** Set the receiver of the message */ + void setReceiver( const QString& receiver); + + /** get the message text */ + QString text( QTextCodec* codec ) const; + + /** set the message text */ + void setText( Encoding newEncoding, const QString& newText, QTextCodec* codec = 0); + + /** get the message text as a bytearray for decoding */ + QByteArray textArray() const; + + /** set the message text as a bytearray for decoding */ + void setTextArray( const QByteArray& newTextArray ); + + /** set the mesasge text as a bytearray for decoding */ + void setTextArray( const QCString& newTextArray ); + + /** get the message properties */ + int properties() const; + + /** ask about a specific property */ + bool hasProperty( int prop ) const; + + /** add a property to the message */ + void addProperty( int prop ); + + /** get the channel ( type ) of the message */ + int type() const; + + /** set the channel ( type ) of the message */ + void setType( int newType ); + + /** get the timestamp of the message */ + QDateTime timestamp() const; + + /** set the timestamp of the message */ + void setTimestamp( QDateTime ts ); + + /** get the ICBM cookie of the message */ + QByteArray icbmCookie() const; + + /** set the ICBM cookie of the message */ + void setIcbmCookie( const QByteArray& cookie ); + + /** get the protocol version (channel 2 messages only) */ + int protocolVersion() const; + + /** prepare for handling of different protocol versions */ + void setProtocolVersion( int version ); + + /** get the channel 2 counter value of the message */ + int channel2Counter() const; // channel 2 message have an additional counter whose value needs be kept in a request response + + /** set the channel 2 counter value */ + void setChannel2Counter( int value ); + + /** get the message (content) type */ + int messageType() const; + + /** set the message (content) type */ + void setMessageType( int type ); + + /** get the exchange for the chat room this message is for */ + Oscar::WORD exchange() const; + + /** set the exchange for the chat room this message is for */ + void setExchange( Oscar::WORD ); + + /** get the chat room that this message is for */ + QString chatRoom() const; + + /** set the chat room that this message is for */ + void setChatRoom( const QString& ); + + /** get the message encoding */ + Encoding encoding() const; + + /** set the message encoding */ + void setEncoding( Encoding newEncoding ); + + operator bool() const; + +private: + //TODO d-pointer + QString m_sender; + QString m_receiver; + int m_channel; + int m_properties; + int m_messageType; + int m_protocolVersion; + int m_channel2Counter; + QByteArray m_icbmCookie; + QByteArray m_textArray; + QDateTime m_timestamp; + Oscar::WORD m_exchange; + QString m_chatRoom; + Encoding m_encoding; +}; + +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.cpp b/kopete/protocols/oscar/liboscar/oscarsettings.cpp new file mode 100644 index 00000000..36b0bb12 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarsettings.cpp @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + Oscar Backend Setting Storage + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "oscarsettings.h" + +namespace Oscar +{ + +Settings::Settings() +{ +} + + +Settings::~Settings() +{ +} + +void Settings::setWebAware( bool aware ) +{ + m_webAware = aware; +} + +bool Settings::webAware() const +{ + return m_webAware; +} + +void Settings::setRequireAuth( bool require ) +{ + m_requireAuth = require; +} + +bool Settings::requireAuth() const +{ + return m_requireAuth; +} + +void Settings::setHideIP( bool hide ) +{ + m_hideIP = hide; +} + +bool Settings::hideIP() const +{ + return m_hideIP; +} + + + + + +} + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.h b/kopete/protocols/oscar/liboscar/oscarsettings.h new file mode 100644 index 00000000..12ece2e6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarsettings.h @@ -0,0 +1,62 @@ +/* + Kopete Oscar Protocol + Oscar Backend Setting Storage + + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef OSCARSETTINGS_H +#define OSCARSETTINGS_H + +#include "kopete_export.h" + +namespace Oscar +{ + +/** +* This class is used to keep track of various persistant settings that the backend will always +* need to get from the frontend. This is the interface and storage class that will handle the +* settings. +* @author Matt Rogers +*/ +class KOPETE_EXPORT Settings +{ +public: + Settings(); + ~Settings(); + + /* Web awareness settings */ + void setWebAware( bool webAware ); + bool webAware() const; + + /* Authorization settings */ + void setRequireAuth( bool require ); + bool requireAuth() const; + + /* Hide IP Settings */ + void setHideIP( bool hide ); + bool hideIP() const; + +private: + + bool m_webAware; + bool m_requireAuth; + bool m_hideIP; +}; + +} + +#endif + +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp new file mode 100644 index 00000000..cd9e9619 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp @@ -0,0 +1,284 @@ +/* + Kopete Oscar Protocol + oscartypeclasses.cpp - Oscar Type Definitions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "oscartypeclasses.h" +#include <qdeepcopy.h> +#include <qvaluelist.h> +#include <kdebug.h> +#include "oscarutils.h" +#include "buffer.h" + + +// using namespace Oscar; + +Oscar::TLV::TLV() +{ + type = 0; + length = 0; +} + +Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, char* newData ) +{ + type = newType; + length = newLength; + data.truncate(0); + data.duplicate( newData, length ); +} + +Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, const QByteArray& newData ) +{ + type = newType; + length = newLength; + data.duplicate( newData ); +} + +Oscar::TLV::TLV( const TLV& t ) +{ + type = t.type; + length = t.length; + data.truncate(0); + data.duplicate( t.data ); +} + +Oscar::TLV::operator bool() const +{ + return type != 0; +} + + +Oscar::SSI::SSI() +{ + m_gid = 0; + m_bid = 0; + m_type = 0xFFFF; + m_tlvLength = 0; + m_waitingAuth = false; +} + +Oscar::SSI::SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV> &tlvlist, int tlvLength ) +{ + m_name = name; + m_gid = gid; + m_bid = bid; + m_type = type; + m_tlvLength = tlvLength; + + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( tlvlist ); + + if ( m_tlvLength == 0 && !m_tlvList.isEmpty() ) + refreshTLVLength(); + + checkTLVs(); +} + +Oscar::SSI::SSI( const Oscar::SSI& other ) +{ + m_name = other.m_name; + m_gid = other.m_gid; + m_bid = other.m_bid; + m_type = other.m_type; + m_tlvLength = other.m_tlvLength; + m_alias = other.m_alias; + m_waitingAuth = other.m_waitingAuth; + + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( other.m_tlvList ); + + if ( m_tlvLength == 0 && !m_tlvList.isEmpty() ) + refreshTLVLength(); +} + +bool Oscar::SSI::isValid() const +{ + return m_type != 0xFFFF; +} + +QString Oscar::SSI::name() const +{ + return m_name; +} + +Q_UINT16 Oscar::SSI::gid() const +{ + return m_gid; +} + +Q_UINT16 Oscar::SSI::bid() const +{ + return m_bid; +} + +Q_UINT16 Oscar::SSI::type() const +{ + return m_type; +} + +const QValueList<TLV>& Oscar::SSI::tlvList() const +{ + return m_tlvList; +} + +void Oscar::SSI::setTLVListLength( Q_UINT16 newLength ) +{ + m_tlvLength = newLength; +} + +Q_UINT16 Oscar::SSI::tlvListLength() const +{ + return m_tlvLength; +} + +void Oscar::SSI::setTLVList( QValueList<TLV> list ) +{ + //deepcopy the tlvs + m_tlvList = QDeepCopy< QValueList<TLV> >( list ); + refreshTLVLength(); + checkTLVs(); +} + +void Oscar::SSI::refreshTLVLength() +{ + m_tlvLength = 0; + QValueList<TLV>::iterator it = m_tlvList.begin(); + for( ; it != m_tlvList.end(); ++it ) + { + m_tlvLength += 4; + m_tlvLength += (*it).length; + } +} + +void Oscar::SSI::checkTLVs() +{ + //check for the auth TLV + TLV authTLV = findTLV( m_tlvList, 0x0066 ); + if ( authTLV ) + { + kdDebug(14151) << k_funcinfo << "Need auth for contact " << m_name << endl; + m_waitingAuth = true; + } + else + m_waitingAuth = false; + + //check for the alias TLV + TLV aliasTLV = findTLV( m_tlvList, 0x0131 ); + if ( aliasTLV ) + { + m_alias = QString::fromUtf8( aliasTLV.data, aliasTLV.length ); + kdDebug( 14151 ) << k_funcinfo << "Got an alias '" << m_alias << "' for contact '" << m_name << "'" << endl; + } + + TLV privacyTLV = findTLV( m_tlvList, 0x00CA ); + if ( privacyTLV ) + kdDebug(14151) << k_funcinfo << "Found privacy settings " << privacyTLV.data << endl; + + TLV infoTLV = findTLV( m_tlvList, 0x00CC ); + if ( infoTLV ) + kdDebug(14151) << k_funcinfo << "Found 'allow others to see...' options " << infoTLV.data << endl; +} + +QString Oscar::SSI::alias() const +{ + return m_alias; +} + +void Oscar::SSI::setAlias( const QString& newAlias ) +{ + m_alias = newAlias; +} + +bool Oscar::SSI::waitingAuth() const +{ + return m_waitingAuth; +} + +void Oscar::SSI::setWaitingAuth( bool waiting ) +{ + m_waitingAuth = waiting; +} + +void Oscar::SSI::setIconHash( QByteArray hash ) +{ + m_hash.duplicate( hash ); +} + +QByteArray Oscar::SSI::iconHash( ) const +{ + return m_hash; +} + +QString Oscar::SSI::toString() const +{ + QString ssiString = QString::fromLatin1( "name: " ); + ssiString += m_name; + ssiString += " gid: "; + ssiString += QString::number( m_gid ); + ssiString += " bid: "; + ssiString += QString::number( m_bid ); + ssiString += " type: "; + ssiString += QString::number( m_type ); + ssiString += " tlv length: "; + ssiString += QString::number( m_tlvLength ); + return ssiString; +} + +bool Oscar::SSI::operator==( const SSI& item ) const +{ + if ( m_name == item.name() && m_gid == item.gid() && m_bid == item.bid() && m_type == item.type() ) + return true; + else + return false; +} + +Oscar::SSI::operator bool() const +{ + return isValid(); +} + +Oscar::SSI::operator QByteArray() const +{ + Buffer b; + QCString name( m_name.utf8() ); + uint namelen = name.length(); + const char *namedata = name; + b.addWord( namelen ); + // Using namedata instead of name because + // Buffer::addString(QByteArray, DWORD) ignores it's second argument, + // while Buffer::addString(const char*, DWORD) does not ignore it. + // We must provide the explicit length argument to addString() because + // we don't need trailing null byte to be added when automatic + // conversion from QCString to QByteArray is performed. + // This hack will not be needed with Qt 4. + b.addString( namedata, namelen ); + b.addWord( m_gid ); + b.addWord( m_bid ); + b.addWord( m_type ); + b.addWord( m_tlvLength ); + QValueList<Oscar::TLV>::const_iterator it = m_tlvList.begin(); + for( ; it != m_tlvList.end(); ++it ) + { + b.addWord( (*it).type ); + b.addWord( (*it).length ); + b.addString( (*it).data, (*it).data.size() ); + } + + return (QByteArray) b; +} + + +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.h b/kopete/protocols/oscar/liboscar/oscartypeclasses.h new file mode 100644 index 00000000..94e0c910 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.h @@ -0,0 +1,144 @@ +/* + Kopete Oscar Protocol + oscartypeclasses.h - Oscar Type Definitions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARTYPECLASSES_H_ +#define _OSCARTYPECLASSES_H_ + +#include <qglobal.h> +#include <qstring.h> +#include <qcstring.h> +#include <qvaluelist.h> +#include "kopete_export.h" + +namespace Oscar +{ +class KOPETE_EXPORT TLV +{ +public: + + TLV(); + TLV( Q_UINT16, Q_UINT16, char* data ); + TLV( Q_UINT16, Q_UINT16, const QByteArray& ); + TLV( const TLV& t ); + + operator bool() const; + + Q_UINT16 type; + Q_UINT16 length; + QByteArray data; + +}; + +class KOPETE_EXPORT SSI +{ +public: + SSI(); + SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV>& tlvlist, int tlvLength = 0 ); + SSI( const SSI& other ); + + /** Get the validity of this item */ + bool isValid() const; + + /** \brief The name of this SSI item. + * This is usually the screenname, ICQ number, or group name. */ + QString name() const; + + /** \brief The group id of the SSI item */ + Q_UINT16 gid() const; + + /** \brief The buddy id of the SSI item */ + Q_UINT16 bid() const; + + /** + * \brief The type of the SSI Item. + * see ROSTER_* defines on oscartypes.h + * Use a value of 0xFFFF for an SSI item not on the server list + */ + Q_UINT16 type() const; + + /** \brief the TLV list for the item */ + const QValueList<TLV>& tlvList() const; + + /** \brief Set the TLV list for the item */ + void setTLVList( QValueList<TLV> ); + + /** + * \brief Set the length of the TLV list + * + * This is not the number of items in the list!! It's the aggregation of the + * sizes of the TLVs + */ + void setTLVListLength( Q_UINT16 newLength ); + + /** \brief Get the TLV list length */ + Q_UINT16 tlvListLength() const; + + /** + * Get the alias for the SSI item + * This is TLV 0x0131 in an SSI item + */ + QString alias() const; + + /** + * Set the alias for the SSI item + * This should be done after a successful modification of the item + * on the server + */ + void setAlias( const QString& newAlias ); + + /** \brief Indicates we're awaiting authorization from this item */ + bool waitingAuth() const; + + /** Set whether we are waiting authorization or not from this item */ + void setWaitingAuth( bool waiting ); + + void setIconHash( QByteArray hash ); + + QByteArray iconHash() const; + + /** \brief String representation of our SSI object */ + QString toString() const; + + bool operator==( const SSI& item ) const; + operator bool() const; + + operator QByteArray() const; + + void refreshTLVLength(); + + //! parse the TLVs checking for aliases and auth and stuff like that + void checkTLVs(); + +private: + QString m_name; + int m_gid; + int m_bid; + int m_type; + QValueList<TLV> m_tlvList; + int m_tlvLength; + bool m_waitingAuth; + QString m_alias; + QByteArray m_hash; +}; + +} + +//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4; + +#endif diff --git a/kopete/protocols/oscar/liboscar/oscartypes.h b/kopete/protocols/oscar/liboscar/oscartypes.h new file mode 100644 index 00000000..b7d4f55b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscartypes.h @@ -0,0 +1,292 @@ +/* + Kopete Oscar Protocol + oscartypes.h - Oscar Type Definitions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARTYPES_H_ +#define _OSCARTYPES_H_ + +#include "oscartypeclasses.h" +#include <qglobal.h> +#include <qdatetime.h> +#include <qstring.h> + +//! Debug Areas +const int OSCAR_RAW_DEBUG = 14151; +const int OSCAR_GEN_DEBUG = 14150; +const int OSCAR_AIM_DEBUG = 14152; +const int OSCAR_ICQ_DEBUG = 14153; + +namespace Oscar +{ +//! Capabilities +enum Capabilities +{ + CAP_CHAT = 0, CAP_VOICE, CAP_SENDFILE, CAP_ISICQ, CAP_IMIMAGE, CAP_BUDDYICON, CAP_SAVESTOCKS, + CAP_GETFILE, CAP_ICQSERVERRELAY, CAP_GAMES, CAP_GAMES2, CAP_SENDBUDDYLIST, CAP_RTFMSGS, CAP_IS_2001, + CAP_TRILLIAN, CAP_TRILLIANCRYPT, CAP_APINFO, CAP_UTF8, CAP_TYPING, CAP_INTEROPERATE, CAP_KOPETE, CAP_MICQ, + CAP_MACICQ, CAP_SIMOLD, CAP_SIMNEW, CAP_XTRAZ, CAP_STR_2001, CAP_STR_2002, CAP_LAST +}; + +typedef unsigned char cap[16]; +const cap oscar_caps[] = +{ + //CAP_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + //CAP_VOICE, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_ISICQ, + {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_IMIMAGE, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SAVESTOCKS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_ICQSERVERRELAY, + {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_RTFMSGS, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}, + + // CAP_IS_2001, + {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, + 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}, + + // CAP_TRILLIAN + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}, + + // CAP_TRILLIANCRYPT + {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, + 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}, + + // CAP_APINFO, + {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6, + 0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}, + + // CAP_UTF8, + {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_TYPING - client supports mini typing notifications + {0x56, 0x3F, 0xC8, 0x09, 0x0B, 0x6f, 0x41, 0xBD, + 0x9F, 0x79, 0x42, 0x26, 0x09, 0xDF, 0xA2, 0xF3}, + + // CAP_INTEROPERATE, + {0x09, 0x46, 0x13, 0x4D, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_KOPETE, + // last 4 bytes determine version + // NOTE change with each Kopete Release! + // first number, major version + // second number, minor version + // third number, point version 100+ + // fourth number, point version 0-99 + {'K', 'o', 'p', 'e', 't', 'e', ' ', 'I', + 'C', 'Q', ' ', ' ', 0, 12, 0, 7}, + + // CAP_MICQ + // last 4 bytes determine version + {0x6d, 0x49, 0x43, 0x51, 0x20, 0xa9, 0x20, 0x52, + 0x2e, 0x4b, 0x2e, 0x20, 0x00, 0x00, 0x00, 0x00}, + + // CAP_MACICQ + {0xDD, 0x16, 0xF2, 0x02, 0x84, 0xE6, 0x11, 0xD4, + 0x90, 0xDB, 0x00, 0x10, 0x4B, 0x9B, 0x4B, 0x7D}, + + // CAP_SIMOLD + // last byte determines version + // (major + 1) << 6 + minor + {0x97, 0xB1, 0x27, 0x51, 0x24, 0x3C, 0x43, 0x34, + 0xAD, 0x22, 0xD6, 0xAB, 0xF7, 0x3F, 0x14, 0x00}, + + // CAP_SIMNEW + // last 4 bytes determine version (US-ASCII encoded) + {'S', 'I', 'M', ' ', 'c', 'l', 'i', 'e', + 'n', 't', ' ', ' ', 0 , 0 , 0 , 0}, + + // CAP_XTRAZ + {0x1A, 0x09, 0x3C, 0x6C, 0xD7, 0xFD, 0x4E, 0xC5, + 0x9D, 0x51, 0xA6, 0x47, 0x4E, 0x34, 0xF5, 0xA0}, + + // CAP_STR_2001 + {0xA0, 0xE9, 0x3F, 0x37, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_STR_2002 + {0x10, 0xCF, 0x40, 0xD1, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, + + // CAP_LAST, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +//! Oscar Data Types +typedef Q_UINT8 BYTE; +typedef Q_UINT16 WORD; +typedef Q_UINT32 DWORD; + + +struct FLAP +{ + BYTE channel; + WORD sequence; + WORD length; +}; + +struct SNAC +{ + WORD family; + WORD subtype; + WORD flags; + DWORD id; +}; + +struct RateInfo +{ + WORD classId; + DWORD windowSize; + DWORD initialLevel; + DWORD clearLevel; + DWORD alertLevel; + DWORD limitLevel; + DWORD disconnectLevel; + DWORD currentLevel; + DWORD maxLevel; + DWORD lastTime; + BYTE currentState; +}; + +struct ChatExchangeInfo +{ + WORD number; + WORD maxRooms; + WORD maxRoomNameLength; + WORD maxMsgLength; + BYTE flags; + QString description; + BYTE canCreate; + QString charset1; + QString charset2; + QString lang1; + QString lang2; +}; + +struct ChatRoomInfo +{ + WORD exchange; + QByteArray cookie; + WORD instance; + QString description; + WORD maxMsgLength; + QString name; +}; + +struct ClientVersion +{ + QString clientString; + WORD clientId; + WORD major; + WORD minor; + WORD point; + WORD build; + DWORD other; + QString country; + QString lang; +}; + + /* ICQ Version Characteristics */ + const unsigned char ICQ_TCP_VERSION = 0x0008; + + /* AIM Version Characteristics */ + const char AIM_MD5_STRING[] = "AOL Instant Messenger (SM)"; + + /* SSI types */ + const WORD ROSTER_CONTACT = 0x0000; // a normal contact + const WORD ROSTER_GROUP = 0x0001; // a group of contacts + const WORD ROSTER_VISIBLE = 0x0002; // a contact on the visible list + const WORD ROSTER_INVISIBLE = 0x0003; // a contact on the invisible list + const WORD ROSTER_VISIBILITY = 0x0004; // this entry contains visibility setting TLV(0xca)=TLV(202) + const WORD ROSTER_PRESENCE = 0x0005; // Presence info (if others can see your idle status, etc) + const WORD ROSTER_ICQSHORTCUT = 0x0009; // Unknown or ICQ2k shortcut bar items + const WORD ROSTER_IGNORE = 0x000e; // a contact on the ignore list + const WORD ROSTER_LASTUPDATE = 0x000F; // Last update date (name: "LastUpdateDate") + const WORD ROSTER_NONICQ = 0x0010; // a non-icq contact, no UIN, used to send SMS + const WORD ROSTER_IMPORTTIME = 0x0013; // roster import time (name: "Import time") + const WORD ROSTER_BUDDYICONS = 0x0014; // Buddy icon info. (names: from "0" and incrementing by one) + + /* User classes/statuses */ + const WORD CLASS_UNCONFIRMED = 0x0001; // AOL Unconfirmed user + const WORD CLASS_ADMINISTRATOR = 0x0002; // AOL Administrator + const WORD CLASS_AOL = 0x0004; // AOL Staff + const WORD CLASS_COMMERCIAL = 0x0008; // AOL commercial account + const WORD CLASS_FREE = 0x0010; // ICQ non-commerical account + const WORD CLASS_AWAY = 0x0020; // Away status + const WORD CLASS_ICQ = 0x0040; // ICQ user + const WORD CLASS_WIRELESS = 0x0080; // AOL wireless user + const WORD CLASS_UNKNOWN100 = 0x0100; // Unknown + const WORD CLASS_UNKNOWN400 = 0x0400; // Unknown + const WORD CLASS_UNKNOWN800 = 0x0800; // Unknown + + const WORD STATUS_ONLINE = 0x0000; // Online + const WORD STATUS_AWAY = 0x0001; // Away + const WORD STATUS_DND = 0x0002; // Do not Disturb + const WORD STATUS_NA = 0x0004; // Not Available + const WORD STATUS_OCCUPIED = 0x0010; // Occupied (BUSY/BISY) + const WORD STATUS_FREE4CHAT = 0x0020; // Free for chat + const WORD STATUS_INVISIBLE = 0x0100; // Invisible +} + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarutils.cpp b/kopete/protocols/oscar/liboscar/oscarutils.cpp new file mode 100644 index 00000000..13e28d9e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarutils.cpp @@ -0,0 +1,300 @@ +/* + Kopete Oscar Protocol + oscarutils.cpp - Oscar Utility Functions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "oscarutils.h" +#include <qhostaddress.h> +#include <kapplication.h> +#include <kdebug.h> + + +using namespace Oscar; + +QString Oscar::normalize( const QString& contact ) +{ + QString normal = contact.lower(); + normal.remove( ' ' ); + return normal; +} + +bool Oscar::operator==( TLV a, TLV b ) +{ + if ( a.type == b.type && a.length == b.length ) + return true; + else + return false; +} + +TLV Oscar::findTLV( const QValueList<TLV>& list, int type ) +{ + TLV t; + QValueList<TLV>::const_iterator it; + for ( it = list.begin(); it != list.end(); ++it ) + { + if ( ( *it ).type == type ) + return ( *it ); + } + + return t; +} + +bool Oscar::uptateTLVs( SSI& item, const QValueList<TLV>& list ) +{ + bool changed = false; + QValueList<TLV> tList( item.tlvList() ); + + QValueList<TLV>::const_iterator it; + for ( it = list.begin(); it != list.end(); ++it ) + { + TLV t = Oscar::findTLV( tList, ( *it ).type ); + if ( t && t.length == ( *it ).length && + memcmp( t.data.data(), ( *it ).data.data(), t.length ) == 0 ) + continue; + + if ( t ) + tList.remove( t ); + + tList.append( *it ); + changed = true; + } + + if ( changed ) + item.setTLVList( tList ); + + return changed; +} + +int Oscar::parseCap( char* cap ) +{ + int capflag = -1; + for (int i = 0; i < CAP_LAST; i++) + { + if (memcmp(&oscar_caps[i], cap, 16) == 0) + { + capflag = i; + break; // should only match once... + } + } + return capflag; +} + +const QString Oscar::capToString( char* cap ) +{ + QString dbg; + + dbg.sprintf( "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}", + cap[0], cap[1], cap[2], cap[3], cap[4], cap[5], cap[6], cap[7], cap[8], cap[9], + cap[10], cap[11], cap[12], cap[13], cap[14], cap[15] ); + + return dbg; +} + +DWORD Oscar::parseCapabilities( Buffer &inbuf, QString &versionString ) +{ + // + // FIXME: port capabilities array to some qt based list class, makes usage of memcmp obsolete + // + DWORD capflags = 0; + QString dbgCaps = "CAPS: "; + + while(inbuf.length() >= 16) + { + QByteArray cap; + cap.duplicate( inbuf.getBlock(16) ); + + for (int i=0; i < CAP_LAST; i++) + { + if (i == CAP_KOPETE) + { + if (memcmp(&oscar_caps[i], cap.data(), 12) == 0) + { + capflags |= (1 << i); + versionString.sprintf( "%d.%d.%d%d", cap[12], cap[13], cap[14], cap[15] ); + versionString.insert( 0, "Kopete " ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Kopete version - " << versionString << endl; + } + } + else if (i == CAP_MICQ) + { + if (memcmp(&oscar_caps[i], cap.data(), 12) == 0) + { + kdDebug(14150) << k_funcinfo << "MICQ version : <" << + (int)cap[12] << ":" << (int)cap[13] << ":" << + (int)cap[14] << ":" << (int)cap[15] << ">" << endl; + + capflags |= (1 << i); + + // FIXME: how to decode this micq version mess? [mETz - 08.06.2004] + /*versionString.sprintf("%d.%d.%d%d", + cap[12], cap[13], cap[14], cap[15]);*/ + break; + } + } + else if (i == CAP_SIMNEW) + { + if (memcmp(&oscar_caps[i], cap, 12) == 0) + { + kdDebug(14150) << k_funcinfo << "SIM version : <" << + (unsigned int)cap[12] << ":" << (unsigned int)cap[13] << ":" << + (unsigned int)cap[14] << ":" << (unsigned int)cap[15] << ">" << endl; + capflags |= (1 << i); + versionString.sprintf("%d.%d.%d%d", + cap[12], cap[13], cap[14], cap[15]); + versionString.insert( 0, "SIM " ); + break; + } + } + else if (i == CAP_SIMOLD) + { + if (memcmp(&oscar_caps[i], cap, 15) == 0) + { + int hiVersion = (cap[15] >> 6) - 1; + unsigned loVersion = cap[15] & 0x1F; + kdDebug(14150) << k_funcinfo << "OLD SIM version : <" << + hiVersion << ":" << loVersion << endl; + capflags |= (1 << i); + versionString.sprintf("%d.%d", (unsigned int)hiVersion, loVersion); + versionString.insert( 0, "SIM " ); + break; + } + } + else if (memcmp(&oscar_caps[i], cap.data(), 16) == 0) + { + capflags |= (1 << i); + dbgCaps += capName(i); + break; + } // END if(memcmp... + } // END for... + } + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << dbgCaps << endl; + return capflags; +} + +const QString Oscar::capName( int capNumber ) +{ + QString capString; + + switch ( capNumber ) + { + case CAP_VOICE: + capString = "CAP_VOICE "; + break; + case CAP_BUDDYICON: + capString = "CAP_BUDDYICON "; + break; + case CAP_IMIMAGE: + capString = "CAP_IMIMAGE "; + break; + case CAP_CHAT: + capString = "CAP_CHAT "; + break; + case CAP_GETFILE: + capString = "CAP_GETFILE "; + break; + case CAP_SENDFILE: + capString = "CAP_SENDFILE "; + break; + case CAP_GAMES2: + case CAP_GAMES: + capString = "CAP_GAMES "; + break; + case CAP_SAVESTOCKS: + capString = "CAP_SAVESTOCKS "; + break; + case CAP_SENDBUDDYLIST: + capString = "CAP_SENDBUDDYLIST "; + break; + case CAP_ISICQ: + capString = "CAP_ISICQ "; + break; + case CAP_APINFO: + capString = "CAP_APINFO "; + break; + case CAP_RTFMSGS: + capString = "CAP_RTFMSGS "; + break; + case CAP_ICQSERVERRELAY: + capString = "CAP_ICQSERVERRELAY "; + break; + case CAP_IS_2001: + capString = "CAP_IS_2001 "; + break; + case CAP_TRILLIAN: + capString = "CAP_TRILLIAN "; + break; + case CAP_TRILLIANCRYPT: + capString = "CAP_TRILLIANCRYPT "; + break; + case CAP_UTF8: + capString = "CAP_UTF8 "; + break; + case CAP_TYPING: + capString = "CAP_TYPING "; + break; + case CAP_INTEROPERATE: + capString = "CAP_INTEROPERATE "; + break; + case CAP_KOPETE: + capString = "CAP_KOPETE "; + break; + case CAP_MICQ: + capString = "CAP_MICQ "; + break; + case CAP_MACICQ: + capString = "CAP_MACICQ "; + break; + case CAP_SIMOLD: + capString = "CAP_SIMOLD "; + break; + case CAP_SIMNEW: + capString = "CAP_SIMNEW "; + break; + case CAP_XTRAZ: + capString = "CAP_XTRAZ "; + break; + case CAP_STR_2001: + capString = "CAP_STR_2001 "; + break; + case CAP_STR_2002: + capString = "CAP_STR_2002 "; + break; + default: + capString = "UNKNOWN CAP "; + } // END switch + + return capString; +} + +DWORD Oscar::getNumericalIP(const QString &address) +{ + QHostAddress addr; + if ( addr.setAddress( address ) == false ) + return 0; + + return (DWORD)addr.toIPv4Address(); +} + +QString Oscar::getDottedDecimal( DWORD address ) +{ + QHostAddress addr; + addr.setAddress((Q_UINT32)address); + return addr.toString(); +} + + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/oscarutils.h b/kopete/protocols/oscar/liboscar/oscarutils.h new file mode 100644 index 00000000..bf8b5aba --- /dev/null +++ b/kopete/protocols/oscar/liboscar/oscarutils.h @@ -0,0 +1,93 @@ +/* + Kopete Oscar Protocol + oscarutils.h - Oscar Utility Functions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef _OSCARUTILS_H_ +#define _OSCARUTILS_H_ + +#include <qglobal.h> +#include <qvaluelist.h> +#include <qstring.h> +#include "oscartypes.h" +#include "buffer.h" + +namespace Oscar +{ + +///Normalize the contact name to all lowercase and no spaces +KOPETE_EXPORT QString normalize( const QString& ); + +///compare TLVs for equality +KOPETE_EXPORT bool operator==( TLV, TLV ); + +/** + * Find the TLV corresponding to the type in the list + */ +KOPETE_EXPORT TLV findTLV( const QValueList<TLV>&, int type ); + +/** + * Update TLVs of SSI item from TLV list if necessary + * \return true if something was updated + */ +KOPETE_EXPORT bool uptateTLVs( SSI& item, const QValueList<TLV>& list ); + +/** + * Get the value of the capability that corresponds to the value + * in the Capabilities enum + * \return -1 if the capability isn't found + * \return a non-negative number corresponding to the value of the + * capability in the Capabilities enum + */ +int parseCap( char* cap ); + +/** + * Convert the capability to a string we can print + */ +const QString capToString(char *cap); + +/** + * Parse the character array for validness and a version string + * \param buffer the buffer we'll be parsing for capabilities + * \param versionString a QString reference that will contain the + * version string of the detected client. Will be QString::null if + * no client is found + * \return a DWORD containg a bit array of the capabilities we found + */ +DWORD parseCapabilities(Buffer &inbuf, QString &versionString); + +/** + * Get the name of the capability from its number + */ +const QString capName( int capNumber ); + +/** + * Convert an IP address in dotted decimal notation to a + * numerical constant + */ +DWORD getNumericalIP( const QString& address ); + +/** + * Convert a numerical constant that is an IP address to + * dotted decimal format + */ +QString getDottedDecimal( DWORD address ); + +} + +#endif + +//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp new file mode 100644 index 00000000..a1baf073 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp @@ -0,0 +1,137 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "ownuserinfotask.h" +#include <qcstring.h> +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" +#include "userdetails.h" +#include "ssimanager.h" + + +using namespace Oscar; + +OwnUserInfoTask::OwnUserInfoTask( Task* parent ) : Task( parent ) +{ +} + + +OwnUserInfoTask::~OwnUserInfoTask() +{ +} + + +bool OwnUserInfoTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + else + { + if ( st->snacService() == 0x01 && + ( st->snacSubtype() == 0x0F || st->snacSubtype() == 0x21 ) ) + return true; + else + return false; + } + +} + +bool OwnUserInfoTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( !st ) + return false; + + Buffer* b = transfer->buffer(); + if ( st->snacSubtype() == 0x0F ) + { + UserDetails ud; + ud.fill( b ); + m_details = ud; + emit gotInfo(); + setSuccess( 0, QString::null ); + return true; + } + else + { + bool needUpload = false; + WORD infoType = b->getWord(); + if ( infoType == 0x0000 || infoType == 0x0001 ) + { + BYTE flags = b->getByte(); + if ( flags == 0x41 ) //we need to do a buddy upload when bit 8 = 1 + needUpload = true; + + QByteArray qba; + if ( b->length() != 0 ) + { //buffer might be empty if flags bit 8 = 1 + BYTE checksumLength = b->getByte(); + qba.duplicate( b->getBlock( checksumLength ) ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Self icon checksum: " << qba << endl; + } + + if ( needUpload ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buddy icon upload requested" << endl; + emit buddyIconUploadRequested(); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no item for hash found" << endl; + } + } + + if ( infoType == 0x0002 ) + { + QString availableMsg( b->getBSTR() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "self available message: " << endl; + } + + setSuccess( 0, QString::null ); + return true; + } + + } + + return false; +} + +void OwnUserInfoTask::onGo() +{ + //Send SNAC( 0x01, 0x0E ) + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x000E, 0x0000, client()->snacSequence() }; + Buffer *b = new Buffer(); //empty snac data + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +UserDetails OwnUserInfoTask::getInfo() const +{ + return m_details; +} + +//kate: tab-width 4; indent-mode csands; + +#include "ownuserinfotask.moc" diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.h b/kopete/protocols/oscar/liboscar/ownuserinfotask.h new file mode 100644 index 00000000..30a169db --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + aimlogintask.h - Handles logging into to the AIM service + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef OWNUSERINFOTASK_H +#define OWNUSERINFOTASK_H + +#include "task.h" +#include "userdetails.h" + +/** +Request our user info from the server and handle our user info when it comes back + +@author Kopete Developers +*/ +class OwnUserInfoTask : public Task +{ +Q_OBJECT +public: + OwnUserInfoTask( Task* parent ); + + ~OwnUserInfoTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + + UserDetails getInfo() const; + +signals: + /** Emitted when user info is recieved. Needed because succeeded() is only emitted once. */ + void gotInfo(); + + void haveAvailableMessage( const QString& ); + + void haveIconChecksum( const QString& ); + + void buddyIconUploadRequested(); + +private: + UserDetails m_details; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.cpp b/kopete/protocols/oscar/liboscar/prmparamstask.cpp new file mode 100644 index 00000000..3668c73b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/prmparamstask.cpp @@ -0,0 +1,72 @@ +/* + Kopete Oscar Protocol + prmparamstask.h - handle OSCAR protocol errors + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "prmparamstask.h" +#include <kdebug.h> +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" + +using namespace Oscar; + +PRMParamsTask::PRMParamsTask( Task* parent ) + : Task( parent ) +{ +} + + +PRMParamsTask::~PRMParamsTask() +{ +} + + +bool PRMParamsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0009 && st->snacSubtype() == 0x0003 ) + return true; + + return false; +} + +bool PRMParamsTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring PRM Parameters. We don't use them" << endl; + setSuccess( 0, QString::null ); + return true; + } + + return false; +} + +void PRMParamsTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending PRM Parameters request" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0009, 0x0002, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer *t = createTransfer( f, s, buffer ); + send( t ); +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.h b/kopete/protocols/oscar/liboscar/prmparamstask.h new file mode 100644 index 00000000..eebfdc61 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/prmparamstask.h @@ -0,0 +1,42 @@ +/* + Kopete Oscar Protocol + prmparamstask.h - handle OSCAR protocol errors + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef PRMPARAMSTASK_H +#define PRMPARAMSTASK_H + +#include <task.h> + +class Transfer; + +/** +@author Matt Rogers +*/ +class PRMParamsTask : public Task +{ +public: + PRMParamsTask( Task* parent ); + ~PRMParamsTask(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +}; + +#endif + +// kate: space-indent on; tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/profiletask.cpp b/kopete/protocols/oscar/liboscar/profiletask.cpp new file mode 100644 index 00000000..d64d5dbe --- /dev/null +++ b/kopete/protocols/oscar/liboscar/profiletask.cpp @@ -0,0 +1,119 @@ +/* + Kopete Oscar Protocol + profiletask.h - Update the user's profile on the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "profiletask.h" + +#include <qstring.h> +#include <kdebug.h> + +#include "transfer.h" +#include "connection.h" +#include "oscartypes.h" +#include "oscarutils.h" + +using namespace Oscar; + +ProfileTask::ProfileTask( Task* parent ) + : Task( parent ) +{ +} + + +ProfileTask::~ProfileTask() +{ +} + + +bool ProfileTask::forMe( const Transfer* transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +bool ProfileTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +void ProfileTask::onGo() +{ + sendProfileUpdate(); +} + +void ProfileTask::setProfileText( const QString& text ) +{ + m_profileText = text; +} + +void ProfileTask::setAwayMessage( const QString& text ) +{ + m_awayMessage = text; +} + +void ProfileTask::sendProfileUpdate() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_SETUSERINFO/CLI_SET_LOCATION_INFO)" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0004, 0x0000, client()->snacSequence() }; + Buffer *buffer = new Buffer(); + Buffer capBuf; + + if ( !m_profileText.isNull() && !client()->isIcq() ) + { + static const QString defencoding = "text/aolrtf; charset=\"us-ascii\""; + buffer->addTLV(0x0001, defencoding.length(), defencoding.latin1()); + buffer->addTLV(0x0002, m_profileText.length(), m_profileText.local8Bit()); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting profile = " << m_profileText << endl; + } + + if ( !m_awayMessage.isNull() && !client()->isIcq() ) + { + static const QString defencoding = "text/aolrtf; charset=\"us-ascii\""; + buffer->addTLV(0x0003, defencoding.length(), defencoding.latin1()); + buffer->addTLV(0x0004, m_awayMessage.length(), m_awayMessage.local8Bit()); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting away message = " << m_awayMessage << endl; + } + + if ( client()->isIcq() ) + { + capBuf.addString( oscar_caps[CAP_ICQSERVERRELAY], 16 ); // we support type-2 messages + capBuf.addString( oscar_caps[CAP_UTF8], 16 ); // we can send/receive UTF encoded messages + capBuf.addString( oscar_caps[CAP_ISICQ], 16 ); // I think this is an icq client, but maybe I'm wrong + capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile + //capBuf.addString( oscar_caps[CAP_RTFMSGS], 16 ); // we do incoming RTF messages + capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us! + capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture? + } + else + { + capBuf.addString( oscar_caps[CAP_UTF8], 16 ); //we can send/receive UTF encoded messages + capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile + capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us! + capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture? + } + + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding capabilities, size=" << capBuf.length() << endl; + buffer->addTLV(0x0005, capBuf.length(), capBuf.buffer()); + Transfer* st = createTransfer( f, s , buffer ); + send( st ); + setSuccess( 0, QString::null ); + +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/profiletask.h b/kopete/protocols/oscar/liboscar/profiletask.h new file mode 100644 index 00000000..23555105 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/profiletask.h @@ -0,0 +1,56 @@ +/* + Kopete Oscar Protocol + profiletask.h - Update the user's profile on the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef PROFILETASK_H +#define PROFILETASK_H + +#include "task.h" + +/** +Task that sets the profile and away message on the server (AIM only). +Also takes care of updating the capabilities supported by the client (AIM and ICQ). + +The profile will be updated only if the profile text has been set non-null. +The away message will be set only if the away message has been set non-null. + +@author Matt Rogers +*/ +class ProfileTask : public Task +{ +public: + ProfileTask( Task* parent ); + ~ProfileTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + + void setProfileText( const QString& text ); + void setAwayMessage( const QString& text ); + +private: + + void sendProfileUpdate(); + +private: + QString m_profileText; + QString m_awayMessage; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclass.cpp b/kopete/protocols/oscar/liboscar/rateclass.cpp new file mode 100644 index 00000000..7fee4239 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclass.cpp @@ -0,0 +1,246 @@ +/* + rateclass.cpp - Rate Limiting Implementation + + Copyright (c) 2004 by Tom Linsky <thomas.linsky@cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "rateclass.h" +#include <qtimer.h> +#include <kdebug.h> +#include "transfer.h" + +using namespace Oscar; + +RateClass::RateClass( QObject* parent ) +: QObject( parent, 0 ) +{ + m_waitingToSend = false; + m_packetTimer.start(); +} + +RateClass::~ RateClass() +{ + dumpQueue(); + m_members.clear(); +} + +WORD RateClass::id() const +{ + return m_rateInfo.classId; +} + +void RateClass::setRateInfo( RateInfo newRateInfo ) +{ + m_rateInfo.classId = newRateInfo.classId; + m_rateInfo.windowSize = newRateInfo.windowSize; + m_rateInfo.clearLevel = newRateInfo.clearLevel; + m_rateInfo.alertLevel = newRateInfo.alertLevel; + m_rateInfo.limitLevel = newRateInfo.limitLevel; + m_rateInfo.disconnectLevel = newRateInfo.disconnectLevel; + m_rateInfo.currentLevel = newRateInfo.currentLevel; + m_rateInfo.initialLevel = newRateInfo.initialLevel; + m_rateInfo.maxLevel = newRateInfo.maxLevel; + m_rateInfo.lastTime = newRateInfo.lastTime; + m_rateInfo.currentState = newRateInfo.currentState; +} + +void RateClass::addMember( const SNAC& s ) +{ + addMember( s.family, s.subtype ); +} + +void RateClass::addMember( WORD family, WORD subtype ) +{ + SnacPair snacPair; + snacPair.family = family; + snacPair.subtype = subtype; + m_members.append( snacPair ); +} + +bool RateClass::isMember(const SNAC &s) const +{ + QValueList<SnacPair>::const_iterator it; + QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd(); + for ( it = m_members.constBegin(); it != spEnd; ++it ) + { + if ( ( *it ).family == s.family && ( *it ).subtype == s.subtype ) + return true; + } + return false; +} + +bool RateClass::isMember( WORD family, WORD subtype ) const +{ + + QValueList<SnacPair>::const_iterator it; + QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd(); + for ( it = m_members.constBegin(); it != spEnd; ++it ) + { + if ( ( *it ).family == family && ( *it ).subtype == subtype ) + { + return true; + } + } + return false; +} + +void RateClass::enqueue( Transfer* t ) +{ + m_packetQueue.push_back( t ); + /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Send queue length is now: " + << m_packetQueue.count() << endl;*/ + setupTimer(); +} + +void RateClass::dequeue() +{ + m_packetQueue.pop_front(); +} + +bool RateClass::queueIsEmpty() const +{ + return m_packetQueue.isEmpty(); +} + +int RateClass::timeToInitialLevel() +{ + DWORD newLevel = 0; + + //get time elapsed since the last packet was sent + int timeDiff = m_packetTimer.elapsed(); + + newLevel = calcNewLevel( timeDiff ); + + if ( newLevel < m_rateInfo.initialLevel ) + { + int waitTime = ( m_rateInfo.initialLevel * m_rateInfo.windowSize ) - ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ); + return waitTime; + } + + return 0; +} + +int RateClass::timeToNextSend() +{ + + DWORD newLevel = 0; + + //get time elapsed since the last packet was sent + int timeDiff = m_packetTimer.elapsed(); + + DWORD windowSize = m_rateInfo.windowSize; + newLevel = calcNewLevel( timeDiff ); + + //The maximum level at which we can safely send a packet + DWORD maxPacket = m_rateInfo.alertLevel + RATE_SAFETY_TIME; + +/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate Information:" + << "\nWindow Size: " << windowSize + << "\nWindow Size - 1: " << windowSize - 1 + << "\nOld Level: " << m_rateInfo.currentLevel + << "\nAlert Level: " << m_rateInfo.alertLevel + << "\nLimit Level: " << m_rateInfo.limitLevel + << "\nDisconnect Level: " << m_rateInfo.disconnectLevel + << "\nNew Level: " << newLevel + << "\nTime elapsed: " << timeDiff + << "\nMax level to send: " << maxPacket << endl; */ + + //If we are one packet or less away from being blocked, wait to send + if ( newLevel < maxPacket || newLevel < m_rateInfo.disconnectLevel ) + { + int waitTime = ( windowSize * maxPacket ) - ( ( windowSize - 1 ) * m_rateInfo.currentLevel ); + kdDebug(OSCAR_RAW_DEBUG) << "We're sending too fast. Will wait " << waitTime << "ms before sending" << endl; + return waitTime; + } + + return 0; +} + +DWORD RateClass::calcNewLevel( int timeDifference ) const +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Time since last packet: " + // << timeDifference << endl; + //Calculate new rate level + //NewLevel = ( ( Window - 1 ) * OldLevel + TimeDiff )/Window + //add 1 because we never want to round down or there will be problems + uint newLevel = ( ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ) + timeDifference ) / m_rateInfo.windowSize; + if ( newLevel > m_rateInfo.initialLevel ) + newLevel = m_rateInfo.initialLevel; + + return newLevel; +} + +void RateClass::setupTimer() +{ + if ( !m_waitingToSend ) + { + m_waitingToSend = true; + + int ttns = timeToNextSend(); + if ( ttns <= 0 ) + { + slotSend(); //send now + } + else + { + QTimer::singleShot( ttns, this, SLOT( slotSend() ) ); //or send later + } + } +} + +void RateClass::slotSend() +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl; + + if ( m_packetQueue.isEmpty() ) + return; + + //send then pop off the list + emit dataReady( m_packetQueue.first() ); + dequeue(); + + updateRateInfo(); + + m_waitingToSend = false; + + // check if we still have packets to send + if ( !m_packetQueue.isEmpty() ) + setupTimer(); +} + +void RateClass::updateRateInfo() +{ + //Update rate info + DWORD newLevel = calcNewLevel( m_packetTimer.elapsed() ); + m_rateInfo.currentLevel = newLevel; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current Level = " << newLevel << endl; + + //restart the timer + m_packetTimer.restart(); +} + +void RateClass::dumpQueue() +{ + QValueList<Transfer*>::iterator it = m_packetQueue.begin(); + while ( it != m_packetQueue.end() && m_packetQueue.count() > 0 ) + { + Transfer* t = ( *it ); + it = m_packetQueue.remove( it ); + delete t; + } +} + +#include "rateclass.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclass.h b/kopete/protocols/oscar/liboscar/rateclass.h new file mode 100644 index 00000000..1bb86f03 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclass.h @@ -0,0 +1,132 @@ +/* + rateclass.h - Oscar Rate Limiting Implementation + + Copyright (c) 2004 by Tom Linsky <twl6@po.cwru.edu> + Copyright (c) 2004 by Matt Rogers <mattr@k + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef RATECLASS_H +#define RATECLASS_H + +#include "oscartypes.h" +#include <qobject.h> +#include <qvaluelist.h> +#include <qdatetime.h> +#include <qpair.h> + +const int RATE_SAFETY_TIME = 50; + +struct SnacPair +{ + int family; + int subtype; +}; + +class Transfer; + +class RateClass : public QObject +{ + Q_OBJECT +public: + RateClass( QObject* parent = 0 ); + ~RateClass(); + + /** Accessor for classid */ + Oscar::WORD id() const; + + /** Sets rate information */ + void setRateInfo( Oscar::RateInfo newRateInfo ); + + /** Add a SNAC to the rate class */ + void addMember( const Oscar::SNAC& s ); + + /** Adds rate class members */ + void addMember( Oscar::WORD family, Oscar::WORD subtype ); + + /** Tells whether the passed snac is a member of this rate class */ + bool isMember( const Oscar::SNAC& s ) const; + + /** + * Tells whether the passed family and subtype combo is a member + * of this rate class + */ + bool isMember( Oscar::WORD family, Oscar::WORD subtype ) const; + + /** Add a packet to the queue */ + void enqueue( Transfer* ); + + /** Takes a packet off the front of the queue */ + void dequeue(); + + /** Check if the queue is empty */ + bool queueIsEmpty() const; + + /** + * Calulate the time until we can send again + * Uses the first packet on the queue to determine the time since that's + * the packet that will get sent. + * \return the time in milliseconds that we need to wait + */ + int timeToNextSend(); + + /** + * Calulate the time until we get to initial level + * \return the time in milliseconds that we need to wait + */ + int timeToInitialLevel(); + + /** + * Calculates a new rate level and updates the rate class' current level + * to match + */ + void updateRateInfo(); + + /** + * Dump the current packet queue. These packets will not be sent. Used + * on disconnection + */ + void dumpQueue(); + +signals: + + /** Tell the rate class manager we're ready to send */ + void dataReady( Transfer* ); + +private: + + /** Calculate our new rate level */ + Oscar::DWORD calcNewLevel( int timeDifference ) const; + + /** sets up the timer for the transfer just added to the queue */ + void setupTimer(); + +private slots: + /** + * Send the packet. Basically emits dataReady for the first transfer + */ + void slotSend(); + +private: + + Oscar::RateInfo m_rateInfo; + QValueList<SnacPair> m_members; + QValueList<Transfer*> m_packetQueue; + QTime m_packetTimer; + + // we are waiting for the QTimer::singleShot() to send + bool m_waitingToSend; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.cpp b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp new file mode 100644 index 00000000..8b306c0b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp @@ -0,0 +1,177 @@ +/* + Kopete Oscar Protocol + rateclassmanager.cpp - Manages the rates we get from the OSCAR server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qvaluelist.h> +#include <kdebug.h> + + +#include "rateclassmanager.h" +#include "transfer.h" +#include "connection.h" +#include "rateclass.h" + + +class RateClassManagerPrivate +{ +public: + //! The list of rate classes owned by this manager + QValueList<RateClass*> classList; + Connection* client; +}; + +RateClassManager::RateClassManager( Connection* parent, const char* name ) +: QObject( parent, name ) +{ + d = new RateClassManagerPrivate(); + d->client = parent; +} + +RateClassManager::~RateClassManager() +{ + reset(); + delete d; +} + +void RateClassManager::reset() +{ + QValueList<RateClass*>::iterator it = d->classList.begin(); + while ( it != d->classList.end() && d->classList.count() > 0) + { + RateClass* rc = ( *it ); + it = d->classList.remove( it ); + delete rc; + } +} + +void RateClassManager::registerClass( RateClass* rc ) +{ + QObject::connect( rc, SIGNAL( dataReady( Transfer* ) ), this, SLOT( transferReady( Transfer* ) ) ); + d->classList.append( rc ); +} + +bool RateClassManager::canSend( Transfer* t ) const +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + + if ( !st ) //no snac transfer, no rate limiting + { kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not sending a snac" << endl; + return true; + } + + RateClass* rc = findRateClass( st ); + if ( rc ) + { + if ( rc->timeToNextSend() == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's okay to send" << endl; + return true; + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's not okay to send yet" << endl; + return false; + } + } + else // no rate class + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no rate class. doing no rate limiting" << endl; + return true; + } +} + +void RateClassManager::queue( Transfer* t ) +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + if ( !st ) + { //we're not sending a snac + transferReady( t ); + return; + } + + RateClass* rc = findRateClass( st ); + if ( rc ) + rc->enqueue( st ); + else + transferReady( t ); +} + +QValueList<RateClass*> RateClassManager::classList() const +{ + return d->classList; +} + +void RateClassManager::transferReady( Transfer* t ) +{ + //tell the client to send it again. We should be + //able to send it now + FlapTransfer* ft = dynamic_cast<FlapTransfer*>( t ); + + if ( ft ) + ft->setFlapSequence( d->client->flapSequence() ); + + d->client->forcedSend( t ); +} + + +RateClass* RateClassManager::findRateClass( SnacTransfer* st ) const +{ + SNAC s = st->snac(); + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Looking for SNAC " << s.family << ", " << s.subtype << endl; + RateClass* rc = 0L; + QValueList<RateClass*>::const_iterator it; + QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd(); + + for ( it = d->classList.constBegin(); it != rcEnd; ++it ) + { + if ( ( *it )->isMember( s.family, s.subtype ) ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found SNAC(" << s.family << ", " << s.subtype << ") in class" << endl; + rc = ( *it ); + break; + } + } + + return rc; +} + +void RateClassManager::recalcRateLevels() +{ + QValueList<RateClass*>::iterator it; + QValueList<RateClass*>::iterator rcEnd = d->classList.end(); + for ( it = d->classList.begin(); it != rcEnd; ++it ) + ( *it )->updateRateInfo(); +} + +int RateClassManager::timeToInitialLevel( SNAC s ) +{ + QValueList<RateClass*>::const_iterator it; + QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd(); + + for ( it = d->classList.constBegin(); it != rcEnd; ++it ) + { + if ( ( *it )->isMember( s.family, s.subtype ) ) + { + return ( *it )->timeToInitialLevel(); + } + } + return 0; +} + +#include "rateclassmanager.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.h b/kopete/protocols/oscar/liboscar/rateclassmanager.h new file mode 100644 index 00000000..4b972ede --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateclassmanager.h @@ -0,0 +1,83 @@ +/* + Kopete Oscar Protocol + rateclassmanager.h - Manages the rates we get from the OSCAR server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef RATECLASSMANAGER_H +#define RATECLASSMANAGER_H + +#include <qobject.h> +#include <qvaluelist.h> +#include <qmap.h> +#include "oscartypes.h" + +class Transfer; +class SnacTransfer; +class RateClass; +class Connection; +class RateClassManagerPrivate; + + +class RateClassManager : public QObject +{ +Q_OBJECT +public: + RateClassManager( Connection* parent, const char* name = 0 ); + ~RateClassManager(); + + /** Reset the rate manager */ + void reset(); + + /** Tell the rate manager about the new class */ + void registerClass( RateClass* ); + + //! Check if we can send the packet right away + bool canSend( Transfer* t ) const; + + //! Queue a transfer for sending later + void queue( Transfer* t ); + + /** Get the list of rate classes */ + QValueList<RateClass*> classList() const; + + /** Recalculate the rate levels for all the classes */ + void recalcRateLevels(); + + /** + * Find the rate class for the snac and + * calculate time until we get to initial level + * \return the time in milliseconds that we need to wait + */ + int timeToInitialLevel( Oscar::SNAC s ); + +public slots: + + void transferReady( Transfer* ); + +private: + + /** Find the rate class for the transfer */ + RateClass* findRateClass( SnacTransfer* st ) const; + +private: + + RateClassManagerPrivate* d; + +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.cpp b/kopete/protocols/oscar/liboscar/rateinfotask.cpp new file mode 100644 index 00000000..f19cf792 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateinfotask.cpp @@ -0,0 +1,173 @@ +/* + Kopete Oscar Protocol + rateinfotask.cpp - Fetch the rate class information + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "rateinfotask.h" + +#include <qvaluelist.h> +#include <kdebug.h> +#include "rateclass.h" +#include "rateclassmanager.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" +#include "connection.h" + +using namespace Oscar; + +RateInfoTask::RateInfoTask( Task* parent ) + : Task( parent ) +{ + connect( this, SIGNAL( gotRateLimits() ), this, SLOT( sendRateInfoAck() ) ); +} + + +RateInfoTask::~RateInfoTask() +{ + +} + + +bool RateInfoTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( st && st->snacService() == 1 && st->snacSubtype() == 7 ) + return true; + else + return false; +} + +bool RateInfoTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleRateInfoResponse(); + setTransfer( 0 ); + return true; + } + return false; +} + +void RateInfoTask::onGo() +{ + sendRateInfoRequest(); +} + +void RateInfoTask::sendRateInfoRequest() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info request (SNAC 0x01, 0x06)" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0006, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* st = createTransfer( f, s, buffer ); + send( st ); +} + +void RateInfoTask::handleRateInfoResponse() +{ + QValueList<RateClass*> rates; + Oscar::RateInfo ri; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "handling rate info response (SNAC 0x01, 0x07)" << endl; + Buffer* buffer = transfer()->buffer(); + + int numClasses = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got " << numClasses << " rate classes" << endl; + for ( int i = 0; i < numClasses; i++ ) + { + RateClass* newClass = new RateClass( client()->rateManager() ); + //parse rate classes and put them somewhere + ri.classId = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate class: " << ri.classId << endl; + //discard the rest (for right now) + ri.windowSize = buffer->getDWord(); //window size + ri.clearLevel = buffer->getDWord(); //clear level + ri.alertLevel = buffer->getDWord(); //alert level + ri.limitLevel = buffer->getDWord(); //limit level + ri.disconnectLevel = buffer->getDWord(); //disconnect level + ri.currentLevel = buffer->getDWord(); //current level + ri.initialLevel = ri.currentLevel; + ri.maxLevel = buffer->getDWord(); //max level + ri.lastTime = buffer->getDWord(); //last time + ri.currentState = buffer->getByte(); //current state + + newClass->setRateInfo( ri ); + rates.append( newClass ); + } + + int groupNum = 0; + int numGroupPairs = 0; + + for ( int i = 0; i < numClasses; i++ ) + { + groupNum = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding snac members to group " << groupNum << endl; + + RateClass* rc = 0L; + QValueList<RateClass*>::iterator it = rates.begin(); + for ( ; it != rates.end(); ++it ) + { + if ( ( *it )->id() == groupNum ) + { + rc = ( *it ); + break; + } + } + + m_rateGroups.append( groupNum ); + numGroupPairs = buffer->getWord(); + for ( int j = 0; j < numGroupPairs; j++ ) + { + WORD family = buffer->getWord(); + WORD subtype = buffer->getWord(); + rc->addMember( family, subtype ); + } + } + + QValueList<RateClass*>::iterator it = rates.begin(); + QValueList<RateClass*>::iterator rcEnd = rates.end(); + for ( ; it != rcEnd; ++it ) + client()->rateManager()->registerClass( ( *it ) ); + + emit gotRateLimits(); +} + +void RateInfoTask::sendRateInfoAck() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info acknowledgement" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0008, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + QValueListConstIterator<int> cit = m_rateGroups.begin(); + QValueListConstIterator<int> end = m_rateGroups.end(); + for ( cit = m_rateGroups.begin(); cit != end; ++cit ) + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding rate " << (*cit) << " to rate ack" << endl; + buffer->addWord( (*cit) ); + } + + Transfer* st = createTransfer( f, s, buffer ); + send( st ); + setSuccess( 0, QString::null ); +} + +#include "rateinfotask.moc" + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.h b/kopete/protocols/oscar/liboscar/rateinfotask.h new file mode 100644 index 00000000..3964f0ea --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rateinfotask.h @@ -0,0 +1,64 @@ +/* + Kopete Oscar Protocol + rateinfotask.h - Fetch the rate class information + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef RATEINFOTASK_H +#define RATEINFOTASK_H + +#include "task.h" +#include <qvaluelist.h> + +using namespace Oscar; + +/** +@author Matt Rogers +*/ +class RateInfoTask : public Task +{ +Q_OBJECT +public: + RateInfoTask( Task* parent ); + ~RateInfoTask(); + bool take( Transfer* transfer ); + +protected: + + bool forMe( const Transfer* transfer ) const; + void onGo(); + +signals: + void gotRateLimits(); + +private slots: + + //! Send the rate info request (SNAC 0x01, 0x06) + void sendRateInfoRequest(); + + //! Handle the rate info response (SNAC 0x01, 0x07) + void handleRateInfoResponse(); + + //! Acknowledge the rate information + void sendRateInfoAck(); + +private: + bool m_needRateAck; + QValueList<int> m_rateGroups; +}; + +//kate: tab-width 4; indent-mode csands; + +#endif diff --git a/kopete/protocols/oscar/liboscar/rtf.cc b/kopete/protocols/oscar/liboscar/rtf.cc new file mode 100644 index 00000000..6daa636e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf.cc @@ -0,0 +1,2427 @@ +#define yy_create_buffer rtf_create_buffer +#define yy_delete_buffer rtf_delete_buffer +#define yy_scan_buffer rtf_scan_buffer +#define yy_scan_string rtf_scan_string +#define yy_scan_bytes rtf_scan_bytes +#define yy_flex_debug rtf_flex_debug +#define yy_init_buffer rtf_init_buffer +#define yy_flush_buffer rtf_flush_buffer +#define yy_load_buffer_state rtf_load_buffer_state +#define yy_switch_to_buffer rtf_switch_to_buffer +#define yyin rtfin +#define yyleng rtfleng +#define yylex rtflex +#define yyout rtfout +#define yyrestart rtfrestart +#define yytext rtftext +#define yywrap rtfwrap + +#line 20 "rtf.cc" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header$ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> +#include <unistd.h> + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +static yyconst short int yy_accept[33] = + { 0, + 0, 0, 11, 8, 8, 9, 9, 1, 2, 8, + 0, 0, 5, 3, 5, 0, 0, 5, 5, 5, + 0, 6, 5, 7, 5, 5, 5, 4, 5, 5, + 5, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 4, 1, 1, 1, 5, 1, + 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 1, 1, 7, + 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 1, 12, 1, 1, 1, 1, 10, 10, 10, 10, + + 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 13, 11, 11, 11, + 11, 11, 14, 1, 15, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[16] = + { 0, + 1, 1, 2, 1, 1, 2, 3, 4, 1, 2, + 2, 3, 2, 3, 3 + } ; + +static yyconst short int yy_base[37] = + { 0, + 0, 14, 45, 0, 0, 39, 25, 59, 59, 0, + 38, 0, 2, 59, 14, 0, 3, 59, 16, 21, + 25, 59, 28, 59, 38, 23, 19, 59, 17, 12, + 5, 59, 47, 51, 1, 55 + } ; + +static yyconst short int yy_def[37] = + { 0, + 33, 33, 32, 34, 34, 32, 32, 32, 32, 34, + 32, 32, 35, 32, 35, 36, 32, 32, 32, 32, + 36, 32, 32, 32, 32, 32, 25, 32, 25, 25, + 25, 0, 32, 32, 32, 32 + } ; + +static yyconst short int yy_nxt[75] = + { 0, + 32, 5, 13, 32, 18, 17, 6, 19, 22, 17, + 19, 7, 22, 8, 9, 5, 18, 31, 18, 20, + 6, 19, 30, 18, 29, 7, 23, 8, 9, 12, + 18, 28, 24, 25, 13, 13, 14, 15, 14, 14, + 26, 16, 11, 27, 32, 32, 28, 4, 4, 4, + 4, 10, 10, 32, 10, 21, 21, 21, 3, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yyconst short int yy_chk[75] = + { 0, + 0, 1, 35, 0, 13, 12, 1, 13, 17, 12, + 31, 1, 17, 1, 1, 2, 15, 30, 19, 15, + 2, 19, 29, 20, 27, 2, 20, 2, 2, 7, + 23, 26, 21, 23, 7, 7, 7, 7, 7, 7, + 25, 11, 6, 25, 3, 0, 25, 33, 33, 33, + 33, 34, 34, 0, 34, 36, 36, 36, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "rtf.ll" +#define INITIAL 0 +#line 2 "rtf.ll" +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +#define YY_NO_UNPUT 1 +#define YY_STACK_USED 0 +#line 447 "rtf.cc" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 46 "rtf.ll" + + +#line 601 "rtf.cc" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 59 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 48 "rtf.ll" +{ return UP; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 49 "rtf.ll" +{ return DOWN; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 50 "rtf.ll" +{ return SLASH; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 51 "rtf.ll" +{ return UNICODE_CHAR; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 52 "rtf.ll" +{ return CMD; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 53 "rtf.ll" +{ return HEX; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 54 "rtf.ll" +{ return IMG; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 55 "rtf.ll" +{ return TXT; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 56 "rtf.ll" +{ return TXT; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 57 "rtf.ll" +ECHO; + YY_BREAK +#line 734 "rtf.cc" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 33 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 32); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 57 "rtf.ll" + + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size()) + break; + QColor &c = colors[t.param]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int yywrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = yy_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = yylex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = yytext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << yytext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(yytext+1); + break; + case TXT: + cur_level.setText(yytext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(yytext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(yytext[2]) << 4) + h2d(yytext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = yytext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + yy_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ diff --git a/kopete/protocols/oscar/liboscar/rtf.ll b/kopete/protocols/oscar/liboscar/rtf.ll new file mode 100644 index 00000000..d982234b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf.ll @@ -0,0 +1,864 @@ +%{ +/* + rtf.ll - A simple RTF Parser (Flex code) + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* + +update rtf.cc: +flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll +sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc +rm -f lex.yy.c + +*/ + +#define UP 1 +#define DOWN 2 +#define CMD 3 +#define TXT 4 +#define HEX 5 +#define IMG 6 +#define UNICODE_CHAR 7 +#define SKIP 8 +#define SLASH 9 +#define S_TXT 10 + +#define YY_NEVER_INTERACTIVE 1 +#define YY_ALWAYS_INTERACTIVE 0 +#define YY_MAIN 0 + +%} + +%option nounput +%option nostack +%option prefix="rtf" + +%% + +"{" { return UP; } +"}" { return DOWN; } +"\\"[\\\{\}] { return SLASH; } +"\\u"[0-9]{3,7}[ ]?"?" { return UNICODE_CHAR; } +"\\"[A-Za-z]+[0-9]*[ ]? { return CMD; } +"\\'"[0-9A-Fa-f][0-9A-Fa-f] { return HEX; } +"<##"[^>]+">" { return IMG; } +[^\\{}<]+ { return TXT; } +. { return TXT; } +%% + +#include "rtf2html.h" + +void ParStyle::clearFormatting() +{ + // For now, do nothing. + // dir is not a formatting item. +} + +QString RTF2HTML::quoteString(const QString &_str, quoteMode mode) +{ + QString str = _str; + str.replace(QRegExp("&"), "&"); + str.replace(QRegExp("<"), "<"); + str.replace(QRegExp(">"), ">"); + str.replace(QRegExp("\""), """); + str.replace(QRegExp("\r"), ""); + switch (mode){ + case quoteHTML: + str.replace(QRegExp("\n"), "<br>\n"); + break; + case quoteXML: + str.replace(QRegExp("\n"), "<br/>\n"); + break; + default: + break; + } + QRegExp re(" +"); + int len; + int pos = 0; + + while ((pos = re.search(str, pos)) != -1) { + len = re.matchedLength(); + + if (len == 1) + continue; + QString s = " "; + for (int i = 1; i < len; i++) + s += " "; + str.replace(pos, len, s); + } + return str; +} + +RTF2HTML::RTF2HTML() + : cur_level(this) +{ + rtf_ptr = NULL; + bExplicitParagraph = false; +} + +OutTag* RTF2HTML::getTopOutTag(TagEnum tagType) +{ + vector<OutTag>::iterator it, it_end; + for(it = oTags.begin(), it_end = oTags.end(); it != it_end; ++it) + if (it->tag == tagType) + return &(*it); + return NULL; +} + +void RTF2HTML::FlushOutTags() +{ + vector<OutTag>::iterator iter; + for (iter = oTags.begin(); iter != oTags.end(); iter++) + { + OutTag &t = *iter; + switch (t.tag){ + case TAG_FONT_COLOR: + { + // RTF colors are 1-based; colors[] is a 0-based array. + if (t.param > colors.size() || t.param == 0) + break; + QColor &c = colors[t.param-1]; + PrintUnquoted("<span style=\"color:#%02X%02X%02X\">", c.red(), c.green(), c.blue()); + } + break; + case TAG_FONT_SIZE: + PrintUnquoted("<span style=\"font-size:%upt\">", t.param); + break; + case TAG_FONT_FAMILY: + { + FontDef &f = fonts[t.param-1]; + string name = (!f.nonTaggedName.empty()) ? f.nonTaggedName : f.taggedName; + PrintUnquoted("<span style=\"font-family:%s\">", name.c_str()); + } + break; + case TAG_BG_COLOR:{ + if (t.param > colors.size()) + break; + QColor &c = colors[t.param]; + PrintUnquoted("<span style=\"background-color:#%02X%02X%02X;\">", c.red(), c.green(), c.blue()); + break; + } + case TAG_BOLD: + PrintUnquoted("<b>"); + break; + case TAG_ITALIC: + PrintUnquoted("<i>"); + break; + case TAG_UNDERLINE: + PrintUnquoted("<u>"); + break; + default: + break; + } + } + oTags.clear(); +} + +// This function will close the already-opened tag 'tag'. It will take +// care of closing the tags which 'tag' contains first (ie. it will unroll +// the stack till the point where 'tag' is). +void Level::resetTag(TagEnum tag) +{ + // A stack which'll keep tags we had to close in order to reach 'tag'. + // After we close 'tag', we will reopen them. + stack<TagEnum> s; + + while (p->tags.size() > m_nTagsStartPos){ // Don't go further than the point where this level starts. + + TagEnum nTag = p->tags.top(); + + /* A tag will be located in oTags if it still wasn't printed out. + A tag will get printed out only if necessary (e.g. <I></I> will + be optimized away). + Thus, for each tag we remove from the actual tag stack, we also + try to remove a yet-to-be-printed tag, and only if there are no + yet-to-be-printed tags left, we start closing the tags we pop. + The tags have one space - needed for umlaute (�) and .utf8() + */ + if (p->oTags.empty()){ + switch (nTag){ + case TAG_FONT_COLOR: + case TAG_FONT_SIZE: + case TAG_BG_COLOR: + case TAG_FONT_FAMILY: + p->PrintUnquoted(" </span>"); + break; + case TAG_BOLD: + p->PrintUnquoted(" </b>"); + break; + case TAG_ITALIC: + p->PrintUnquoted(" </i>"); + break; + case TAG_UNDERLINE: + p->PrintUnquoted(" </u>"); + break; + default: + break; + } + }else{ + p->oTags.pop_back(); + } + + p->tags.pop(); + if (nTag == tag) break; // if we reached the tag we were looking to close. + s.push(nTag); // remember to reopen this tag + } + + if (tag == TAG_ALL) return; + + while (!s.empty()){ + TagEnum nTag = s.top(); + switch (nTag){ + case TAG_FONT_COLOR:{ + unsigned nFontColor = m_nFontColor; + m_nFontColor = 0; + setFontColor(nFontColor); + break; + } + case TAG_FONT_SIZE:{ + unsigned nFontSize = m_nFontSize; + m_nFontSize = 0; + setFontSize(nFontSize); + break; + } + case TAG_BG_COLOR:{ + unsigned nFontBgColor = m_nFontBgColor; + m_nFontBgColor = 0; + setFontBgColor(nFontBgColor); + break; + } + case TAG_FONT_FAMILY:{ + unsigned nFont = m_nFont; + m_nFont = 0; + setFont(nFont); + break; + } + case TAG_BOLD:{ + bool nBold = m_bBold; + m_bBold = false; + setBold(nBold); + break; + } + case TAG_ITALIC:{ + bool nItalic = m_bItalic; + m_bItalic = false; + setItalic(nItalic); + break; + } + case TAG_UNDERLINE:{ + bool nUnderline = m_bUnderline; + m_bUnderline = false; + setUnderline(nUnderline); + break; + } + default: + break; + } + s.pop(); + } +} + +Level::Level(RTF2HTML *_p) : + p(_p), + m_bFontTbl(false), + m_bColors(false), + m_bFontName(false), + m_bTaggedFontNameOk(false), + m_nFont(0), + m_nEncoding(0) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +Level::Level(const Level &l) : + p(l.p), + m_bFontTbl(l.m_bFontTbl), + m_bColors(l.m_bColors), + m_bFontName(false), + m_bTaggedFontNameOk(l.m_bTaggedFontNameOk), + m_nFont(l.m_nFont), + m_nEncoding(l.m_nEncoding) +{ + m_nTagsStartPos = p->tags.size(); + Init(); +} + +void Level::Init() +{ + m_nFontColor = 0; + m_nFontBgColor = 0; + m_nFontSize = 0; + m_bFontName = false; + m_bBold = false; + m_bItalic = false; + m_bUnderline = false; +} + +void RTF2HTML::PrintUnquoted(const char *str, ...) +{ + char buff[1024]; + va_list ap; + va_start(ap, str); + vsnprintf(buff, sizeof(buff), str, ap); + va_end(ap); + sParagraph += buff; +} + +void RTF2HTML::PrintQuoted(const QString &str) +{ + sParagraph += quoteString(str); +} + +void RTF2HTML::FlushParagraph() +{ + if (!bExplicitParagraph || sParagraph.isEmpty()) + return; + + /* + s += "<p dir=\""; + // Note: Lower-case 'ltr' and 'rtl' are important for Qt. + s += (parStyle.dir == ParStyle::DirRTL ? "rtl" : "ltr"); + s += "\">"; + s += sParagraph; + s += "</p>"; + */ + + s += sParagraph; + s += "<br>"; + + // Clear up the paragraph members + sParagraph = ""; + bExplicitParagraph = false; +} + +void Level::setFont(unsigned nFont) +{ + if (nFont <= 0) + return; + + if (m_bFontTbl){ + if (nFont > p->fonts.size() +1){ + kdDebug(14200) << "Invalid font index (" << + nFont << ") while parsing font table." << endl; + return; + } + if (nFont > p->fonts.size()){ + FontDef f; + f.charset = 0; + p->fonts.push_back(f); + } + m_nFont = nFont; + } + else + { + if (nFont > p->fonts.size()) + { + kdDebug(14200) << "Invalid font index (" << + nFont << ")." << endl; + return; + } + if (m_nFont == nFont) + return; + m_nFont = nFont; + if (m_nFont) resetTag(TAG_FONT_FAMILY); + m_nEncoding = p->fonts[nFont-1].charset; + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, nFont)); + p->PutTag(TAG_FONT_FAMILY); + } +} + +void Level::setFontName() +{ + // This function is only valid during font table parsing. + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + // Be prepared to accept a font name. + m_bFontName = true; + } +} + +void Level::setEncoding(unsigned nEncoding) +{ + if (m_bFontTbl){ + if ((m_nFont > 0) && (m_nFont <= p->fonts.size())) + p->fonts[m_nFont-1].charset = nEncoding; + return; + } + m_nEncoding = nEncoding; +} + +void Level::setBold(bool bBold) +{ + if (m_bBold == bBold) return; + if (m_bBold) resetTag(TAG_BOLD); + m_bBold = bBold; + if (!m_bBold) return; + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); +} + +void Level::setItalic(bool bItalic) +{ + if (m_bItalic == bItalic) return; + if (m_bItalic) resetTag(TAG_ITALIC); + m_bItalic = bItalic; + if (!m_bItalic) return; + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + p->PutTag(TAG_ITALIC); +} + +void Level::setUnderline(bool bUnderline) +{ + if (m_bUnderline == bUnderline) return; + if (m_bUnderline) resetTag(TAG_UNDERLINE); + m_bUnderline = bUnderline; + if (!m_bUnderline) return; + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); +} + +void Level::setFontColor(unsigned short nColor) +{ + if (m_nFontColor == nColor) return; + if (m_nFontColor) resetTag(TAG_FONT_COLOR); + if (nColor > p->colors.size()) return; + m_nFontColor = nColor; + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); +} + +void Level::setFontBgColor(unsigned short nColor) +{ + if (m_nFontBgColor == nColor) return; + if (m_nFontBgColor != 0) resetTag(TAG_BG_COLOR); + if (nColor > p->colors.size()) return; + m_nFontBgColor = nColor; + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); +} + +void Level::setFontSizeHalfPoints(unsigned short nSize) +{ + setFontSize(nSize / 2); +} + +void Level::setFontSize(unsigned short nSize) +{ + if (m_nFontSize == nSize) return; + if (m_nFontSize) resetTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_SIZE, nSize)); + p->PutTag(TAG_FONT_SIZE); + m_nFontSize = nSize; +} + +void Level::startParagraph() +{ + // Whatever tags we have open now, close them. + // We cannot carry let character formatting tags wrap paragraphs, + // since a formatting tag can close at any time and we cannot + // close the paragraph any time we want. + resetTag(TAG_ALL); + + // Flush the current paragraph HTML to the document HTML. + p->FlushParagraph(); + + // Mark this new paragraph as an explicit one (from \par etc.). + p->bExplicitParagraph = true; + + // Restore character formatting + p->oTags.push_back(OutTag(TAG_FONT_SIZE, m_nFontSize)); + p->PutTag(TAG_FONT_SIZE); + p->oTags.push_back(OutTag(TAG_FONT_COLOR, m_nFontColor)); + p->PutTag(TAG_FONT_COLOR); + p->oTags.push_back(OutTag(TAG_FONT_FAMILY, m_nFont)); + p->PutTag(TAG_FONT_FAMILY); + if (m_nFontBgColor != 0) + { + p->oTags.push_back(OutTag(TAG_BG_COLOR, m_nFontBgColor)); + p->PutTag(TAG_BG_COLOR); + } + if (m_bBold) + { + p->oTags.push_back(OutTag(TAG_BOLD, 0)); + p->PutTag(TAG_BOLD); + } + if (m_bItalic) + { + p->PutTag(TAG_ITALIC); + p->oTags.push_back(OutTag(TAG_ITALIC, 0)); + } + if (m_bUnderline) + { + p->oTags.push_back(OutTag(TAG_UNDERLINE, 0)); + p->PutTag(TAG_UNDERLINE); + } +} + +bool Level::isParagraphOpen() const +{ + return p->bExplicitParagraph; +} + +void Level::clearParagraphFormatting() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + // Since we don't implement any of the paragraph formatting tags (e.g. alignment), + // we don't clean up anything here. Note that \pard does NOT clean character + // formatting (such as font size, font weight, italics...). + p->parStyle.clearFormatting(); +} + +void Level::setParagraphDirLTR() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirLTR; +} + +void Level::setParagraphDirRTL() +{ + // implicitly start a paragraph + if (!isParagraphOpen()) + startParagraph(); + p->parStyle.dir = ParStyle::DirRTL; +} + +void Level::addLineBreak() +{ + p->PrintUnquoted("<br/>"); +} + +void Level::reset() +{ + resetTag(TAG_ALL); + if (m_bColors){ + if (m_bColorInit){ + QColor c(m_nRed, m_nGreen, m_nBlue); + p->colors.push_back(c); + resetColors(); + } + return; + } +} + +void Level::setText(const char *str) +{ + if (m_bColors) + { + reset(); + } + else if (m_bFontTbl) + { + if ((m_nFont <= 0) || (m_nFont > p->fonts.size())) + return; + + FontDef& def = p->fonts[m_nFont-1]; + + char *pp = strchr(str, ';'); + unsigned size; + if (pp != NULL) + size = (pp - str); + else + size = strlen(str); + + if (m_bFontName) + { + def.nonTaggedName.append(str, size); + // We know we have the entire name + if (pp != NULL) + m_bFontName = false; + } + else if (!m_bTaggedFontNameOk) + { + def.taggedName.append(str, size); + if (pp != NULL) + m_bTaggedFontNameOk = true; + } + } + else + { + for (; *str; str++) + if ((unsigned char)(*str) >= ' ') break; + if (!*str) return; + p->FlushOutTags(); + text += str; + } +} + +void Level::flush() +{ + if (text.length() == 0) return; + // TODO: Make encoding work in Kopete + /* + const char *encoding = NULL; + if (m_nEncoding){ + for (const ENCODING *c = ICQPlugin::core->encodings; c->language; c++){ + if (!c->bMain) + continue; + if ((unsigned)c->rtf_code == m_nEncoding){ + encoding = c->codec; + break; + } + } + } + if (encoding == NULL) + encoding = p->encoding; + + QTextCodec *codec = ICQClient::_getCodec(encoding); + */ + //p->PrintQuoted(codec->toUnicode(text.c_str(), text.length())); + p->PrintQuoted(text.c_str()); + text = ""; +} + +const unsigned FONTTBL = 0; +const unsigned COLORTBL = 1; +const unsigned RED = 2; +const unsigned GREEN = 3; +const unsigned BLUE = 4; +const unsigned CF = 5; +const unsigned FS = 6; +const unsigned HIGHLIGHT = 7; +const unsigned PARD = 8; +const unsigned PAR = 9; +const unsigned I = 10; +const unsigned B = 11; +const unsigned UL = 12; +const unsigned F = 13; +const unsigned FCHARSET = 14; +const unsigned FNAME = 15; +const unsigned ULNONE = 16; +const unsigned LTRPAR = 17; +const unsigned RTLPAR = 18; +const unsigned LINE = 19; + +static char cmds[] = + "fonttbl\x00" + "colortbl\x00" + "red\x00" + "green\x00" + "blue\x00" + "cf\x00" + "fs\x00" + "highlight\x00" + "pard\x00" + "par\x00" + "i\x00" + "b\x00" + "ul\x00" + "f\x00" + "fcharset\x00" + "fname\x00" + "ulnone\x00" + "ltrpar\x00" + "rtlpar\x00" + "line\x00" + "\x00"; + +int yywrap() { return 1; } + +static char h2d(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return (c - 'A') + 10; + if ((c >= 'a') && (c <= 'f')) + return (c - 'a') + 10; + return 0; +} + +QString RTF2HTML::Parse(const char *rtf, const char *_encoding) +{ + encoding = _encoding; + YY_BUFFER_STATE yy_current_buffer = yy_scan_string(rtf); + rtf_ptr = rtf; + for (;;){ + int res = yylex(); + if (!res) break; + switch (res){ + case UP:{ + cur_level.flush(); + levels.push(cur_level); + break; + } + case DOWN:{ + if (!levels.empty()){ + cur_level.flush(); + cur_level.reset(); + cur_level = levels.top(); + levels.pop(); + } + break; + } + case IMG:{ + cur_level.flush(); + const char ICQIMAGE[] = "icqimage"; + const char *smiles[] = { ":-)" , ":-O" , ":-|" , ":-/" , // 0-3 + ":-(" , ":-*" , ":-/" , ":'(" , // 4-7 + ";-)" , ":-@" , ":-$" , ":-X" , // 8-B + ":-P" , "8-)" , "O:)" , ":-D" }; // C-F + const char *p = yytext + 3; + if ((strlen(p) > strlen(ICQIMAGE)) && !memcmp(p, ICQIMAGE, strlen(ICQIMAGE))){ + unsigned n = 0; + for (p += strlen(ICQIMAGE); *p; p++){ + if ((*p >= '0') && (*p <= '9')){ + n = n << 4; + n += (*p - '0'); + continue; + } + if ((*p >= 'A') && (*p <= 'F')){ + n = n << 4; + n += (*p - 'A') + 10; + continue; + } + if ((*p >= 'a') && (*p <= 'f')){ + n = n << 4; + n += (*p - 'a') + 10; + continue; + } + break; + } + if (n < 16) + PrintUnquoted(" %s ", smiles[n] ); + }else{ + kdDebug(14200) << "Unknown image " << yytext << endl; + } + break; + } + case SKIP: + break; + case SLASH: + cur_level.setText(yytext+1); + break; + case TXT: + cur_level.setText(yytext); + break; + case UNICODE_CHAR:{ + cur_level.flush(); + sParagraph += QChar((unsigned short)(atol(yytext + 2))); + break; + } + case HEX:{ + char s[2]; + s[0] = (h2d(yytext[2]) << 4) + h2d(yytext[3]); + s[1] = 0; + cur_level.setText(s); + break; + } + case CMD: + { + cur_level.flush(); + const char *cmd = yytext + 1; + unsigned n_cmd = 0; + unsigned cmd_size = 0; + int cmd_value = -1; + const char *p; + for (p = cmd; *p; p++, cmd_size++) + if (((*p >= '0') && (*p <= '9')) || (*p == ' ')) break; + if (*p && (*p != ' ')) cmd_value = atol(p); + for (p = cmds; *p; p += strlen(p) + 1, n_cmd++){ + if (strlen(p) > cmd_size) continue; + if (!memcmp(p, cmd, cmd_size)) break; + } + cmd += strlen(p); + switch (n_cmd){ + case FONTTBL: // fonttbl + cur_level.setFontTbl(); + break; + case COLORTBL: + cur_level.setColors(); + break; + case RED: + cur_level.setRed(cmd_value); + break; + case GREEN: + cur_level.setGreen(cmd_value); + break; + case BLUE: + cur_level.setBlue(cmd_value); + break; + case CF: + cur_level.setFontColor(cmd_value); + break; + case FS: + cur_level.setFontSizeHalfPoints(cmd_value); + break; + case HIGHLIGHT: + cur_level.setFontBgColor(cmd_value); + break; + case PARD: + cur_level.clearParagraphFormatting(); + break; + case PAR: + cur_level.startParagraph(); + break; + case I: + cur_level.setItalic(cmd_value != 0); + break; + case B: + cur_level.setBold(cmd_value != 0); + break; + case UL: + cur_level.setUnderline(cmd_value != 0); + break; + case ULNONE: + cur_level.setUnderline(false); + break; + case F: + // RTF fonts are 0-based; our font index is 1-based. + cur_level.setFont(cmd_value+1); + break; + case FCHARSET: + cur_level.setEncoding(cmd_value); + break; + case FNAME: + cur_level.setFontName(); + break; + case LTRPAR: + cur_level.setParagraphDirLTR(); + break; + case RTLPAR: + cur_level.setParagraphDirRTL(); + break; + case LINE: + cur_level.addLineBreak(); + } + break; + } + } + } + yy_delete_buffer(yy_current_buffer); + yy_current_buffer = NULL; + FlushParagraph(); + return s; +} + +/* +bool ICQClient::parseRTF(const char *rtf, const char *encoding, QString &res) +{ + char _RTF[] = "{\\rtf"; + if ((strlen(rtf) > strlen(_RTF)) && !memcmp(rtf, _RTF, strlen(_RTF))){ + RTF2HTML p; + res = p.Parse(rtf, encoding); + return true; + } + QTextCodec *codec = ICQClient::_getCodec(encoding); + res = codec->toUnicode(rtf, strlen(rtf)); + return false; +} +*/ diff --git a/kopete/protocols/oscar/liboscar/rtf2html.h b/kopete/protocols/oscar/liboscar/rtf2html.h new file mode 100644 index 00000000..a305b89d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/rtf2html.h @@ -0,0 +1,207 @@ +/* + rtf2html.h - A simple RTF Parser + + Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code) + Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port) + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef RTF2HTML_H +#define RTF2HTML_H + +#include <qstring.h> +#include <stdio.h> + +#include <qtextcodec.h> +#include <qcolor.h> +#include <qregexp.h> +#include <kdebug.h> + +#include <vector> +#include <stack> +#include <string> +#include <stdarg.h> + +using namespace std; + +struct FontDef +{ + int charset; + string taggedName; + string nonTaggedName; +}; + +class RTF2HTML; + +enum TagEnum +{ + TAG_ALL = 0, + TAG_FONT_SIZE, + TAG_FONT_COLOR, + TAG_FONT_FAMILY, + TAG_BG_COLOR, + TAG_BOLD, + TAG_ITALIC, + TAG_UNDERLINE +}; + +class ParStyle +{ +public: + ParStyle() { dir = DirLTR; } + void clearFormatting(); + +public: + enum {DirLTR, DirRTL} dir; +}; + +class Level +{ +public: + Level(RTF2HTML *_p); + Level(const Level&); + void setText(const char* str); + void setFontTbl() { m_bFontTbl = true; } + void setColors() { m_bColors = true; resetColors(); } + void setRed(unsigned char val) { setColor(val, &m_nRed); } + void setGreen(unsigned char val) { setColor(val, &m_nGreen); } + void setBlue(unsigned char val) { setColor(val, &m_nBlue); } + void setFont(unsigned nFont); + void setEncoding(unsigned nFont); + void setFontName(); + void setFontColor(unsigned short color); + void setFontBgColor(unsigned short color); + void setFontSizeHalfPoints(unsigned short sizeInHalfPoints); + void setFontSize(unsigned short sizeInPoints); + void setBold(bool); + void setItalic(bool); + void setUnderline(bool); + void startParagraph(); + bool isParagraphOpen() const; + void clearParagraphFormatting(); + void setParagraphDirLTR(); + void setParagraphDirRTL(); + void addLineBreak(); + void flush(); + void reset(); + void resetTag(TagEnum tag); +protected: + string text; + void Init(); + RTF2HTML *p; + void resetColors() { m_nRed = m_nGreen = m_nBlue = 0; m_bColorInit = false; } + void setColor(unsigned char val, unsigned char *p) + { *p = val; m_bColorInit=true; } + + // Marks the position in m_tags where this level begun. + unsigned m_nTagsStartPos; + + // True when parsing the fonts table + bool m_bFontTbl; + // True when parsing the colors table. + bool m_bColors; + // True when inside a 'fname' block. + bool m_bFontName; + // False until we get the tagged font name. + bool m_bTaggedFontNameOk; + + unsigned char m_nRed; + unsigned char m_nGreen; + unsigned char m_nBlue; + bool m_bColorInit; + unsigned m_nFont; // 1-based + unsigned m_nEncoding; + unsigned m_nFontColor; // 1-based + unsigned m_nFontSize; + unsigned m_nFontBgColor; // 1-based + bool m_bBold; + bool m_bItalic; + bool m_bUnderline; +}; + +class OutTag +{ +public: + OutTag(TagEnum _tag, unsigned _param) : tag(_tag), param(_param) {} + TagEnum tag; + unsigned param; +}; + +enum quoteMode +{ + quoteHTML, + quoteXML, + quoteNOBR +}; + +class RTF2HTML +{ + friend class Level; + +public: + RTF2HTML(); + QString Parse(const char *rtf, const char *encoding); + + // Paragraph-specific functions: + + QString quoteString(const QString &_str, quoteMode mode = quoteHTML); + // Appends a string with formatting into the paragraph buffer. + void PrintUnquoted(const char *str, ...); + // Quotes and appends a string to the paragraph buffer. + void PrintQuoted(const QString &str); + // Writes down the tags from oTags into the paragraph buffer. + void FlushOutTags(); + // Retrieves the top not-yet-written tag. + OutTag* getTopOutTag(TagEnum tagType); + // Writes down the paragraph buffer and resets the paragraph state. + void FlushParagraph(); + + // Document-wide functions: + + void PutTag(TagEnum n) + { + tags.push(n); + } + +protected: + +// Paragraph members + + // True if the paragraph was opened explicitly. + bool bExplicitParagraph; + // The paragraph's HTML buffer. + QString sParagraph; + // Defines the paragraph's formatting. + ParStyle parStyle; + // Tags which weren't yet printed out. + vector<OutTag> oTags; + +// Document members + + // The document HTML buffer. + QString s; + // Fonts table. + vector<FontDef> fonts; + // Colors table. + vector<QColor> colors; + // Stack of tags (across all levels, not just current level) + stack<TagEnum> tags; + +// RTF parser internals + + const char *rtf_ptr; + const char *encoding; + Level cur_level; + stack<Level> levels; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/safedelete.cpp b/kopete/protocols/oscar/liboscar/safedelete.cpp new file mode 100644 index 00000000..703e8ed3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/safedelete.cpp @@ -0,0 +1,139 @@ +/* + safedelete.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "safedelete.h" + +#include <qtimer.h> + +//---------------------------------------------------------------------------- +// SafeDelete +//---------------------------------------------------------------------------- +SafeDelete::SafeDelete() +{ + lock = 0; +} + +SafeDelete::~SafeDelete() +{ + if(lock) + lock->dying(); +} + +void SafeDelete::deleteLater(QObject *o) +{ + if(!lock) + deleteSingle(o); + else + list.append(o); +} + +void SafeDelete::unlock() +{ + lock = 0; + deleteAll(); +} + +void SafeDelete::deleteAll() +{ + if(list.isEmpty()) + return; + + QObjectListIt it(list); + for(QObject *o; (o = it.current()); ++it) + deleteSingle(o); + list.clear(); +} + +void SafeDelete::deleteSingle(QObject *o) +{ +#if QT_VERSION < 0x030000 + // roll our own QObject::deleteLater() + SafeDeleteLater *sdl = SafeDeleteLater::ensureExists(); + sdl->deleteItLater(o); +#else + o->deleteLater(); +#endif +} + +//---------------------------------------------------------------------------- +// SafeDeleteLock +//---------------------------------------------------------------------------- +SafeDeleteLock::SafeDeleteLock(SafeDelete *sd) +{ + own = false; + if(!sd->lock) { + _sd = sd; + _sd->lock = this; + } + else + _sd = 0; +} + +SafeDeleteLock::~SafeDeleteLock() +{ + if(_sd) { + _sd->unlock(); + if(own) + delete _sd; + } +} + +void SafeDeleteLock::dying() +{ + _sd = new SafeDelete(*_sd); + own = true; +} + +//---------------------------------------------------------------------------- +// SafeDeleteLater +//---------------------------------------------------------------------------- +SafeDeleteLater *SafeDeleteLater::self = 0; + +SafeDeleteLater *SafeDeleteLater::ensureExists() +{ + if(!self) + new SafeDeleteLater(); + return self; +} + +SafeDeleteLater::SafeDeleteLater() +{ + list.setAutoDelete(true); + self = this; + QTimer::singleShot(0, this, SLOT(explode())); +} + +SafeDeleteLater::~SafeDeleteLater() +{ + list.clear(); + self = 0; +} + +void SafeDeleteLater::deleteItLater(QObject *o) +{ + list.append(o); +} + +void SafeDeleteLater::explode() +{ + delete this; +} + +#include "safedelete.moc" + diff --git a/kopete/protocols/oscar/liboscar/safedelete.h b/kopete/protocols/oscar/liboscar/safedelete.h new file mode 100644 index 00000000..e8215c06 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/safedelete.h @@ -0,0 +1,79 @@ +/* + gwclientstream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SAFEDELETE_H +#define SAFEDELETE_H + +#include<qobject.h> +#include<qobjectlist.h> + +class SafeDelete; +class SafeDeleteLock +{ +public: + SafeDeleteLock(SafeDelete *sd); + ~SafeDeleteLock(); + +private: + SafeDelete *_sd; + bool own; + friend class SafeDelete; + void dying(); +}; + +class SafeDelete +{ +public: + SafeDelete(); + ~SafeDelete(); + + void deleteLater(QObject *o); + + // same as QObject::deleteLater() + static void deleteSingle(QObject *o); + +private: + QObjectList list; + void deleteAll(); + + friend class SafeDeleteLock; + SafeDeleteLock *lock; + void unlock(); +}; + +class SafeDeleteLater : public QObject +{ + Q_OBJECT +public: + static SafeDeleteLater *ensureExists(); + void deleteItLater(QObject *o); + +private slots: + void explode(); + +private: + SafeDeleteLater(); + ~SafeDeleteLater(); + + QObjectList list; + friend class SafeDelete; + static SafeDeleteLater *self; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.cpp b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp new file mode 100644 index 00000000..af80e191 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp @@ -0,0 +1,107 @@ +/* + Kopete Oscar Protocol + senddcinfotask.cpp - Send the DC info to the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "senddcinfotask.h" + +#include <kdebug.h> +#include "connection.h" +#include "buffer.h" +#include "oscarsettings.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + +SendDCInfoTask::SendDCInfoTask(Task* parent, DWORD status): Task(parent), mStatus(status) +{ +} + + +SendDCInfoTask::~SendDCInfoTask() +{ +} + + +void SendDCInfoTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x001E, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending DC Info" << endl; + + /** \TODO Support something more than online in the status flags + * \TODO Support something more than DC Disabled in the status flags + */ + /* + if (status & ICQ_STATUS_SET_INVIS) + sendChangeVisibility(0x03); + else + sendChangeVisibility(0x04); + */ + + /* This is TLV 0x06 */ + buffer->addWord( 0x0006 ); + buffer->addWord( 0x0004 ); + //### Don't hardcode this value + //Right now, it's always coded to not support DC + DWORD statusFlag = 0x01000000; + if ( client()->settings()->webAware() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting web aware on" << endl; + statusFlag |= 0x00010000; + } + if ( client()->settings()->hideIP() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting hide ip on" << endl; + statusFlag |= 0x10000000; // Direct connection upon authorization, hides IP + } + + buffer->addDWord( statusFlag | mStatus ); + + /* Fill in the DC Info + * We don't support Direct Connection yet. So fill in some + * dummy values + */ + buffer->addWord( 0x000C ); //TLV Type 0x0C + buffer->addWord( 0x0025 ); + + buffer->addDWord( 0x00000000 ); + buffer->addWord( 0x0000 ); + buffer->addWord( 0x0000 ); + + buffer->addByte( 0x00 ); // Mode, TODO: currently fixed to "Direct Connection disabled" + buffer->addWord( ICQ_TCP_VERSION ); // icq tcp protocol version, v8 currently + + buffer->addDWord( 0x00000000 ); // Direct Connection Cookie + buffer->addDWord( 0x00000050 ); // web front port + buffer->addDWord( 0x00000003 ); // number of following client features + buffer->addDWord( 0x00000000 ); // InfoUpdateTime + buffer->addDWord( 0x00000000 ); // PhoneStatusTime + buffer->addDWord( 0x00000000 ); // PhoneBookTime + buffer->addWord( 0x0000 ); + + buffer->addWord( 0x0008 ); // TLV(8) + buffer->addWord( 0x0002 ); // length 2 + buffer->addWord( 0x0000 ); // error code - 0 + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); +} + + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.h b/kopete/protocols/oscar/liboscar/senddcinfotask.h new file mode 100644 index 00000000..d130cc40 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/senddcinfotask.h @@ -0,0 +1,41 @@ +/* + Kopete Oscar Protocol + $FILENAME.h + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SENDDCINFOTASK_H +#define SENDDCINFOTASK_H + +#include <task.h> + +/** +Fire and forget task that sends our direct connection info + +@author Matt Rogers +*/ +class SendDCInfoTask : public Task +{ +public: + SendDCInfoTask( Task* parent, DWORD status ); + ~SendDCInfoTask(); + virtual void onGo(); + +private: + DWORD mStatus; +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.cpp b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp new file mode 100644 index 00000000..f0601e22 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp @@ -0,0 +1,57 @@ +/* + Kopete Oscar Protocol + sendidletimetask.cpp - Sends the idle time to the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "sendidletimetask.h" + +#include <kdebug.h> +#include "connection.h" +#include "buffer.h" +#include "oscarutils.h" +#include "transfer.h" + +SendIdleTimeTask::SendIdleTimeTask( Task* parent ) : Task( parent ) +{ + m_idleTime = 0; +} + + +SendIdleTimeTask::~SendIdleTimeTask() +{ + +} + + +void SendIdleTimeTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending idle time of " << m_idleTime << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0011, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + buffer->addDWord( m_idleTime ); + + Transfer *t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); + +} + +void SendIdleTimeTask::setIdleTime( DWORD newTime ) +{ + m_idleTime = newTime; +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.h b/kopete/protocols/oscar/liboscar/sendidletimetask.h new file mode 100644 index 00000000..beba74c2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendidletimetask.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + sendidletimetask.h - Send the idle time to the server + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SENDIDLETIMETASK_H +#define SENDIDLETIMETASK_H + +#include "task.h" +#include "oscartypes.h" + +/** +Sends the idle time to the server + +@author Matt Rogers +*/ +class SendIdleTimeTask : public Task +{ +public: + SendIdleTimeTask( Task* parent ); + ~SendIdleTimeTask(); + virtual void onGo(); + + //! Set the idle time to send + void setIdleTime( DWORD ); + +private: + + DWORD m_idleTime; +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.cpp b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp new file mode 100644 index 00000000..48509595 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp @@ -0,0 +1,447 @@ +/* + sendmessagetask.h - Outgoing OSCAR Messaging Handler + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "sendmessagetask.h" + +#include <kapplication.h> +#include <kdebug.h> +#include "connection.h" +#include "oscartypes.h" +#include "transfer.h" + + +SendMessageTask::SendMessageTask(Task* parent): Task(parent) +{ + m_autoResponse = false; + m_cookieCount = 0x7FFF; +} + + +SendMessageTask::~SendMessageTask() +{ +} + +void SendMessageTask::setMessage( const Oscar::Message& msg ) +{ + m_message = msg; +} + +void SendMessageTask::setAutoResponse( bool autoResponse ) +{ + m_autoResponse = autoResponse; +} + +void SendMessageTask::onGo() +{ + if ( m_message.textArray().isEmpty() && m_message.type() == 1 ) // at least channel 2 needs to send empty messages + { + setError(-1, "No message to send"); + return; + } + + // Check Message to see what SNAC to use + int snacSubfamily = 0x0006; + if ( ( m_message.type() == 2 ) && m_message.hasProperty( Oscar::Message::AutoResponse ) ) + { // an auto response is send for ack of channel 2 messages + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending SNAC 0x0B instead of 0x06 " << endl; + snacSubfamily = 0x000B; + } + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, snacSubfamily, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + if ( snacSubfamily == 0x0006 ) + { + DWORD cookie1 = KApplication::random(); + DWORD cookie2 = KApplication::random(); + + b->addDWord( cookie1 ); + b->addDWord( cookie2 ); + } + else + { + b->addString( m_message.icbmCookie() ); // in automated response, we need the same cookie as in the request + } + + b->addWord( m_message.type() ); + + b->addByte( m_message.receiver().length() ); + b->addString( m_message.receiver().latin1(), m_message.receiver().length() ); + + + if ( snacSubfamily == 0x0006 ) + { + /* send a regular message */ + switch ( m_message.type() ) + { + case 1: + addChannel1Data( b ); + break; + case 2: + addChannel2Data( b ); + break; + case 4: + addChannel4Data( b ); + break; + } + + // Add the TLV to indicate if this is an autoresponse: 0x00040000 + // Right now, only supported for the AIM client, I'm not sure about ICQ + // For some reason you can't have both a 0x0004 and 0x0003 TLV in the same + // SNAC, if you do the AIM server complains + if ( !client()->isIcq() && (m_autoResponse == true) ) + { + TLV tlv4( 0x0004, 0, NULL); + b->addTLV( tlv4 ); + } + else + { + b->addDWord( 0x00030000 ); //empty TLV 3 to get an ack from the server + } + + if ( client()->isIcq() && m_message.type() != 2 && ! m_message.hasProperty( Oscar::Message::StatusMessageRequest ) ) + b->addDWord( 0x00060000 ); //empty TLV 6 to store message on the server if not online + } + else + { + /* send an autoresponse */ + b->addWord( 0x0003 ); // reason code: 1: channel not supported; 2: busted payload; 3: channel specific; + //TODO: i hardcoded it for now, since we don't suppoert error messages atm anyway + addRendezvousMessageData( b ); + } + + Transfer* t = createTransfer( f, s, b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SENDING: " << t->toString() << endl; + send( t ); + + setSuccess(true); +} + + +void SendMessageTask::addBasicTLVs( Buffer* b ) +{ + //TODO add stuff like user class, user status, online time, etc TLVS +} + + +void SendMessageTask::addChannel1Data( Buffer* b ) +{ + Buffer tlv2buffer; + + //Send features TLV using data from gaim. Features are different + //depending on whether we're ICQ or AIM + if ( client()->isIcq() ) + { + tlv2buffer.addDWord( 0x05010002 ); //TLV 0x0501, length 2 + tlv2buffer.addWord( 0x0106 ); //TLV 0x0501 data + } + else + { + tlv2buffer.addDWord( 0x05010004 ); //TLV 0x0501, length 4 + tlv2buffer.addDWord( 0x01010102 ); //TLV 0x0501 data. + } + //we only send one message part. There's only one client that actually uses + //them and it's quite old and infrequently used + tlv2buffer.addWord( 0x0101 ); //add TLV(0x0101) also known as TLV(257) + tlv2buffer.addWord( m_message.textArray().size() + 4 ); // add TLV length + + if ( m_message.encoding() == Oscar::Message::UserDefined ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message in " + << "per-contact encoding" << endl; + tlv2buffer.addWord( 0x0000 ); + tlv2buffer.addWord( 0x0000 ); + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message as " + << "UCS-2" << endl; + tlv2buffer.addWord( 0x0002 ); + tlv2buffer.addWord( 0x0000 ); + } + tlv2buffer.addString( m_message.textArray() ); + + TLV tlv2( 0x0002, tlv2buffer.length(), tlv2buffer.buffer() ); + b->addTLV( tlv2 ); +} + +void SendMessageTask::addChannel2Data( Buffer* b ) +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Trying to send type 2 message!" << endl; + + Buffer tlv5buffer; + + tlv5buffer.addWord( 0 ); // 0 = request; other possibilities: 1 = cancel; 2 = accept; + //TODO: i hardcoded it for now, don't yet what to use the other stuff for + + // message id cookie. needs to be the same one as above, thus copy first eight bytes of buffer + Buffer* tmp = new Buffer(b->buffer(), 8); + tlv5buffer.addString( tmp->buffer(), 8 ); + delete tmp; + + /* send our client capability. oscardocs say this one means we support type 2 messages, + ethereal say it means we support server relay. however, it's what most clients send, + even official ones... + */ + + // too lazy to think about byte order :) + tlv5buffer.addByte( 0x09 ); + tlv5buffer.addByte( 0x46 ); + tlv5buffer.addByte( 0x13 ); + tlv5buffer.addByte( 0x49 ); + tlv5buffer.addByte( 0x4C ); + tlv5buffer.addByte( 0x7F ); + tlv5buffer.addByte( 0x11 ); + tlv5buffer.addByte( 0xD1 ); + tlv5buffer.addByte( 0x82 ); + tlv5buffer.addByte( 0x22 ); + tlv5buffer.addByte( 0x44 ); + tlv5buffer.addByte( 0x45 ); + tlv5buffer.addByte( 0x53 ); + tlv5buffer.addByte( 0x54 ); + tlv5buffer.addByte( 0x00 ); + tlv5buffer.addByte( 0x00 ); + + // These are optional, would probably be a god ide to start using them, though + + // add TLV 03: internal ip +// tlv5buffer.addWord( 0x0003 ); // TLV Type +// tlv5buffer.addWord( 0x0004 ); // TLV Length +// tlv5buffer.addDWord( 0x00000000 ); // TLV Data: Internal IP + + // add TLV 05: listening port +// tlv5buffer.addWord( 0x0005 ); // TLV Type +// tlv5buffer.addWord( 0x0002 ); // TLV Length +// tlv5buffer.addWord( 0x0000 ); // TLV Data: listening port + + // add TLV 0A: acktype (1 = normal message) + tlv5buffer.addWord( 0x000A ); // TLV Type + tlv5buffer.addWord( 0x0002 ); // TLV Length + tlv5buffer.addWord( 0x0001 ); // TLV Data: unknown, usually 1 + + // add TLV 0B: unknown +// tlv5buffer.addWord( 0x000B ); // TLV Type +// tlv5buffer.addWord( 0x0002 ); // TLV Length +// tlv5buffer.addWord( 0x0000 ); // TLV Data: unknown + + // add TLV 0F: unknown + tlv5buffer.addWord( 0x000F ); // TLV Type + tlv5buffer.addWord( 0x0000 ); // TLV Length + // TLV Data: empty + + + + /* now comes the important TLV 0x2711 */ + + Buffer tlv2711buffer; + addRendezvousMessageData( &tlv2711buffer ); + TLV tlv2711( 0x2711, tlv2711buffer.length(), tlv2711buffer.buffer() ); + tlv5buffer.addTLV( tlv2711 ); + + TLV tlv5( 0x0005, tlv5buffer.length(), tlv5buffer.buffer() ); + b->addTLV( tlv5 ); +} + +void SendMessageTask::addChannel4Data( Buffer* b ) +{ + //TODO +} + +void SendMessageTask::addRendezvousMessageData( Buffer* b ) +{ + // first data segment + b->addLEWord( 0x001B ); // length of this data segment, always 27 + + // protocol version + // miranda,licq use 8, gaim,icq5 use 9, icq2003b uses 10. + // 9 seems to make things a litle difficult, 10 seems a little more like 8, but still more difficult + b->addLEWord( 0x0008 ); // so stick with 8 for now :) + + for ( int i = 0; i < 16; i++) + { + b->addByte( 0x00 ); // pluginID or all zeros (see oscar docs) + } + + b->addWord( 0x0000 ); // unknown + b->addLEDWord( 0x00000003 ); // FIXME client capabilities: not sure, but should be ICQ Server Relay + b->addByte( 0x0000 ); // unknown + + // channel 2 counter: in auto response, use original message value. s/t else otherwise (most anythig will work) + int channel2Counter = 0xBEEF; // just some number for now + if ( m_message.hasProperty( Oscar::Message::AutoResponse ) ) + channel2Counter = m_message.channel2Counter(); + else + channel2Counter = (m_cookieCount--) & 0x7FFF; + + b->addLEWord( channel2Counter ); // channel 2 counter + + // second data segment + b->addLEWord( 0x000E ); // length of this data segment, always 14 + b->addLEWord( channel2Counter ); // channel 2 counter + + for ( int i = 0; i < 12; i++) + { + b->addByte( 0x00 ); // unknown, usually all zeros + } + + // actual message data segment + + // Message type + if ( m_message.messageType() == 0x00 ) + b->addByte( 0x01 ); + else + b->addByte( m_message.messageType() ); + + int messageFlags = 0x00; // Normal + if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) ) + messageFlags = 0x03; // Auto message. required for both requesting and sending status messages + else if ( m_message.hasProperty( Oscar::Message::AutoResponse ) ) + messageFlags = 0x00; // A regular type 2 msg ack requires 0x00 here... + b->addByte( messageFlags ); + + // status code, priority: + // common (ICQ) practice seems to be: both 1 when requesting away message, both 0 otherwise + // miranda sends 256/0 in away message request. it works, but i don't see the point... + // other then that, don't yet really know what they are for. + if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) && ( ! m_message.hasProperty( Oscar::Message::AutoResponse ) ) ) + { + b->addLEWord( 0x0001 ); // status (?) + b->addLEWord( 0x0001 ); // priority (?) + } + else + { + b->addLEWord( 0x0000 ); // status (?) + b->addLEWord( 0x0000 ); // priority (?) + } + + + b->addLEWord( m_message.textArray().size() + 1 ); // length of string + zero termination + b->addString( m_message.textArray() ); // string itself + b->addByte( 0x00 ); // zero termination + b->addLEDWord( 0x00000000 ); // foreground + b->addLEDWord( 0x00FFFFFF ); // foreground + + if ( m_message.encoding() == Oscar::Message::UTF8 ) + { + b->addLEDWord( 38 ); + b->addString( "{0946134E-4C7F-11D1-8222-444553540000}", 38 ); + } +} + + + +/* Old oscarsocket code, which is here for reference in case this doesn't work +QTextCodec *codec = 0L; +WORD charset = 0x0000; // default to ascii +WORD charsubset = 0x0000; +int length = message.length(); +unsigned char *utfMessage = 0L; + +codec=QTextCodec::codecForMib(3); // US-ASCII + +if(codec) +{ + if(codec->canEncode(message)) // this returns true for some accented western european chars but kopete can't decode on receipt + { + //kdDebug(14151) << k_funcinfo << "Going to encode as US-ASCII" << endl; + // We are forcing kopete to send messages using ISO-8859-1 + // It's a hack and should be reimplemented in a better way + charset=0x0003; + codec=QTextCodec::codecForMib(4); + //kdDebug(14151) << k_funcinfo << "Now trying ISO-8859-1" << endl; + } + else + { + codec=0L; // we failed encoding it as US-ASCII + //kdDebug(14151) << k_funcinfo << "Cannot encode as US-ASCII" << endl; + } +} + +// if we couldn't encode it as ascii, and either the client says it can do UTF8, or we have no +// contact specific encoding set, might as well send it as UTF-16BE as as ISO-8859-1 +if ( !codec && ( contact->hasCap(CAP_UTF8) || !contact->encoding() ) ) +{ + // use UTF is peer supports it and encoding as US-ASCII failed + length=message.length()*2; + utfMessage=new unsigned char[length]; + for(unsigned int l=0; l<message.length(); l++) + { + utfMessage[l*2]=message.unicode()[l].row(); + utfMessage[(l*2)+1]=message.unicode()[l].cell(); + } + charset=0x0002; // send UTF-16BE +} + +// no codec and no charset and per-contact encoding set +if(!codec && charset != 0x0002 && contact->encoding() != 0) +{ + codec=QTextCodec::codecForMib(contact->encoding()); + if(codec) + charset=0x0003; //send as ISO-8859-1 +} + +if(!codec && charset != 0x0002) // it's neither unicode nor did we find a codec so far! +{ + kdDebug(14151) << k_funcinfo << + "Couldn't find suitable encoding for outgoing message, " << + "encoding using ISO-8859-1, prepare for receiver getting unreadable text :)" << endl; + charset=0x0003; + codec=QTextCodec::codecForMib(4); // ISO-8859-1 +} + +tlv2.addWord(0x0101); //add TLV(0x0101) also known as TLV(257) +tlv2.addWord(length + 0x04); // add TLV length +tlv2.addWord(charset); // normal char set +tlv2.addWord(charsubset); // normal char set + +if(utfMessage) +{ + kdDebug(14151) << k_funcinfo << "Outgoing message encoded as 'UTF-16BE'" << endl; + tlv2.addString(utfMessage, length); // the actual message + delete [] utfMessage; +} +else +{ + kdDebug(14151) << k_funcinfo << "Outgoing message encoded as '" << codec->name() << "'" << endl; + QCString outgoingMessage=codec->fromUnicode(message); + tlv2.addString(outgoingMessage, length); // the actual message +} +// ==================================================================================== + +outbuf.addTLV(0x0002, tlv2.length(), tlv2.buffer()); + +if(isAuto) // No clue about this stuff, probably AIM-specific [mETz] +{ + outbuf.addWord(0x0004); + outbuf.addWord(0x0000); +} + +if(mIsICQ) +{ + //outbuf.addWord(0x0003); // TLV.Type(0x03) - request an ack from server + //outbuf.addWord(0x0000); + + outbuf.addWord(0x0006); // TLV.Type(0x06) - store message if recipient offline + outbuf.addWord(0x0000); +} + +sendBuf(outbuf,0x02); +*/ + + + + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.h b/kopete/protocols/oscar/liboscar/sendmessagetask.h new file mode 100644 index 00000000..0eaff13f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/sendmessagetask.h @@ -0,0 +1,55 @@ +/* + sendmessagetask.h - Outgoing OSCAR Messaging Handler + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef SENDMESSAGETASK_H +#define SENDMESSAGETASK_H + +#include "task.h" +#include <qstring.h> +#include "oscarmessage.h" +/** +@author Kopete Developers +*/ +class SendMessageTask : public Task +{ +public: + SendMessageTask( Task* parent ); + ~SendMessageTask(); + + //! Set the message to be sent + void setMessage( const Oscar::Message& msg ); + + //! Are we sending an auto response + void setAutoResponse( bool autoResponse ); + + virtual void onGo(); + +private: + void addBasicTLVs( Buffer* b ); + void addChannel1Data( Buffer* b ); + void addChannel2Data( Buffer* b ); + void addChannel4Data( Buffer* b ); + void addRendezvousMessageData( Buffer* b ); + +private: + Oscar::Message m_message; + bool m_autoResponse; + uint m_cookieCount; +}; + +#endif + +//kate: indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.cpp b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp new file mode 100644 index 00000000..cccad909 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp @@ -0,0 +1,173 @@ +// Kopete Oscar Protocol - Server redirections + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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 + +#include "serverredirecttask.h" + +#include <kdebug.h> + +#include "buffer.h" +#include "connection.h" +#include "transfer.h" + +ServerRedirectTask::ServerRedirectTask( Task* parent ) + :Task( parent ), m_service( 0 ) +{ + +} + +void ServerRedirectTask::setService( WORD family ) +{ + m_service = family; +} + +void ServerRedirectTask::setChatParams( WORD exchange, QByteArray cookie, WORD instance ) +{ + m_chatExchange = exchange; + m_chatCookie.duplicate( cookie ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is" << m_chatCookie << endl; + m_chatInstance = instance; +} + +void ServerRedirectTask::setChatRoom( const QString& roomName ) +{ + m_chatRoom = roomName; +} + + +void ServerRedirectTask::onGo() +{ + if ( m_service != 0 ) + requestNewService(); +} + +bool ServerRedirectTask::forMe( const Transfer* transfer ) +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 1 && st->snacSubtype() == 0x0005 ) + return true; + else + return false; +} + +bool ServerRedirectTask::take( Transfer* transfer ) +{ + if ( !forMe( transfer ) ) + return false; + + setTransfer( transfer ); + bool value = handleRedirect(); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return value; +} + + +void ServerRedirectTask::requestNewService() +{ + FLAP f = { 0x02, 0, 0x00 }; + SNAC s = { 0x0001, 0x0004, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + b->addWord( m_service ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting server for service " << m_service << endl; + if ( m_service == 0x000E ) + { + b->addWord( 0x0001 ); + b->addWord( m_chatCookie.size() + 5 ); + b->addWord( m_chatExchange ); + b->addByte( m_chatCookie.size() ); + b->addString( m_chatCookie ); + b->addWord( m_chatInstance ); + } + + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +bool ServerRedirectTask::handleRedirect() +{ + //TLVs 0x0D, 0x05, 0x06 + //family id + //server + //auth cookie + Buffer* b = transfer()->buffer(); + WORD typeD = b->getWord(); + WORD typeDLen = b->getWord(); + if ( typeD == 0x000D && typeDLen == 0x0002) + { + WORD realService = b->getWord(); + if ( realService != m_service ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "wrong service for this task" << endl; + kdDebug(OSCAR_RAW_DEBUG ) << k_funcinfo << "should be " << m_service << " is " + << realService << endl; + return false; + } + } + else + return false; + + TLV server = b->getTLV(); + m_newHost = QString( server.data ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Host for service " << m_service + << " is " << m_newHost << endl; + if ( m_newHost.isEmpty() ) + return false; + + TLV cookie = b->getTLV(); + + if ( cookie.length == 0 || cookie.data.isEmpty() ) + return false; + else + m_cookie = cookie.data; + + emit haveServer( m_newHost, m_cookie, m_service ); + return true; +} + +QByteArray ServerRedirectTask::cookie() const +{ + return m_cookie; +} + +QString ServerRedirectTask::newHost() const +{ + return m_newHost; +} + +WORD ServerRedirectTask::service() const +{ + return m_service; +} + +WORD ServerRedirectTask::chatExchange() const +{ + return m_chatExchange; +} + +QString ServerRedirectTask::chatRoomName() const +{ + return m_chatRoom; +} + +#include "serverredirecttask.moc" +//kate: indent-mode csands; tab-width 4; + diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.h b/kopete/protocols/oscar/liboscar/serverredirecttask.h new file mode 100644 index 00000000..19f14073 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverredirecttask.h @@ -0,0 +1,72 @@ +// Kopete Oscar Protocol - Server redirections + +// Copyright (C) 2005 Matt Rogers <mattr@kde.org> + +// 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 + + +#ifndef SERVERREDIRECTTASK_H +#define SERVERREDIRECTTASK_H + +#include "task.h" + +#include <qcstring.h> + +#include "oscartypes.h" + +class Transfer; + +class ServerRedirectTask : public Task +{ +Q_OBJECT +public: + ServerRedirectTask( Task* parent ); + + void setService( WORD family ); + void setChatParams( WORD exchange, QByteArray cookie, WORD instance ); + void setChatRoom( const QString& roomName ); + + WORD chatExchange() const; + QString chatRoomName() const; + + //Task implementation + void onGo(); + bool forMe( const Transfer* transfer ); + bool take( Transfer* transfer ); + + void requestNewService(); + bool handleRedirect(); + + QByteArray cookie() const; + QString newHost() const; + WORD service() const; + +signals: + void haveServer( const QString&, const QByteArray&, WORD ); + +private: + WORD m_service; + QString m_newHost; + QByteArray m_cookie; + + WORD m_chatExchange; + QByteArray m_chatCookie; + WORD m_chatInstance; + QString m_chatRoom; +}; + + +#endif diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.cpp b/kopete/protocols/oscar/liboscar/serverversionstask.cpp new file mode 100644 index 00000000..e4186f18 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverversionstask.cpp @@ -0,0 +1,169 @@ +/* + Kopete Oscar Protocol + serverversionstask.cpp - Handles the snac family versions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "serverversionstask.h" + +#include <kdebug.h> + +#include "connection.h" +#include "buffer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + + +using namespace Oscar; + +ServerVersionsTask::ServerVersionsTask( Task* parent ) + : Task( parent ) +{ + m_family = 0; +} + + +ServerVersionsTask::~ServerVersionsTask() +{ +} + + +bool ServerVersionsTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( transfer ); + + if (!st) + return false; + + if ( st->snacService() == 1 ) + { + switch ( st->snacSubtype() ) + { + case 0x03: + case 0x17: + case 0x18: + return true; + break; + default: + return false; + } + } + return false; +} + +bool ServerVersionsTask::take( Transfer* transfer ) +{ + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer ); + if (!st) + return false; + + if ( forMe( transfer ) ) + { + switch ( st->snacSubtype() ) + { + case 0x03: + setTransfer( transfer ); + handleFamilies(); + setTransfer( 0 ); + return true; + break; + case 0x18: + setTransfer( transfer ); + handleServerVersions(); + setTransfer( 0 ); + return true; + break; + default: + return false; + } + } + return false; +} + +void ServerVersionsTask::handleFamilies() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo + << "RECV SNAC 0x01, 0x03 - got the list of families server supports" << endl; + + Buffer* outbuf = transfer()->buffer(); + if ( outbuf->length() % 2 != 0 ) + { + setError( -1, QString::null ); + return; + } + + while ( outbuf->length () != 0 ) + { + m_familiesList.append( outbuf->getWord() ); + } + client()->addToSupportedFamilies( m_familiesList ); + requestFamilyVersions(); // send back a CLI_FAMILIES packet +} + +void ServerVersionsTask::requestFamilyVersions() +{ + bool isIcq = client()->isIcq(); + int listLength = m_familiesList.count(); + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0001, 0x0017, 0x0000, client()->snacSequence() }; + WORD val; + Buffer* outbuf = new Buffer(); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND SNAC 0x01, 0x17 - Snac family versions we want" << endl; + + for ( int i = 0; i < listLength; i++ ) + { + outbuf->addWord( m_familiesList[i] ); + if ( m_familiesList[i] == 0x0001 ) + val = 0x0003; + else + { + if ( m_familiesList[i] == 0x0013 ) + { + if ( isIcq ) + val = 0x0004; // for ICQ2002 + else + val = 0x0003; + } + else + val = 0x0001; + } + + outbuf->addWord(val); + } + + Transfer* st = createTransfer( f, s, outbuf ); + st->toString(); + send( st ); +} + +void ServerVersionsTask::handleServerVersions() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "RECV SNAC 0x01, 0x18, got list of families this server understands" << endl; + + Buffer* buffer = transfer()->buffer(); + int numFamilies = m_familiesList.count(); + for ( int srvFamCount = 0; srvFamCount < numFamilies; srvFamCount++ ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server version=" << buffer->getWord() + << ", server family=" << buffer->getWord() << endl; + } + setSuccess( 0, QString::null ); +} + +#include "serverversionstask.moc" diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.h b/kopete/protocols/oscar/liboscar/serverversionstask.h new file mode 100644 index 00000000..a9c56f35 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/serverversionstask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + serverversionstask.h - Handles the snac family versions + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SERVERVERSIONSTASK_H +#define SERVERVERSIONSTASK_H + +#include "task.h" +#include <qvaluelist.h> +#include "oscartypes.h" + +class Transfer; + +/** +@author Matt Rogers +*/ +class ServerVersionsTask : public Task +{ +Q_OBJECT +public: + ServerVersionsTask( Task* parent ); + + ~ServerVersionsTask(); + + bool forMe(const Transfer* transfer) const; + bool take(Transfer* transfer); + + +private: + //! Handles the families the server supports + void handleFamilies(); + + //! Handles the version of each family the server supports + void handleServerVersions(); + + //! Request the versions we want for each snac family the + //! the server supports + void requestFamilyVersions(); + +private: + QValueList<int> m_familiesList; + WORD m_family; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.cpp b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp new file mode 100644 index 00000000..13e30101 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp @@ -0,0 +1,135 @@ +/* + Kopete Oscar Protocol + servicesetuptask.cpp - Set up the services for the BOS connection + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "servicesetuptask.h" + +#include <kdebug.h> +#include "blmlimitstask.h" +#include "connection.h" +#include "clientreadytask.h" +#include "icbmparamstask.h" +#include "locationrightstask.h" +#include "ownuserinfotask.h" +#include "prmparamstask.h" +#include "profiletask.h" +#include "senddcinfotask.h" +#include "sendidletimetask.h" +#include "ssiactivatetask.h" +#include "ssilisttask.h" +#include "ssimanager.h" +#include "ssiparamstask.h" +#include "transfer.h" + +ServiceSetupTask::ServiceSetupTask( Task* parent ) + : Task( parent ) +{ + m_finishedTaskCount = 0; + m_locRightsTask = new LocationRightsTask( parent ); + m_profileTask = new ProfileTask( parent ); + m_blmLimitsTask = new BLMLimitsTask( parent ); + m_icbmTask = new ICBMParamsTask( parent ); + m_prmTask = new PRMParamsTask( parent ); + m_ssiParamTask = new SSIParamsTask( parent ); + m_ssiListTask = new SSIListTask( parent ); + m_ssiActivateTask = new SSIActivateTask( parent ); + + QObject::connect( m_ssiListTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_ssiParamTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_prmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_icbmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_blmLimitsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_profileTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_locRightsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); + QObject::connect( m_ssiActivateTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) ); +} + + +ServiceSetupTask::~ServiceSetupTask() +{ + delete m_locRightsTask; + delete m_profileTask; + delete m_blmLimitsTask; + delete m_icbmTask; + //delete m_prmTask; + //delete m_ssiParamTask; + delete m_ssiListTask; +} + + +bool ServiceSetupTask::forMe( const Transfer* transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +bool ServiceSetupTask::take( Transfer* transfer ) +{ + Q_UNUSED( transfer ); + return false; +} + +void ServiceSetupTask::childTaskFinished() +{ + m_finishedTaskCount++; + +// kdDebug( OSCAR_RAW_DEBUG ) << "Finished count is " << m_finishedTaskCount << endl; + + if ( m_finishedTaskCount == 7 ) + { + if ( client()->ssiManager()->listComplete() ) + m_ssiActivateTask->go( true ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending DC info and client ready" << endl; + SendIdleTimeTask* sitt = new SendIdleTimeTask( client()->rootTask() ); + QValueList<int> familyList; + familyList.append( 0x0001 ); + familyList.append( 0x0002 ); + familyList.append( 0x0003 ); + familyList.append( 0x0004 ); + familyList.append( 0x0006 ); + familyList.append( 0x0008 ); + familyList.append( 0x0009 ); + familyList.append( 0x000A ); + familyList.append( 0x0013 ); + ClientReadyTask* crt = new ClientReadyTask( client()->rootTask() ); + crt->setFamilies( familyList ); + sitt->go( true ); + crt->go( true ); //autodelete + } + + if ( m_finishedTaskCount == 8 ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Service setup finished" << endl; + setSuccess( 0, QString::null ); + } +} + + +void ServiceSetupTask::onGo() +{ + m_locRightsTask->go(); + m_profileTask->go(); + m_blmLimitsTask->go(); + m_icbmTask->go(); + m_prmTask->go( true ); + m_ssiParamTask->go( true ); + m_ssiListTask->go(); +} + +//kate: tab-width 4; indent-mode csands; + +#include "servicesetuptask.moc" diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.h b/kopete/protocols/oscar/liboscar/servicesetuptask.h new file mode 100644 index 00000000..c5bf5a70 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/servicesetuptask.h @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + servicesetuptask.h - Set up the services for the BOS connection + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SERVICESETUPTASK_H +#define SERVICESETUPTASK_H + +#include "task.h" + +class LocationRightsTask; +class ProfileTask; +class BLMLimitsTask; +class ICBMParamsTask; +class PRMParamsTask; +class SSIParamsTask; +class SSIListTask; +class SSIActivateTask; + + +/** +Set up the various services for the BOS connection +@author Matt Rogers +*/ +class ServiceSetupTask : public Task +{ +Q_OBJECT +public: + ServiceSetupTask( Task* parent ); + ~ServiceSetupTask(); + + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + +public slots: + void childTaskFinished(); + +private: + + /** Tracks how many tasks have finished */ + int m_finishedTaskCount; + + LocationRightsTask* m_locRightsTask; + ProfileTask* m_profileTask; + BLMLimitsTask* m_blmLimitsTask; + ICBMParamsTask* m_icbmTask; + PRMParamsTask* m_prmTask; + SSIParamsTask* m_ssiParamTask; + SSIListTask* m_ssiListTask; + SSIActivateTask* m_ssiActivateTask; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.cpp b/kopete/protocols/oscar/liboscar/snacprotocol.cpp new file mode 100644 index 00000000..3bf16bbc --- /dev/null +++ b/kopete/protocols/oscar/liboscar/snacprotocol.cpp @@ -0,0 +1,109 @@ +/* + Kopete Oscar Protocol + snacprotocol.cpp - reads the protocol used by Oscar for signalling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "snacprotocol.h" + +#include <qcstring.h> +#include <qdatastream.h> +#include <qobject.h> +#include <kdebug.h> +#include <stdlib.h> +#include "transfer.h" + + +using namespace Oscar; + +SnacProtocol::SnacProtocol(QObject *parent, const char *name) + : InputProtocolBase(parent, name) +{ +} + +SnacProtocol::~SnacProtocol() +{ +} + +Transfer* SnacProtocol::parse( const QByteArray & packet, uint& bytes ) +{ + BYTE b; + WORD w; + DWORD dw; + + FLAP f; + SNAC s; + SnacTransfer *st; + QDataStream* m_din = new QDataStream( packet, IO_ReadOnly ); + + //flap parsing + *m_din >> b; //this should be the start byte + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << b << endl; + *m_din >> b; + f.channel = b; + *m_din >> w; + f.sequence = w; + *m_din >> w; + f.length = w; + + if ( ( f.length + 6 ) > packet.size() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet not big enough to parse!" << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << packet.size() + << " we need " << f.length + 6 << endl; + return 0; + } + //snac parsing + *m_din >> w; + s.family = w; + *m_din >> w; + s.subtype = w; + *m_din >> w; + s.flags = w; + *m_din >> dw; + s.id = dw; + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "family: " << s.family + << " subtype: " << s.subtype << " flags: " << s.flags + << " id: " << s.id << endl; + + //use pointer arithmatic to skip the flap and snac headers + //so we don't have to do double parsing in the tasks + char* charPacket = packet.data(); + char* snacData; + int snacOffset = 10; //default + if ( s.flags >= 0x8000 ) //skip the next 8 bytes, we don't care about the snac version ATM + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "skipping snac version" << endl; + snacOffset = 18; + snacData = charPacket + 24; + } + else + { + snacOffset = 10; + snacData = charPacket + 16; + } + + Buffer *snacBuffer = new Buffer( snacData, f.length - snacOffset ); + st = new SnacTransfer( f, s, snacBuffer ); + bytes = f.length + 6; + delete m_din; + m_din = 0; + return st; +} + + +#include "snacprotocol.moc" diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.h b/kopete/protocols/oscar/liboscar/snacprotocol.h new file mode 100644 index 00000000..eea5c032 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/snacprotocol.h @@ -0,0 +1,46 @@ +/* + Kopete Oscar Protocol + snacprotocol.h - reads the protocol used by Oscar for signalling stuff + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_SNACPROTOCOL_H +#define OSCAR_SNACPROTOCOL_H + +#include "inputprotocolbase.h" + +class SnacTransfer; + + +class SnacProtocol : public InputProtocolBase +{ +Q_OBJECT +public: + SnacProtocol( QObject *parent = 0, const char *name = 0 ); + ~SnacProtocol(); + + /** + * Attempt to parse the supplied data into an @ref SnacTransfer object. + * The exact state of the parse attempt can be read using @ref state. + * @param rawData The unparsed data. + * @param bytes An integer used to return the number of bytes read. + * @return A pointer to an EventTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object. + */ + Transfer * parse( const QByteArray &, uint & bytes ); + +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp new file mode 100644 index 00000000..1e7a17d6 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp @@ -0,0 +1,50 @@ +/* + Kopete Oscar Protocol + ssiactivatetask.cpp - Send the SNAC for SSI activation + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <kdebug.h> +#include "ssiactivatetask.h" +#include "connection.h" +#include "buffer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "transfer.h" + + + +SSIActivateTask::SSIActivateTask(Task* parent): Task(parent) +{ +} + + +SSIActivateTask::~SSIActivateTask() +{ +} + + +void SSIActivateTask::onGo() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending SSI activate" << endl; + FLAP f = { 0x02, 0, 0 } ; + SNAC s = { 0x0013, 0x0007, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + Transfer* t = createTransfer( f, s, buffer ); + send( t ); + setSuccess( 0, QString::null ); +} + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.h b/kopete/protocols/oscar/liboscar/ssiactivatetask.h new file mode 100644 index 00000000..66f0a67b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.h @@ -0,0 +1,38 @@ +/* + Kopete Oscar Protocol + ssiactivatetask.h - Send the SNAC for SSI activation + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SSIACTIVATETASK_H +#define SSIACTIVATETASK_H + +#include "task.h" + +/** +A fire and forget task to send the activation SNAC for the SSI list to the server. + +@author Matt Rogers +*/ +class SSIActivateTask : public Task +{ +public: + SSIActivateTask( Task* parent ); + ~SSIActivateTask(); + void onGo(); +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.cpp b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp new file mode 100644 index 00000000..59188d2b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp @@ -0,0 +1,188 @@ +/* + Kopete Oscar Protocol + ssiauthtask.cpp - SSI Authentication Task + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "ssiauthtask.h" +#include "ssimanager.h" +#include "transfer.h" +#include "buffer.h" +#include "connection.h" +#include "oscarutils.h" + +#include <kdebug.h> + +SSIAuthTask::SSIAuthTask( Task* parent ) + : Task( parent ) +{ + m_manager = parent->client()->ssiManager(); +} + +SSIAuthTask::~SSIAuthTask() +{ +} + +bool SSIAuthTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0013 ) + return false; + + switch ( st->snacSubtype() ) + { + case 0x0015: // Future authorization granted + case 0x0019: // Authorization request + case 0x001b: // Authorization reply + case 0x001c: // "You were added" message + return true; + break; + default: + return false; + } +} + +bool SSIAuthTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + SnacTransfer* st = dynamic_cast<SnacTransfer*> ( t ); + + switch ( st->snacSubtype() ) + { + case 0x0015: // Future authorization granted + handleFutureAuthGranted(); + break; + case 0x0019: // Authorization request + handleAuthRequested(); + break; + case 0x001b: // Authorization reply + handleAuthReplied(); + break; + case 0x001c: // "You were added" message + handleAddedMessage(); + break; + } + setTransfer( 0 ); + return true; + } + return false; +} + +void SSIAuthTask::grantFutureAuth( const QString& uin, const QString& reason ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0014, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addBSTR( reason.utf8() ); + buf->addWord( 0x0000 ); // Unknown + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::sendAuthRequest( const QString& uin, const QString& reason ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0018, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addBSTR( reason.utf8() ); + buf->addWord( 0x0000 ); // Unknown + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::sendAuthReply( const QString& uin, const QString& reason, bool auth ) +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x001A, 0x0000, client()->snacSequence() }; + + Buffer* buf = new Buffer(); + buf->addBUIN( uin.latin1() ); + buf->addByte( auth ? 0x01 : 0x00 ); // accepted / declined + buf->addBSTR( reason.utf8() ); + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void SSIAuthTask::handleFutureAuthGranted() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + QByteArray reason = buf->getBSTR(); + + buf->getWord(); // 0x0000 - Unknown + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Future authorization granted from " << uin << endl; + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + emit futureAuthGranted( uin, QString::fromUtf8( reason.data(), reason.size() ) ); +} + +void SSIAuthTask::handleAuthRequested() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + QByteArray reason = buf->getBSTR(); + + buf->getWord(); // 0x0000 - Unknown + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization requested from " << uin << endl; + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + + emit authRequested( uin, QString::fromUtf8( reason.data(), reason.size() ) ); +} + +void SSIAuthTask::handleAuthReplied() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + bool accepted = buf->getByte(); + QByteArray reason = buf->getBSTR(); + + if ( accepted ) + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request accepted by " << uin << endl; + else + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request declined by " << uin << endl; + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl; + emit authReplied( uin, QString::fromUtf8( reason.data(), reason.size() ), accepted ); +} + +void SSIAuthTask::handleAddedMessage() +{ + Buffer* buf = transfer()->buffer(); + + QString uin = Oscar::normalize( buf->getBUIN() ); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "User " << uin << " added you to the contact list" << endl; + emit contactAddedYou( uin ); +} + +#include "ssiauthtask.moc" +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.h b/kopete/protocols/oscar/liboscar/ssiauthtask.h new file mode 100644 index 00000000..d470cfe9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiauthtask.h @@ -0,0 +1,60 @@ +/* + Kopete Oscar Protocol + ssiauthtask.h - SSI Authentication Task + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SSIAUTHTASK_H +#define SSIAUTHTASK_H + +#include <task.h> + +class SSIManager; + +/** +@author Kopete Developers +*/ +class SSIAuthTask : public Task +{ +Q_OBJECT +public: + SSIAuthTask( Task* parent ); + + ~SSIAuthTask(); + + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + void grantFutureAuth( const QString& uin, const QString& reason ); + void sendAuthRequest( const QString& uin, const QString& reason ); + void sendAuthReply( const QString& uin, const QString& reason, bool auth ); +signals: + void futureAuthGranted( const QString& uin, const QString& reason ); + void authRequested( const QString& uin, const QString& reason ); + void authReplied( const QString& uin, const QString& reason, bool auth ); + void contactAddedYou( const QString& uin ); +private: + void handleFutureAuthGranted(); + void handleAuthRequested(); + void handleAuthReplied(); + void handleAddedMessage(); + +private: + SSIManager* m_manager; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.cpp b/kopete/protocols/oscar/liboscar/ssilisttask.cpp new file mode 100644 index 00000000..fe2a981d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssilisttask.cpp @@ -0,0 +1,174 @@ +/* + Kopete Oscar Protocol + ssilisttask.cpp - handles all operations dealing with the whole SSI list + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "ssilisttask.h" + +#include <kdebug.h> +#include "connection.h" +#include "oscarutils.h" +#include "ssimanager.h" +#include "transfer.h" + +SSIListTask::SSIListTask( Task* parent ) : Task( parent ) +{ + m_ssiManager = client()->ssiManager(); + QObject::connect( this, SIGNAL( newContact( const Oscar::SSI& ) ), m_ssiManager, SLOT( newContact( const Oscar::SSI& ) ) ); + QObject::connect( this, SIGNAL( newGroup( const Oscar::SSI& ) ), m_ssiManager, SLOT( newGroup( const Oscar::SSI& ) ) ); + QObject::connect( this, SIGNAL( newItem( const Oscar::SSI& ) ), m_ssiManager, SLOT( newItem( const Oscar::SSI& ) ) ); +} + + +SSIListTask::~SSIListTask() +{} + +bool SSIListTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 ) + { + switch ( st->snacSubtype() ) + { + case 0x0006: + case 0x000F: + return true; + default: + return false; + }; + } + + return false; +} + +bool SSIListTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer * st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st->snacSubtype() == 0x0006 ) + { + setTransfer( transfer ); + handleSSIListReply(); + setTransfer( 0 ); + return true; + } + else if ( st->snacSubtype() == 0x000F ) + { + setTransfer( transfer ); + handleSSIUpToDate(); + setTransfer( 0 ); + return true; + } + } + + return false; +} + +void SSIListTask::onGo() +{ + checkSSITimestamp(); +} + +void SSIListTask::handleSSIListReply() +{ + QValueList<TLV> tlvList; + + Buffer* buffer = transfer()->buffer(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI Protocol version: " << buffer->getByte() << endl; + WORD ssiItems = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in this SSI packet: " << ssiItems << endl; + WORD parsedItems; + for ( parsedItems = 1; parsedItems <= ssiItems; ++parsedItems ) + { + tlvList.clear(); + WORD strlength = buffer->getWord(); + QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength ); + WORD groupId = buffer->getWord(); + WORD itemId = buffer->getWord(); + WORD itemType = buffer->getWord(); + WORD tlvLength = buffer->getWord(); + for ( int i = 0; i < tlvLength; ) + { + TLV t = buffer->getTLV(); + i += 4; + i += t.length; + tlvList.append( t ); + } + + if ( itemType == ROSTER_CONTACT ) + itemName = Oscar::normalize( itemName ); + + Oscar::SSI s( itemName, groupId, itemId, itemType, tlvList ); + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got SSI Item: " << s.toString() << endl; + if ( s.type() == ROSTER_GROUP ) + emit newGroup( s ); + + if ( s.type() == ROSTER_CONTACT ) + emit newContact( s ); + + if ( s.type() != ROSTER_CONTACT && s.type() != ROSTER_GROUP ) + emit newItem( s ); + } + + if ( buffer->length() > 0 ) + { + client()->ssiManager()->setLastModificationTime( buffer->getDWord() ); + //check the snac flags for another packet + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer() ); + if ( st && st->snacFlags() == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI List complete" << endl; + client()->ssiManager()->setListComplete( true ); + setSuccess( 0, QString::null ); + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Awaiting another SSI packet" << endl; + } + +} + +void SSIListTask::handleSSIUpToDate() +{ + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Our SSI List is up to date" << endl; + Buffer* buffer = transfer()->buffer(); + + client()->ssiManager()->setLastModificationTime( buffer->getDWord() ); + WORD ssiItems = buffer->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in SSI list: " << ssiItems << endl; + + client()->ssiManager()->setListComplete( true ); + setSuccess( 0, QString::null ); +} + +void SSIListTask::checkSSITimestamp() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Checking the timestamp of the SSI list" << endl; + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0005, 0x0000, client()->snacSequence() }; + Buffer* buffer = new Buffer(); + buffer->addDWord( client()->ssiManager()->lastModificationTime() ); + buffer->addDWord( client()->ssiManager()->numberOfItems() ); + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +#include "ssilisttask.moc" + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.h b/kopete/protocols/oscar/liboscar/ssilisttask.h new file mode 100644 index 00000000..96a4c3d8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssilisttask.h @@ -0,0 +1,106 @@ +/* + Kopete Oscar Protocol + ssilisttask.h - handles all operations dealing with the whole SSI list + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SSILISTTASK_H +#define SSILISTTASK_H + +#include <task.h> + +class SSI; +class SSIManager; + +/** + * This task handles all the operations dealing with the whole SSI list + * All SNACs handled by this task are in family 13. Subtypes 5, 6, and 7 + * are handled by individual functions. Subtype F is handled in the take() + * function. We don't use subtype 4 because the same thing can be accomplished + * using subtypes 5 and 6 together. + * + * @author Matt Rogers +*/ +class SSIListTask : public Task +{ +Q_OBJECT +public: + SSIListTask( Task* parent ); + ~SSIListTask(); + + virtual bool take( Transfer* transfer ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + virtual void onGo(); + +signals: + /** We have a new group */ + void newGroup( const Oscar::SSI& ); + + /** We have a new contact */ + void newContact( const Oscar::SSI& ); + + /** + * We have a new contact and they're on the visible list + */ + void newVisibleItem( const Oscar::SSI& ); + + /** + * We have a new contact and they're on the invisible list + */ + void newInvisibleItem( const Oscar::SSI& ); + + /** + * We have a new item + * Used for items we don't explicitly handle yet + */ + void newItem( const Oscar::SSI& ); + +private: + + /** + * Handle the list we get from the server + * This is SNAC( 0x13, 0x06 ) + */ + void handleSSIListReply(); + + /** + * Check the timestamp of the local SSI copy + * If it's up to date, we'll get SNAC( 13, 06 ) + * If it's out of date, we'll get SNAC( 13, 0F ) + * This is SNAC( 0x13, 0x05 ) + */ + void checkSSITimestamp(); + + /** + * The timestamp of the SSI is up to date + * This is SNAC( 0x13, 0x0F ) + */ + void handleSSIUpToDate(); + + +private: + /** + * Pointer to the SSI manager so we don't have to keep + * calling client()->ssiManager(). It's guaranteed to + * exist. + */ + SSIManager* m_ssiManager; + +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimanager.cpp b/kopete/protocols/oscar/liboscar/ssimanager.cpp new file mode 100644 index 00000000..066e93fa --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimanager.cpp @@ -0,0 +1,658 @@ +/* + Kopete Oscar Protocol + ssimanager.cpp - SSI management + + Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + Copyright ( c ) 2004 Matt Rogers <mattr@kde.org> + + Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu> + + ************************************************************************* + * * + * 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 of the License, or ( at your option ) any later version. * + * * + ************************************************************************* +*/ + +#include "ssimanager.h" +#include <kdebug.h> +#include "oscarutils.h" + +// ------------------------------------------------------------------- + +class SSIManagerPrivate +{ +public: + QValueList<Oscar::SSI> SSIList; + QValueList<WORD> groupIdList; + QValueList<WORD> itemIdList; + bool complete; + DWORD lastModTime; + WORD maxContacts; + WORD maxGroups; + WORD maxVisible; + WORD maxInvisible; + WORD maxIgnore; + WORD nextContactId; + WORD nextGroupId; +}; + +SSIManager::SSIManager( QObject *parent, const char *name ) + : QObject( parent, name ) +{ + d = new SSIManagerPrivate; + d->complete = false; + d->lastModTime = 0; + d->nextContactId = 0; + d->nextGroupId = 0; + d->maxContacts = 999; + d->maxGroups = 999; + d->maxIgnore = 999; + d->maxInvisible = 999; + d->maxVisible = 999; +} + + +SSIManager::~SSIManager() +{ + clear(); + delete d; +} + +void SSIManager::clear() +{ + //delete all SSIs from the list + if ( d->SSIList.count() > 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing the SSI list" << endl; + QValueList<Oscar::SSI>::iterator it = d->SSIList.begin(); + + while ( it != d->SSIList.end() && d->SSIList.count() > 0 ) + it = d->SSIList.remove( it ); + }; + + d->itemIdList.clear(); + d->groupIdList.clear(); + d->complete = false; + d->lastModTime = 0; + d->nextContactId = 0; + d->nextGroupId = 0; +} + +WORD SSIManager::nextContactId() +{ + if ( d->nextContactId == 0 ) + d->nextContactId++; + + d->nextContactId = findFreeId( d->itemIdList, d->nextContactId ); + + if ( d->nextContactId == 0xFFFF ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free id!" << endl; + return 0xFFFF; + } + + if ( d->itemIdList.contains( d->nextContactId ) == 0 ) + d->itemIdList.append( d->nextContactId ); + + return d->nextContactId++; +} + +WORD SSIManager::nextGroupId() +{ + if ( d->nextGroupId == 0 ) + d->nextGroupId++; + + d->nextGroupId = findFreeId( d->groupIdList, d->nextGroupId ); + + if ( d->nextGroupId == 0xFFFF ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free group id!" << endl; + return 0xFFFF; + } + + if ( d->groupIdList.contains( d->nextGroupId ) == 0 ) + d->groupIdList.append( d->nextGroupId ); + + return d->nextGroupId++; +} + +WORD SSIManager::numberOfItems() const +{ + return d->SSIList.count(); +} + +DWORD SSIManager::lastModificationTime() const +{ + return d->lastModTime; +} + +void SSIManager::setLastModificationTime( DWORD lastTime ) +{ + d->lastModTime = lastTime; +} + +void SSIManager::setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible, WORD maxInvisible, WORD maxIgnore ) +{ + //I'm not using k_funcinfo for these debug statements because of + //the function's long signature + QString funcName = QString::fromLatin1( "[void SSIManager::setParameters] " ); + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed in SSI: " + << maxContacts << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of groups allowed in SSI: " + << maxGroups << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on visible list: " + << maxVisible << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on invisible list: " + << maxInvisible << endl; + kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on ignore list: " + << maxIgnore << endl; + + d->maxContacts = maxContacts; + d->maxGroups = maxGroups; + d->maxInvisible = maxInvisible; + d->maxVisible = maxVisible; + d->maxIgnore = maxIgnore; +} + +void SSIManager::loadFromExisting( const QValueList<Oscar::SSI*>& newList ) +{ + Q_UNUSED( newList ); + //FIXME: NOT Implemented! +} + +bool SSIManager::hasItem( const Oscar::SSI& item ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + Oscar::SSI s = ( *it ); + if ( s == item ) + return true; + } + + return false; +} + +Oscar::SSI SSIManager::findGroup( const QString &group ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP && (*it ).name().lower() == group.lower() ) + return ( *it ); + + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findGroup( int groupId ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP && (*it ).gid() == groupId ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( const QString &contact, const QString &group ) const +{ + + if ( contact.isNull() || group.isNull() ) + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << + "Passed NULL name or group string, aborting!" << endl; + + return m_dummyItem; + } + + Oscar::SSI gr = findGroup( group ); // find the parent group + if ( gr.isValid() ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "gr->name= " << gr.name() << + ", gr->gid= " << gr.gid() << + ", gr->bid= " << gr.bid() << + ", gr->type= " << gr.type() << endl; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact && (*it ).gid() == gr.gid() ) + { + //we have found our contact + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "Found contact " << contact << " in SSI data" << endl; + return ( *it ); + } + } + } + else + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << + "ERROR: Group '" << group << "' not found!" << endl; + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( const QString &contact ) const +{ + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findContact( int contactId ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && ( *it ).bid() == contactId ) + return ( *it ); + + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItemForIcon( QByteArray iconHash ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_BUDDYICONS ) + { + TLV t = Oscar::findTLV( ( *it ).tlvList(), 0x00D5 ); + Buffer b(t.data); + b.skipBytes(1); //don't care about flags + BYTE iconSize = b.getByte(); + QByteArray hash( b.getBlock( iconSize ) ); + if ( hash == iconHash ) + { + Oscar::SSI s = ( *it ); + return s; + } + } + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItemForIconByRef( int ref ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + { + if ( ( *it ).type() == ROSTER_BUDDYICONS ) + { + if ( ( *it ).name().toInt() == ref ) + { + Oscar::SSI s = ( *it ); + return s; + } + } + } + return m_dummyItem; +} + +Oscar::SSI SSIManager::findItem( const QString &contact, int type ) const +{ + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + + for ( it = d->SSIList.begin(); it!= listEnd; ++it ) + if ( ( *it ).type() == type && ( *it ).name() == contact ) + return ( *it ); + + return m_dummyItem; +} + +QValueList<Oscar::SSI> SSIManager::groupList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_GROUP ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::visibleList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_VISIBLE ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::invisibleList() const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_INVISIBLE ) + list.append( ( *it ) ); + + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactsFromGroup( const QString &group ) const +{ + QValueList<Oscar::SSI> list; + + Oscar::SSI gr = findGroup( group ); + if ( gr.isValid() ) + { + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == gr.gid() ) + list.append( ( *it ) ); + } + return list; +} + +QValueList<Oscar::SSI> SSIManager::contactsFromGroup( int groupId ) const +{ + QValueList<Oscar::SSI> list; + + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == groupId ) + list.append( ( *it ) ); + + return list; +} + +Oscar::SSI SSIManager::visibilityItem() const +{ + Oscar::SSI item = m_dummyItem; + QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end(); + for ( it = d->SSIList.begin(); it != listEnd; ++it ) + { + if ( ( *it ).type() == 0x0004 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found visibility setting" << endl; + item = ( *it ); + return item; + } + } + + return item; +} + +void SSIManager::setListComplete( bool complete ) +{ + d->complete = complete; +} + +bool SSIManager::listComplete() const +{ + return d->complete; +} + +bool SSIManager::newGroup( const Oscar::SSI& group ) +{ + //trying to find the group by its ID + QValueList<Oscar::SSI>::iterator it, listEnd = d->SSIList.end(); + if ( findGroup( group.name() ).isValid() ) + return false; + + if ( !group.name().isEmpty() ) //avoid the group with gid 0 and bid 0 + { // the group is really new + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group '" << group.name() << "' to SSI list" << endl; + + d->SSIList.append( group ); + addID( group ); + emit groupAdded( group ); + return true; + } + return false; +} + +bool SSIManager::updateGroup( const Oscar::SSI& group ) +{ + Oscar::SSI oldGroup = findGroup( group.name() ); + + if ( oldGroup.isValid() ) + { + removeID( oldGroup ); + d->SSIList.remove( oldGroup ); + } + + if ( d->SSIList.findIndex( group ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New group is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating group '" << group.name() << "' in SSI list" << endl; + d->SSIList.append( group ); + addID( group ); + emit groupUpdated( group ); + + return true; +} + +bool SSIManager::removeGroup( const Oscar::SSI& group ) +{ + QString groupName = group.name(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing group " << group.name() << endl; + int remcount = d->SSIList.remove( group ); + removeID( group ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No groups removed" << endl; + return false; + } + + emit groupRemoved( groupName ); + return true; +} + +bool SSIManager::removeGroup( const QString &group ) +{ + Oscar::SSI gr = findGroup( group ); + + if ( gr.isValid() && removeGroup( gr ) ) + { + return true; + } + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Group " << group << " not found." << endl; + + return false; +} + +bool SSIManager::newContact( const Oscar::SSI& contact ) +{ + if ( d->SSIList.findIndex( contact ) == -1 ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact '" << contact.name() << "' to SSI list" << endl; + addID( contact ); + d->SSIList.append( contact ); + emit contactAdded( contact ); + } + else + return false; + return true; +} + +bool SSIManager::updateContact( const Oscar::SSI& contact ) +{ + Oscar::SSI oldContact = findContact( contact.name() ); + + if ( oldContact.isValid() ) + { + removeID( oldContact ); + d->SSIList.remove( oldContact ); + } + + if ( d->SSIList.findIndex( contact ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New contact is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating contact '" << contact.name() << "' in SSI list" << endl; + addID( contact ); + d->SSIList.append( contact ); + emit contactUpdated( contact ); + + return true; +} + +bool SSIManager::removeContact( const Oscar::SSI& contact ) +{ + QString contactName = contact.name(); + int remcount = d->SSIList.remove( contact ); + removeID( contact ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No contacts were removed." << endl; + return false; + } + + emit contactRemoved( contactName ); + return true; +} + +bool SSIManager::removeContact( const QString &contact ) +{ + Oscar::SSI ct = findContact( contact ); + + if ( ct.isValid() && removeContact( ct ) ) + return true; + else + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact " << contact << " not found." << endl; + + return false; +} + +bool SSIManager::newItem( const Oscar::SSI& item ) +{ + if ( d->SSIList.findIndex( item ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Item is already in list." << endl; + return false; + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding item " << item.toString() << endl; + d->SSIList.append( item ); + addID( item ); + return true; +} + +bool SSIManager::updateItem( const Oscar::SSI& item ) +{ + Oscar::SSI oldItem = findItem( item.name(), item.type() ); + + if ( oldItem.isValid() ) + { + removeID( oldItem ); + d->SSIList.remove( oldItem ); + } + + if ( d->SSIList.findIndex( item ) != -1 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New item is already in list." << endl; + return false; + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating item in SSI list" << endl; + addID( item ); + d->SSIList.append( item ); + return true; +} + +bool SSIManager::removeItem( const Oscar::SSI& item ) +{ + int remcount = d->SSIList.remove( item ); + removeID( item ); + + if ( remcount == 0 ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No items were removed." << endl; + return false; + } + + return true; +} + +void SSIManager::addID( const Oscar::SSI& item ) +{ + if ( item.type() == ROSTER_GROUP ) + { + if ( d->groupIdList.contains( item.gid() ) == 0 ) + d->groupIdList.append( item.gid() ); + } + else + { + if ( d->itemIdList.contains( item.bid() ) == 0 ) + d->itemIdList.append( item.bid() ); + } +} + +void SSIManager::removeID( const Oscar::SSI& item ) +{ + if ( item.type() == ROSTER_GROUP ) + { + d->groupIdList.remove( item.gid() ); + + if ( d->nextGroupId > item.gid() ) + d->nextGroupId = item.gid(); + } + else + { + d->itemIdList.remove( item.bid() ); + + if ( d->nextContactId > item.bid() ) + d->nextContactId = item.bid(); + } +} + +WORD SSIManager::findFreeId( const QValueList<WORD>& idList, WORD fromId ) const +{ + for ( WORD id = fromId; id < 0x8000; id++ ) + { + if ( idList.contains( id ) == 0 ) + return id; + } + + return 0xFFFF; +} + +#include "ssimanager.moc" + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimanager.h b/kopete/protocols/oscar/liboscar/ssimanager.h new file mode 100644 index 00000000..24e87c6a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimanager.h @@ -0,0 +1,154 @@ +/* + Kopete Oscar Protocol + ssimanager.h - SSI management + + Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + Copyright ( c ) 2004 Matt Rogers <mattr@kde.org> + + Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu> + + ************************************************************************* + * * + * 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 of the License, or ( at your option ) any later version. * + * * + ************************************************************************* +*/ + +#ifndef SSIMANAGER_H +#define SSIMANAGER_H + +#include <qobject.h> +#include <qvaluelist.h> + +#include "oscartypes.h" +#include "oscartypeclasses.h" + +using namespace Oscar; + +class SSIManagerPrivate; + +/** +SSI management + +@author Gustavo Pichorim Boiko +@author Matt Rogers +*/ +class KOPETE_EXPORT SSIManager : public QObject +{ + Q_OBJECT +public: + SSIManager( QObject* parent = 0, const char* name = 0 ); + + ~SSIManager(); + + /** Clear the internal SSI list */ + void clear(); + + /** Get the next buddy id for an SSI item */ + WORD nextContactId(); + + /** Get the next group id for an SSI item */ + WORD nextGroupId(); + + /** Get the number of items in the SSI list. */ + WORD numberOfItems() const; + + /** Get the timestamp the list was last modified */ + DWORD lastModificationTime() const; + + /** Set the timestamp of the last modification time */ + void setLastModificationTime( DWORD lastTime ); + + /** Set the parameters we should use for SSI */ + void setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible, + WORD maxInvisible, WORD maxIgnore ); + + /** + * Load an existing list from SSI objects. + * The current SSI list will be overwritten and it's contents + * replaced with the data from the new list + */ + void loadFromExisting( const QValueList<Oscar::SSI*>& newList ); + + bool hasItem( const Oscar::SSI& item ) const; + + Oscar::SSI findGroup( const QString& group ) const; + Oscar::SSI findGroup( int groupId ) const; + + + Oscar::SSI findContact( const QString& contact, const QString& group ) const; + Oscar::SSI findContact( const QString& contact ) const; + Oscar::SSI findContact( int contactId ) const; + + Oscar::SSI findItemForIcon( QByteArray iconHash ) const; + Oscar::SSI findItemForIconByRef( int ) const; + + Oscar::SSI findItem( const QString &contact, int type ) const; + + QValueList<Oscar::SSI> groupList() const; + QValueList<Oscar::SSI> contactList() const; + QValueList<Oscar::SSI> visibleList() const; + QValueList<Oscar::SSI> invisibleList() const; + QValueList<Oscar::SSI> contactsFromGroup( const QString& group ) const; + QValueList<Oscar::SSI> contactsFromGroup( int groupId ) const; + + Oscar::SSI visibilityItem() const; + + void setListComplete( bool complete ); + bool listComplete() const; + +public slots: + bool newGroup( const Oscar::SSI& group ); + bool updateGroup( const Oscar::SSI& group ); + bool removeGroup( const Oscar::SSI& group ); + bool removeGroup( const QString& group ); + + bool newContact( const Oscar::SSI& contact ); + bool updateContact( const Oscar::SSI& contact ); + bool removeContact( const Oscar::SSI& contact ); + bool removeContact( const QString& contact ); + + bool newItem( const Oscar::SSI& item ); + bool updateItem( const Oscar::SSI& item ); + bool removeItem( const Oscar::SSI& item ); + + void addID( const Oscar::SSI& item ); + void removeID( const Oscar::SSI& item ); + +signals: + + //! Emitted when we've added a new contact to the list + void contactAdded( const Oscar::SSI& ); + + //! Emitted when we've updated a contact in the list + void contactUpdated( const Oscar::SSI& ); + + //! Emitted when we've removed a contact from the list + void contactRemoved( const QString& contactName ); + + //! Emitted when we've added a new group to the list + void groupAdded( const Oscar::SSI& ); + + //! Emitted when we've updated a group in the list + void groupUpdated( const Oscar::SSI& ); + + //! Emitted when we've removed a group from the ssi list + void groupRemoved( const QString& groupName ); + + void modifyError( const QString& error ); + +private: + WORD findFreeId( const QValueList<WORD>& idList, WORD fromId ) const; + + SSIManagerPrivate* d; + Oscar::SSI m_dummyItem; +}; + +#endif + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.cpp b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp new file mode 100644 index 00000000..2091fca8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp @@ -0,0 +1,637 @@ +/* + Kopete Oscar Protocol + ssimodifytask.cpp - Handles all the ssi modification stuff + + Copyright (c) 2004 by Kopete Developers <kopete-devel@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "ssimodifytask.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qstring.h> +#include "connection.h" +#include "oscarutils.h" +#include "transfer.h" + + +SSIModifyTask::SSIModifyTask( Task* parent, bool staticTask ) : Task( parent ) +{ + m_ssiManager = parent->client()->ssiManager(); + m_static = staticTask; + m_opType = NoType; + m_opSubject = NoSubject; + m_id = 0; +} + + +SSIModifyTask::~SSIModifyTask() +{ +} + +void SSIModifyTask::onGo() +{ + sendSSIUpdate(); +} + +bool SSIModifyTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + { + setTransfer( transfer ); + + if ( st->snacSubtype() == 0x0008 ) + handleSSIAdd(); + else if ( st->snacSubtype() == 0x0009 ) + handleSSIUpdate(); + else if ( st->snacSubtype() == 0x000A ) + handleSSIRemove(); + else if ( st->snacSubtype() == 0x000E ) + handleSSIAck(); + + setTransfer( 0 ); + } + return true; + } + else + return false; +} + +bool SSIModifyTask::addContact( const QString& contact, const QString& group, bool requiresAuth ) +{ + m_opType = Add; + m_opSubject = Contact; + + QString newContact = Oscar::normalize( contact ); + + Oscar::SSI oldItem = m_ssiManager->findContact( newContact ); + Oscar::SSI groupItem = m_ssiManager->findGroup( group ); + + if ( !groupItem ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "group " << group << " does not exist on SSI. Aborting" << endl; + return false; + } + + //create new SSI item and populate the TLV list + QValueList<TLV> tlvList; + if ( requiresAuth ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "This contact requires auth. adding appropriate tlv" << endl; + TLV t( 0x0066, 0, 0 ); + tlvList.append( t ); + } + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "creating new SSI item for " << contact << " in group " << group << endl; + Oscar::SSI newItem( newContact, groupItem.gid(), m_ssiManager->nextContactId(), ROSTER_CONTACT, tlvList ); + m_newItem = newItem; + return true; +} + +bool SSIModifyTask::removeContact( const QString& contact ) +{ + m_opType = Remove; + m_opSubject = Contact; + m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling" << m_oldItem.name() << " for removal" << endl; + return true; +} + +bool SSIModifyTask::changeGroup( const QString& contact, const QString& newGroup ) +{ + m_opType = Change; + m_opSubject = Group; + m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) ); + Oscar::SSI oldGroupItem; + if ( m_oldItem.isValid() ) + oldGroupItem = m_ssiManager->findGroup( newGroup ); + else + return false; + + if ( m_oldItem.gid() == oldGroupItem.gid() ) + { //buddy already exists in this group + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "contact " << contact << " already exists in group " << oldGroupItem.name() << ". Aborting." << endl; + return false; + } + + m_groupItem = m_ssiManager->findGroup( newGroup ); + if ( !m_groupItem ) + { //couldn't find group + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "new group " << newGroup << " not found in SSI. Aborting" << endl; + return false; + } + + //create a new SSI item for the buddy in the new group + Oscar::SSI newItem( m_oldItem.name(), m_groupItem.gid(), m_oldItem.bid(), ROSTER_CONTACT, m_oldItem.tlvList() ); + m_newItem = newItem; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Moving '" << m_oldItem.name() << "' to group " << m_groupItem.name() << endl; + return true; +} + +bool SSIModifyTask::addGroup( const QString& groupName ) +{ + m_opType = Add; + m_opSubject = Group; + m_newItem = m_ssiManager->findGroup( groupName ); + QValueList<TLV> dummy; + Oscar::SSI newItem( groupName, m_ssiManager->nextGroupId(), 0, ROSTER_GROUP, dummy ); + m_newItem = newItem; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding group '" << m_newItem.name() << "' to SSI" << endl; + return true; +} + +bool SSIModifyTask::removeGroup( const QString& groupName ) +{ + m_opType = Remove; + m_opSubject = Group; + m_oldItem = m_ssiManager->findGroup( groupName ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling group '" << m_oldItem.name() << "' for removal. " << endl; + return true; +} + +bool SSIModifyTask::renameGroup( const QString& oldName, const QString & newName ) +{ + m_opType = Rename; + m_opSubject = Group; + if ( oldName == newName ) + return false; + + m_oldItem = m_ssiManager->findGroup( oldName ); + Oscar::SSI newItem( newName, m_oldItem.gid(), m_oldItem.bid(), ROSTER_GROUP, m_oldItem.tlvList() ); + m_newItem = newItem; + return true; +} + +bool SSIModifyTask::addItem( const Oscar::SSI& item ) +{ + m_opType = Add; + m_opSubject = NoSubject; + m_newItem = item; + return true; +} + +bool SSIModifyTask::removeItem( const Oscar::SSI& item ) +{ + m_opType = Remove; + m_opSubject = NoSubject; + m_oldItem = item; + return true; +} + +bool SSIModifyTask::modifyItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem ) +{ + if ( !m_ssiManager->hasItem( oldItem ) ) + return false; + + //make sure there are some common things between the two items + if ( oldItem.type() != newItem.type() ) + return false; + + m_oldItem = oldItem; + m_newItem = newItem; + m_opType = Change; + m_opSubject = NoSubject; + return true; +} + +bool SSIModifyTask::forMe( const Transfer * transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 ) + { + WORD subtype = st->snacSubtype(); + if ( m_static ) + { + if ( subtype == 0x0008 || subtype == 0x0009 || subtype == 0x000A ) + return true; + } + else + { + if ( subtype == 0x000E && m_id == st->snac().id ) + return true; + } + } + + return false; +} + +void SSIModifyTask::handleSSIAck() +{ + Buffer* b = transfer()->buffer(); + int numItems = b->length() / 2; + for( int i = 0; i < numItems; ++i ) + { + WORD ackCode = b->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << "Acknowledgement code is " << ackCode << endl; + + if ( ackCode != 0x0000 ) + freeIdOnError(); + + switch( ackCode ) + { + case 0x0000: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "SSI Update successful" << endl; + updateSSIManager(); + break; + case 0x0002: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item to modify not found in list" << endl; + setSuccess( 0, QString::null ); + break; + case 0x0003: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item already exists in SSI" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000A: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Error adding item ( invalid id, already in list, invalid data )" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000C: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item. Limit exceeded." << endl; + setSuccess( 0, QString::null ); + break; + case 0x000D: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add ICQ item to AIM list ( and vice versa )" << endl; + setSuccess( 0, QString::null ); + break; + case 0x000E: + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item because contact requires authorization" << endl; + Oscar::SSI groupItem = m_ssiManager->findGroup( m_newItem.gid() ); + QString groupName = groupItem.name(); + addContact( m_newItem.name(), groupName, true ); + go(); + break; + } + default: + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Unknown acknowledgement code" << endl; + setSuccess( 0, QString::null ); + break; + } + }; + + +} + +void SSIModifyTask::sendSSIUpdate() +{ + //what type of update are we sending? + if ( m_opSubject == Group && m_opType == Change ) + changeGroupOnServer(); + + //add an item to the ssi list + if ( m_opType == Add ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding an item to the SSI list" << endl; + sendEditStart(); + + //add the item + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x0008, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_newItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + + //remove an item + if ( m_opType == Remove ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI" << endl; + sendEditStart(); + + //remove the item + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x000A, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_oldItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + + //modify an item + //we use rename for group and change for other items + if ( m_opType == Rename || ( m_opType == Change && m_opSubject != Group ) ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Modifying the item: " << m_oldItem.toString() << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "changing it to: " << m_newItem.toString() << endl; + sendEditStart(); + + //change the group name + FLAP f1 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); + SNAC s1 = { 0x0013, 0x0009, 0x0000, m_id }; + Buffer* ssiBuffer = new Buffer; + ssiBuffer->addString( m_newItem ); + Transfer* t2 = createTransfer( f1, s1, ssiBuffer ); + send( t2 ); + + sendEditEnd(); + } + +} + +void SSIModifyTask::changeGroupOnServer() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Moving a contact from one group to another" << endl; + + sendEditStart(); + + //remove the old buddy from the list + FLAP f1 = { 0x02, 0, 0 }; + SNAC s1 = { 0x0013, 0x000A, 0x0000, client()->snacSequence() }; + Buffer* b1 = new Buffer; + b1->addBSTR( m_oldItem.name().latin1() ); + b1->addWord( m_oldItem.gid() ); + b1->addWord( m_oldItem.bid() ); + b1->addWord( m_oldItem.type() ); + b1->addWord( 0 ); + Transfer* t2 = createTransfer( f1, s1, b1 ); + send( t2 ); + + //add the buddy to the list with a different group + FLAP f2 = { 0x02, 0, 0 }; + m_id = client()->snacSequence(); //we don't care about the first ack + SNAC s2 = { 0x0013, 0x0008, 0x0000, m_id }; + Buffer* b2 = new Buffer; + addItemToBuffer( m_newItem, b2 ); + + Transfer* t3 = createTransfer( f2, s2, b2 ); + send( t3 ); + + //find the old group so we can change it's list of buddy ids + //what a kludge + Oscar::SSI oldGroupItem = m_ssiManager->findGroup( m_oldItem.gid() ); + /* not checking the existance of oldGroupItem because if we got here + it has to exist */ + + //Change the 0x00C8 TLV in the old group item to remove the bid we're + //moving to a different group + QValueList<TLV> list = oldGroupItem.tlvList(); + TLV oldIds = Oscar::findTLV( list, 0x00C8 ); + if ( oldIds.type == 0x00C8 ) + { + Buffer newTLVData; + Buffer tlvBuffer( oldIds.data, oldIds.length ); + while ( tlvBuffer.length() != 0 ) + { + WORD id = tlvBuffer.getWord(); + if ( id != m_oldItem.bid() ) + newTLVData.addWord( id ); + } + + TLV newGroupTLV( 0x00C8, newTLVData.length(), newTLVData.buffer() ); + + list.remove( oldIds ); + list.append( newGroupTLV ); + oldGroupItem.setTLVList( list ); + } + + + //Change the 0x00C8 TLV in the new group item to add the bid we're + //adding to this group + QValueList<TLV> list2 = m_groupItem.tlvList(); + TLV oldIds2 = Oscar::findTLV( list2, 0x00C8 ); + TLV newGroupTLV; + if ( oldIds2.type == 0x00C8 ) + { + Buffer tlvBuffer( oldIds2.data, oldIds2.length ); + tlvBuffer.addWord( m_newItem.bid() ); + + TLV newGroupTLV( 0x00C8, tlvBuffer.length(), tlvBuffer.buffer() ); + list2.remove( oldIds ); + list2.append( newGroupTLV ); + m_groupItem.setTLVList( list2 ); + } + + //change the group properties + FLAP f3 = { 0x02, 0, 0 }; + SNAC s3 = { 0x0013, 0x0009, 0x0000, client()->snacSequence() }; + Buffer* b3 = new Buffer; + addItemToBuffer( oldGroupItem, b3 ); + addItemToBuffer( m_groupItem, b3 ); + + Transfer* t4 = createTransfer( f3, s3, b3 ); //we get no ack from this packet + send( t4 ); + + sendEditEnd(); +} + +void SSIModifyTask::updateSSIManager() +{ + if ( m_oldItem.isValid() && m_newItem.isValid() ) + { + if ( m_opSubject == Contact ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl; + m_ssiManager->removeContact( m_oldItem.name() ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl; + m_ssiManager->newContact( m_newItem ); + } + else if ( m_opSubject == Group ) + { + if ( m_opType == Rename ) + m_ssiManager->updateGroup( m_newItem ); + else if ( m_opType == Change ) + m_ssiManager->updateContact( m_newItem ); + } + else if ( m_opSubject == NoSubject ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl; + m_ssiManager->removeItem( m_oldItem ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl; + m_ssiManager->newItem( m_newItem ); + } + setSuccess( 0, QString::null ); + return; + } + + if ( m_oldItem.isValid() && !m_newItem ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI manager" << endl; + if ( m_opSubject == Group ) + m_ssiManager->removeGroup( m_oldItem.name() ); + else if ( m_opSubject == Contact ) + m_ssiManager->removeContact( m_oldItem.name() ); + else if ( m_opSubject == NoSubject ) + m_ssiManager->removeItem( m_oldItem ); + setSuccess( 0, QString::null ); + return; + } + + if ( m_newItem.isValid() && !m_oldItem ) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << m_newItem.name() << " to SSI manager" << endl; + if ( m_opSubject == Group ) + m_ssiManager->newGroup( m_newItem ); + else if ( m_opSubject == Contact ) + m_ssiManager->newContact( m_newItem ); + else if ( m_opSubject == NoSubject ) + m_ssiManager->newItem( m_newItem ); + setSuccess( 0, QString::null ); + return; + } + + setSuccess( 0, QString::null ); +} + +void SSIModifyTask::freeIdOnError() +{ + if ( m_oldItem.isValid() && m_newItem.isValid() ) + { + if ( m_opSubject == Contact || m_opSubject == NoSubject ) + { + if ( m_oldItem.bid() != m_newItem.bid() ) + m_ssiManager->removeID( m_newItem ); + } + else if ( m_opSubject == Group ) + { + if ( m_oldItem.gid() != m_newItem.gid() ) + m_ssiManager->removeID( m_newItem ); + } + } + else if ( m_newItem.isValid() && !m_oldItem ) + { + if ( m_opSubject == Group || m_opSubject == Contact || + m_opSubject == NoSubject ) + { + m_ssiManager->removeID( m_newItem ); + } + } +} + +void SSIModifyTask::sendEditStart() +{ + SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() }; + FLAP editStart = { 0x02, 0, 10 }; + Buffer* emptyBuffer = new Buffer; + Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer ); + send( t1 ); +} + +void SSIModifyTask::sendEditEnd() +{ + SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() }; + FLAP editEnd = { 0x02, 0, 10 } ; + Buffer* emptyBuffer = new Buffer; + Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer ); + send( t5 ); +} + +void SSIModifyTask::addItemToBuffer( Oscar::SSI item, Buffer* buffer ) +{ + buffer->addBSTR( item.name().latin1() ); + buffer->addWord( item.gid() ); + buffer->addWord( item.bid() ); + buffer->addWord( item.type() ); + buffer->addWord( item.tlvListLength() ); + + QValueList<TLV>::const_iterator it = item.tlvList().begin(); + QValueList<TLV>::const_iterator listEnd = item.tlvList().end(); + for( ; it != listEnd; ++it ) + buffer->addTLV( ( *it ) ); +} + +Oscar::SSI SSIModifyTask::getItemFromBuffer( Buffer* buffer ) const +{ + QValueList<TLV> tlvList; + + WORD strlength = buffer->getWord(); + QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength ); + WORD groupId = buffer->getWord(); + WORD itemId = buffer->getWord(); + WORD itemType = buffer->getWord(); + WORD tlvLength = buffer->getWord(); + for ( int i = 0; i < tlvLength; ) + { + TLV t = buffer->getTLV(); + i += 4; + i += t.length; + tlvList.append( t ); + } + + if ( itemType == ROSTER_CONTACT ) + itemName = Oscar::normalize( itemName ); + + return Oscar::SSI( itemName, groupId, itemId, itemType, tlvList ); +} + +void SSIModifyTask::handleSSIAdd() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << item.name() << " to SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->newGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->newContact( item ); + else + m_ssiManager->newItem( item ); + } +} + +void SSIModifyTask::handleSSIUpdate() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Updating " << item.name() << " in SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->updateGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->updateContact( item ); + else + m_ssiManager->updateItem( item ); + } +} + +void SSIModifyTask::handleSSIRemove() +{ + Buffer* b = transfer()->buffer(); + + while ( b->length() > 0 ) + { + Oscar::SSI item = getItemFromBuffer( b ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << item.name() << " from SSI manager" << endl; + + if ( item.type() == ROSTER_GROUP ) + m_ssiManager->removeGroup( item ); + else if ( item.type() == ROSTER_CONTACT ) + m_ssiManager->removeContact( item ); + else + m_ssiManager->removeItem( item ); + } +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.h b/kopete/protocols/oscar/liboscar/ssimodifytask.h new file mode 100644 index 00000000..2c32dda3 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssimodifytask.h @@ -0,0 +1,156 @@ +/* + Kopete Oscar Protocol + ssimodifytask.h - Handles all the ssi modification stuff + + Copyright (c) 2004 by Kopete Developers <kopete-devel@kde.org> + + Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SSIMODIFYTASK_H +#define SSIMODIFYTASK_H + +#include "task.h" +#include "oscartypes.h" +#include "ssimanager.h" + + +class Buffer; + +/** +This class takes care of any SSI list modifications that need to be made. This includes: +@li adds +@li edits +@li removes +@li group changes +@li alias changes +@li authorization changes +etc. + +This task implements the following SNACs from the SSI family (0x0013): +@li 0x0008 +@li 0x0009 +@li 0x000A +@li 0x000E +@li 0x0011 +@li 0x0012 + +@author Matt Rogers +*/ +class SSIModifyTask : public Task +{ +public: + SSIModifyTask( Task* parent, bool staticTask = false ); + ~SSIModifyTask(); + + virtual void onGo(); + virtual bool take( Transfer* transfer ); + + /* Contact properties */ + enum OperationType { NoType = 0x00, Add = 0x10, Remove = 0x20, Rename = 0x40, Change = 0x80 }; + enum OperationSubject { NoSubject = 0x000, Contact = 0x100, Group = 0x200, Visibility = 0x400, Invisibility = 0x800 }; + + //! Set up the stuff needed to add a contact. + //! @return true if we can send the packet + bool addContact( const QString& contact, const QString& group, bool requiresAuth = false ); + + //! Set up the stuff needed to remove a contact. + //! @return true if we can send the packet + bool removeContact( const QString& contact ); + + //! Set up the stuff needed to change groups + //! @return true if we can send the packet + bool changeGroup( const QString& contact, const QString& newGroup ); + + /* Group properties */ + + //! Add a new group to the SSI list + //! @return true if we can send the packet + bool addGroup( const QString& groupName ); + + //! Remove a group from the SSI list + //! @return true if we can send the packet + bool removeGroup( const QString& groupName ); + + //! Rename a group on the SSI list + //! @return true if we can send the packet + bool renameGroup( const QString& oldName, const QString& newName ); + + //! Add an item to the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool addItem( const SSI& item ); + + //! Remove an item from the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool removeItem( const SSI& item ); + + //! Modify an item on the SSI list + //! Should be used for other items we don't have explicit functions for + //! like icon hashs, privacy settings, non-icq contacts, etc. + bool modifyItem( const SSI& oldItem, const SSI& newItem ); + +protected: + virtual bool forMe( const Transfer* transfer ) const; + +private: + //! Handle the acknowledgement from the server + void handleSSIAck(); + + //! Construct and send the packet to send to the server + void sendSSIUpdate(); + + //! Helper function to change the group on the server + void changeGroupOnServer(); + + //! Update the SSI Manager with the new data + void updateSSIManager(); + + //! Helper function to free id on error + void freeIdOnError(); + + //! Send the SSI edit start packet + void sendEditStart(); + + //! Send the SSI edit end packet + void sendEditEnd(); + + void addItemToBuffer( Oscar::SSI item, Buffer* buffer ); + Oscar::SSI getItemFromBuffer( Buffer* buffer ) const; + + //! Handle server request to add item + void handleSSIAdd(); + + //! Handle server request to update item + void handleSSIUpdate(); + + //! Handle server request to remove item + void handleSSIRemove(); + +private: + SSI m_oldItem; + SSI m_newItem; + SSI m_groupItem; + OperationType m_opType; + OperationSubject m_opSubject; + WORD m_id; + SSIManager* m_ssiManager; + bool m_static; + +}; + +#endif + +//kate: tab-width 4; indent-mode csands diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.cpp b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp new file mode 100644 index 00000000..0be172e8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp @@ -0,0 +1,102 @@ +/* + Kopete Oscar Protocol + ssiparamstask.cpp - Get the SSI parameters so we can use them + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "ssiparamstask.h" +#include <kdebug.h> +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "oscartypes.h" +#include "oscarutils.h" +#include "ssimanager.h" + +using namespace Oscar; + +SSIParamsTask::SSIParamsTask(Task* parent): Task(parent) +{ +} + + +SSIParamsTask::~SSIParamsTask() +{ +} + + +bool SSIParamsTask::forMe(const Transfer* transfer) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0013 && st->snacSubtype() == 0x0003 ) + return true; + + return false; +} + +bool SSIParamsTask::take(Transfer* transfer) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleParamReply(); + setTransfer( 0 ); + return true; + } + + return false; +} + +void SSIParamsTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0013, 0x0002, 0x0000, client()->snacSequence() }; + + Buffer* buffer = new Buffer(); + buffer->addTLV16( 0x000B, 0x000F ); + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +void SSIParamsTask::handleParamReply() +{ + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Getting SSI parameters" << endl; + Buffer* buf = transfer()->buffer(); + //manually parse the TLV out of the packet, since we only want certain things + if ( buf->getWord() != 0x0004 ) + { + setError( -1, QString::null ); + return; //no TLV of type 0x0004, bad packet. do nothing. + } + else + { + buf->skipBytes( 2 ); //the tlv length + WORD maxContacts = buf->getWord(); + WORD maxGroups = buf->getWord(); + WORD maxVisible = buf->getWord(); + WORD maxInvisible = buf->getWord(); + buf->skipBytes( 20 ); + WORD maxIgnore = buf->getWord(); + client()->ssiManager()->setParameters( maxContacts, maxGroups, maxVisible, maxInvisible, maxIgnore ); + } + setSuccess( 0, QString::null ); +} + +// kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.h b/kopete/protocols/oscar/liboscar/ssiparamstask.h new file mode 100644 index 00000000..abf12aa2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/ssiparamstask.h @@ -0,0 +1,43 @@ +/* + Kopete Oscar Protocol + ssiparamstask.h - Get the SSI parameters so we can use them + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef SSIPARAMSTASK_H +#define SSIPARAMSTASK_H + +#include "task.h" + +/** +@author Kopete Developers +*/ +class SSIParamsTask : public Task +{ +public: + SSIParamsTask(Task* parent); + + ~SSIParamsTask(); + + virtual bool forMe(const Transfer* transfer) const; + virtual bool take(Transfer* transfer); + virtual void onGo(); + +private: + void handleParamReply(); +}; + +#endif + +// kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/stream.cpp b/kopete/protocols/oscar/liboscar/stream.cpp new file mode 100644 index 00000000..02967416 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/stream.cpp @@ -0,0 +1,31 @@ +/* + stream.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "stream.h" + +Stream::Stream(QObject *parent) +:QObject(parent) +{ +} + +Stream::~Stream() +{ +} + +#include "stream.moc" diff --git a/kopete/protocols/oscar/liboscar/stream.h b/kopete/protocols/oscar/liboscar/stream.h new file mode 100644 index 00000000..9fbacbda --- /dev/null +++ b/kopete/protocols/oscar/liboscar/stream.h @@ -0,0 +1,75 @@ +/* + stream.h - Kopete Groupwise Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on code copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qobject.h> + +#ifndef OSCAR_STREAM_H +#define OSCAR_STREAM_H + +class Transfer; + +class Stream : public QObject +{ + Q_OBJECT +public: + enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 }; + enum StreamCond { + GenericStreamError, + Conflict, + ConnectionTimeout, + InternalServerError, + InvalidFrom, +/*# InvalidXml, // not required*/ + PolicyViolation, + ResourceConstraint, + SystemShutdown + }; + + Stream(QObject *parent=0); + virtual ~Stream(); + + virtual void close()=0; + virtual int errorCondition() const=0; + virtual QString errorText() const=0; + + /** + * Are there any messages waiting to be read + */ + virtual bool transfersAvailable() const = 0; // adapt to messages + /** + * Read a message received from the server + */ + virtual Transfer* read() = 0; + + /** + * Send a message to the server + */ + virtual void write( Transfer *request) = 0; + + +signals: + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); //signals that there is a transfer ready to be read +// void stanzaWritten(); + void error(int); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/task.cpp b/kopete/protocols/oscar/liboscar/task.cpp new file mode 100644 index 00000000..2c7628d7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/task.cpp @@ -0,0 +1,291 @@ +/* + task.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include <qtimer.h> + +#include "connection.h" +#include "transfer.h" +#include "safedelete.h" +#include "buffer.h" +#include "task.h" + + +class Task::TaskPrivate +{ +public: + TaskPrivate() {} + + Q_UINT32 id; + bool success; + int statusCode; + QString statusString; + Connection* client; + bool insignificant, deleteme, autoDelete; + bool done; + Transfer* transfer; +}; + +Task::Task(Task *parent) +:QObject(parent) +{ + init(); + d->client = parent->client(); + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::Task(Connection* parent, bool) +:QObject(0) +{ + init(); + d->client = parent; + connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected())); +} + +Task::~Task() +{ + delete d->transfer; + delete d; +} + +void Task::init() +{ + d = new TaskPrivate; + d->success = false; + d->insignificant = false; + d->deleteme = false; + d->autoDelete = false; + d->done = false; + d->transfer = 0; + d->id = 0; +} + +Task *Task::parent() const +{ + return (Task *)QObject::parent(); +} + +Connection *Task::client() const +{ + return d->client; +} + +Transfer * Task::transfer() const +{ + return d->transfer; +} + +void Task::setTransfer( Transfer * transfer ) +{ + d->transfer = transfer; +} + +long Task::id() const +{ + return d->id; +} + +bool Task::success() const +{ + return d->success; +} + +int Task::statusCode() const +{ + return d->statusCode; +} + +const QString & Task::statusString() const +{ + return d->statusString; +} + +void Task::go(bool autoDelete) +{ + d->autoDelete = autoDelete; + + onGo(); +} + +bool Task::take( Transfer * transfer) +{ + const QObjectList *p = children(); + if(!p) + return false; + + // pass along the transfer to our children + QObjectListIt it(*p); + Task *t; + for(; it.current(); ++it) { + QObject *obj = it.current(); + if(!obj->inherits("Task")) + continue; + + t = static_cast<Task*>(obj); + + if(t->take( transfer )) + { + //qDebug( "Transfer ACCEPTED by: %s", t->className() ); + return true; + } + //else + //qDebug( "Transfer refused by: %s", t->className() ); + } + + return false; +} + +void Task::safeDelete() +{ + if(d->deleteme) + return; + + d->deleteme = true; + if(!d->insignificant) + SafeDelete::deleteSingle(this); +} + +void Task::onGo() +{ + qDebug( "ERROR: calling default NULL onGo() for this task, you should reimplement this!"); +} + +void Task::onDisconnect() +{ + if(!d->done) { + d->success = false; + d->statusCode = ErrDisc; + d->statusString = tr("Disconnected"); + + // delay this so that tasks that react don't block the shutdown + QTimer::singleShot(0, this, SLOT(done())); + } +} + +void Task::send( Transfer * request ) +{ + client()->send( request ); +} + +void Task::setSuccess(int code, const QString &str) +{ + if(!d->done) { + d->success = true; + d->statusCode = code; + d->statusString = str; + done(); + } +} + +void Task::setError(int code, const QString &str) +{ + if(!d->done) { + d->success = false; + d->statusCode = code; + d->statusString = str; + done(); + } +} + +void Task::done() +{ + debug("Task::done()"); + if(d->done || d->insignificant) + return; + d->done = true; + + if(d->deleteme || d->autoDelete) + d->deleteme = true; + + d->insignificant = true; + debug("emitting finished"); + finished(); + d->insignificant = false; + + if(d->deleteme) + SafeDelete::deleteSingle(this); +} + +void Task::clientDisconnected() +{ + onDisconnect(); +} + +Transfer* Task::createTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ) +{ + return new SnacTransfer( f, s, buffer ); +} + +Transfer* Task::createTransfer( struct FLAP f, Buffer* buffer ) +{ + return new FlapTransfer( f, buffer ); +} + +Transfer* Task::createTransfer( Buffer* buffer ) +{ + return new Transfer( buffer ); +} + + +// void Task::debug(const char *fmt, ...) +// { +// char *buf; +// QString str; +// int size = 1024; +// int r; +// +// do { +// buf = new char[size]; +// va_list ap; +// va_start(ap, fmt); +// r = vsnprintf(buf, size, fmt, ap); +// va_end(ap); +// +// if(r != -1) +// str = QString(buf); +// +// delete [] buf; +// +// size *= 2; +// } while(r == -1); +// +// debug(str); +// } + +void Task::debug(const QString &str) +{ + //black hole + Q_UNUSED( str ); + //client()->debug(QString("%1: ").arg(className()) + str); +} + +bool Task::forMe( const Transfer * transfer ) const +{ + Q_UNUSED( transfer ); + return false; +} + +void Task::setId( Q_UINT32 id ) +{ + d->id = id; +} + +#include "task.moc" + +//kate: tab-width 4; indent-mode csands; + diff --git a/kopete/protocols/oscar/liboscar/task.h b/kopete/protocols/oscar/liboscar/task.h new file mode 100644 index 00000000..e48e02de --- /dev/null +++ b/kopete/protocols/oscar/liboscar/task.h @@ -0,0 +1,116 @@ +/* + task.h - Kopete Oscar Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + Based on code copyright (c) 2004 SuSE Linux AG <http://www.suse.com> + + Based on Iris, Copyright (C) 2003 Justin Karneges + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef OSCAR_TASK_H +#define OSCAR_TASK_H + +#include <qobject.h> + +#include "oscartypes.h" + + +class QString; +class Buffer; +class Connection; +class Transfer; + +using namespace Oscar; + + +class Task : public QObject +{ + Q_OBJECT +public: + enum { ErrDisc }; + Task(Task *parent); + Task( Connection*, bool isRoot ); + virtual ~Task(); + + Task *parent() const; + Connection* client() const; + Transfer *transfer() const; + + long id() const; + void setId(); + + bool success() const; + int statusCode() const; + const QString & statusString() const; + + void go( bool autoDelete = false ); + + /** + * Allows a task to examine an incoming Transfer and decide whether to 'take' it + * for further processing. + */ + virtual bool take( Transfer* transfer ); + void safeDelete(); + + /** + * Direct setter for Tasks which don't have any fields + */ + void setTransfer( Transfer * transfer ); + +signals: + void finished(); + +protected: + virtual void onGo(); + virtual void onDisconnect(); + void setId( Q_UINT32 id ); + void send( Transfer * request ); + void setSuccess( int code=0, const QString &str="" ); + void setError( int code=0, const QString &str="" ); +// void debug( const char *, ... ); + void debug( const QString & ); + + /** + * Used in take() to check if the offered transfer is for this Task + * @return true if this Task should take the Transfer. Default impl always returns false. + */ + virtual bool forMe( const Transfer * transfer ) const; + + /** + * Creates a transfer with the given flap, snac, and buffer + */ + Transfer* createTransfer( FLAP f, SNAC s, Buffer* buffer ); + + /** + * Creates a transfer with the given flap and buffer + */ + Transfer* createTransfer( FLAP f, Buffer* buffer ); + + /** + * Creates a transfer with the given buffer + */ + Transfer* createTransfer( Buffer* buffer ); + +private slots: + void clientDisconnected(); + void done(); + +private: + void init(); + + class TaskPrivate; + TaskPrivate *d; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/Makefile.am b/kopete/protocols/oscar/liboscar/tests/Makefile.am new file mode 100644 index 00000000..9dc6b292 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = $(all_includes) $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/protocols/oscar/liboscar +METASOURCES = AUTO +check_PROGRAMS = kunittest clientstream_test logintest userinfotest ssigrouptest redirecttest ipaddrtest + +kunittest_SOURCES = main.cpp kunittest.cpp chatnavtests.cpp +kunittest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kunittest_LDADD = $(LIB_KDECORE) ../liboscar.la + +clientstream_test_SOURCES = clientstream_test.cpp +clientstream_test_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +logintest_SOURCES = logintest.cpp +logintest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +userinfotest_SOURCES = userinfotest.cpp +userinfotest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +ssigrouptest_SOURCES = ssigrouptest.cpp +ssigrouptest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +redirecttest_SOURCES = redirecttest.cpp +redirecttest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +ipaddrtest_SOURCES = ipaddrtest.cpp +ipaddrtest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la + +check: kunittest + @./kunittest 2>&1 | grep "tests:" diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp new file mode 100644 index 00000000..07a89f98 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp @@ -0,0 +1,305 @@ +/* + Kopete Oscar Protocol - Chat Navigation parsing tests + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "chatnavtests.h" + +#include <iostream> + +#include "buffer.h" +#include "oscartypeclasses.h" + +using namespace std; +using namespace Oscar; + + +ChatNavTests::ChatNavTests() +{ + m_buffer = 0; +} + + +ChatNavTests::~ChatNavTests() +{ +} + +void ChatNavTests::setupExchangeTestBuffer() +{ + delete m_buffer; + m_buffer = 0; + + m_buffer = new Buffer(); + //TLV 0x02 + m_buffer->addDWord(0x00020001); + m_buffer->addByte(0x03); + //TLV 0x03 + m_buffer->addDWord(0x0003003C); + m_buffer->addDWord(0x0001000a); + m_buffer->addDWord(0x00030001); + m_buffer->addDWord(0x14000400); + m_buffer->addDWord(0x02200000); + m_buffer->addDWord(0xC9000200); + m_buffer->addDWord(0x4400CA00); + m_buffer->addDWord(0x04000000); + m_buffer->addDWord(0x0000D000); + m_buffer->addDWord(0x0000D100); + m_buffer->addDWord(0x0207D000); + m_buffer->addDWord(0xD2000200); + m_buffer->addDWord(0x2F00D400); + m_buffer->addDWord(0x0000D500); + m_buffer->addDWord(0x010100DA); + m_buffer->addDWord(0x00020066); +} + +void ChatNavTests::setupRoomInfoTestBuffer() +{ + + delete m_buffer; + m_buffer = 0; + + m_buffer = new Buffer(); + //TLV 0x04 + m_buffer->addDWord(0x000400F8); + m_buffer->addWord(0x0004); //exchange + m_buffer->addByte(0x28); //cookie length + m_buffer->addByte(0x21); //start of cookie + m_buffer->addDWord(0x616F6C3A); + m_buffer->addDWord(0x2F2F3237); + m_buffer->addDWord(0x31393A31); + m_buffer->addDWord(0x302D342D); + m_buffer->addDWord(0x63686174); + m_buffer->addDWord(0x37343739); + m_buffer->addDWord(0x33333134); + m_buffer->addDWord(0x30313137); + m_buffer->addDWord(0x37393435); + m_buffer->addDWord(0x36363500); + m_buffer->addDWord(0x00020016); + m_buffer->addDWord(0x00660002); + m_buffer->addDWord(0x00000068); + m_buffer->addDWord(0x00040000); + m_buffer->addDWord(0x0000006A); + m_buffer->addDWord(0x00176368); + m_buffer->addDWord(0x61743734); + m_buffer->addDWord(0x37393333); + m_buffer->addDWord(0x31343031); + m_buffer->addDWord(0x31373739); + m_buffer->addDWord(0x34353636); + m_buffer->addDWord(0x35006D00); + m_buffer->addDWord(0x02000000); + m_buffer->addDWord(0x6E000200); + m_buffer->addDWord(0x00006F00); + m_buffer->addDWord(0x02000000); + m_buffer->addDWord(0x71000200); + m_buffer->addDWord(0x00007500); + m_buffer->addDWord(0x04000000); + m_buffer->addDWord(0x0000C900); + m_buffer->addDWord(0x02004000); + m_buffer->addDWord(0xCA000442); + m_buffer->addDWord(0xBEF90500); + m_buffer->addDWord(0xD0000200); + m_buffer->addDWord(0x0300D100); + m_buffer->addDWord(0x0207D000); + m_buffer->addDWord(0xD2000200); + m_buffer->addDWord(0x2600D300); + m_buffer->addDWord(0x17636861); + m_buffer->addDWord(0x74373437); + m_buffer->addDWord(0x39333331); + m_buffer->addDWord(0x34303131); + m_buffer->addDWord(0x37373934); + m_buffer->addDWord(0x35363635); + m_buffer->addDWord(0x00D40000); + m_buffer->addDWord(0x00D50001); + m_buffer->addDWord(0x0100D600); + m_buffer->addDWord(0x0875732D); + m_buffer->addDWord(0x61736369); + m_buffer->addDWord(0x6900D700); + m_buffer->addDWord(0x02656E00); + m_buffer->addDWord(0xD8000875); + m_buffer->addDWord(0x732D6173); + m_buffer->addDWord(0x63696900); + m_buffer->addDWord(0xD9000265); + m_buffer->addDWord(0x6E00DB00); + m_buffer->addDWord(0x0D756578); + m_buffer->addDWord(0x742F782D); + m_buffer->addDWord(0x616F6C72); + m_buffer->addDWord(0x746600DA); + m_buffer->addDWord(0x000200E8); +} + +void ChatNavTests::allTests() +{ + exchangeParsingTest(); + roominfoParsingTest(); +} + +void ChatNavTests::exchangeParsingTest() +{ + setupExchangeTestBuffer(); + Buffer testBuffer(*m_buffer); + CHECK( testBuffer.length() != 0, true ); + while ( testBuffer.length() != 0 ) + { + TLV t = testBuffer.getTLV(); + if ( t.type == 0x0002 ) + { +// cout << "Max concurrent rooms: " << t.data << endl; + } + + t = testBuffer.getTLV(); + if ( t.type == 0x0003 ) + { +// cout << "TLV of type 3 with length " << t.length << endl; + Buffer b(t.data); + WORD id = b.getWord(); + CHECK( id > 0, true ); + int tlvCount = b.getWord(); + int realCount = 0; +// cout << "Expecting " << tlvCount << " TLVs" << endl; + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); + CHECK( t.type != 0, true ); + switch (t.type) + { + case 0x02: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + break; + case 0x03: + CHECK( t.length == 1, true ); + CHECK( t.data.count() == 1, true ); + CHECK( t.data[0] > 0, true ); + break; + case 0x04: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + //CHECK( t.data[0] > 0, true ); + break; + case 0x05: + CHECK( t.length > 1, true ); + break; + case 0x06: + CHECK( t.length > 2, true ); + break; + case 0xCA: + CHECK( t.length == 4, true ); + break; + case 0xD1: + CHECK( t.length == 2, true ); + break; + case 0xD2: + CHECK( t.length == 2, true ); + break; + case 0xD3: + CHECK( t.length > 0, true ); + CHECK( t.data.count() == t.length, true ); + break; + case 0xD5: + CHECK( t.length == 1, true ); + break; + default: +// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase); +// cout << "unknown TLV type " << t.type << endl; +// cout.flags(origFlags); + break; + } + realCount++; + } + CHECK( tlvCount == realCount, true ); + } + CHECK( testBuffer.length() == 0, true ); + } +} + +void ChatNavTests::roominfoParsingTest() +{ + setupRoomInfoTestBuffer(); + Buffer testBuffer(*m_buffer); + CHECK( testBuffer.length() != 0, true ); + while ( testBuffer.length() != 0 ) + { + TLV t = testBuffer.getTLV(); + +// cout << "TLV of type " << t.type << " with length " << t.length << endl; + + + CHECK( t.type == 0x04, true ); + CHECK( t.length > 8, true ); + Buffer b( t.data ); + CHECK( b.getWord() > 0, true ); + BYTE cookieLength = b.getByte(); + b.skipBytes( cookieLength ); + CHECK( b.getWord() == 0, true ); + CHECK( b.getByte() > 0, true ); + int tlvCount = b.getWord(); + int realCount = 0; +// cout << "Expecting " << tlvCount << " TLVs" << endl; + while ( b.length() > 0 ) + { + TLV t = b.getTLV(); +// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase); +// cout << "TLV of type " << t.type << endl; +// cout.flags(origFlags); + CHECK( t.type != 0, true ); + switch (t.type) + { + case 0x02: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + break; + case 0x03: + CHECK( t.length == 1, true ); + CHECK( t.data.count() == 1, true ); + CHECK( t.data[0] > 0, true ); + break; + case 0x04: + CHECK( t.length == 2, true ); + CHECK( t.data.count() == 2, true ); + //CHECK( t.data[0] > 0, true ); + break; + case 0x05: + CHECK( t.length > 1, true ); + break; + case 0x06: + CHECK( t.length > 2, true ); + break; + case 0xCA: + CHECK( t.length == 4, true ); + break; + case 0xD1: + CHECK( t.length == 2, true ); + break; + case 0xD2: + CHECK( t.length == 2, true ); + break; + case 0xD3: + CHECK( t.length > 0, true ); + CHECK( t.data.count() == t.length, true ); + break; + case 0xD5: + CHECK( t.length == 1, true ); + break; + default: + break; + } + realCount++; + } + CHECK( tlvCount == realCount, true ); + } +} + +//kate: indent-mode csands; tab-width 4; + + diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.h b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h new file mode 100644 index 00000000..9899682f --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h @@ -0,0 +1,50 @@ +/* + Kopete Oscar Protocol - Chat Navigation parsing tests + Copyright (c) 2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef CHATNAVTESTS_H +#define CHATNAVTESTS_H + +#include "tester.h" + +class Buffer; + +/** +@author Kopete Developers +*/ +class ChatNavTests : public Tester +{ +public: + ChatNavTests(); + ~ChatNavTests(); + + void allTests(); + +// void limitsParsingTest(); + void exchangeParsingTest(); + void roominfoParsingTest(); +// void extRoomInfoParsingTest(); +// void memberListParsingTest(); +// void searchInfoParsingTest(); +// void createRoomParsingTest(); + + void setupExchangeTestBuffer(); + void setupRoomInfoTestBuffer(); + +private: + Buffer* m_buffer; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp new file mode 100644 index 00000000..59f392de --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp @@ -0,0 +1,49 @@ +//Licensed under the GNU General Public License + +#include "clientstream_test.h" + +ClientStreamTest::ClientStreamTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + //myConnector->setOptHostPort( "localhost", 8300 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + // notify when the transport layer is connected + connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + // notify and start sending + //connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) ); + + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +ClientStreamTest::~ClientStreamTest() +{ + delete myTestObject; + delete myConnector; +} + +void ClientStreamTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + myTestObject->connectToServer( server, true ); // fine up to here... +} + +void ClientStreamTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + ClientStreamTest a( argc, argv ); + a.exec(); + return 0; +} + +#include "clientstream_test.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.h b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h new file mode 100644 index 00000000..32a0e3a9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h @@ -0,0 +1,49 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef clientstream_test_h +#define clientstream_test_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" + +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class ClientStreamTest : public QApplication +{ +Q_OBJECT +public: + ClientStreamTest(int argc, char ** argv); + + ~ClientStreamTest(); + + bool isConnected(); + +public slots: + void slotDoTest(); + + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp new file mode 100644 index 00000000..4f4e8df2 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp @@ -0,0 +1,58 @@ +//Licensed under the GNU General Public License + +#include <iostream> +#include "ipaddrtest.h" +#include <qstring.h> + +using namespace std; +IPAddrTest::IPAddrTest(int argc, char ** argv) +: QApplication( argc, argv ) +{ +} + +IPAddrTest::~IPAddrTest() +{ +} + +bool IPAddrTest::testDottedDecimal() +{ + DWORD address = 1096652712; + return ( Oscar::getDottedDecimal( address ) == "65.93.151.168" ); +} + +bool IPAddrTest::testAllZeroDotted() +{ + DWORD address = 0; + return ( Oscar::getDottedDecimal( address ) == "0.0.0.0" ); +} + +bool IPAddrTest::testNumericalIP() +{ + QString address = "65.93.151.168"; + return ( Oscar::getNumericalIP( address ) == 1096652712 ); +} + +bool IPAddrTest::testAllZeroNumerical() +{ + QString address = "0.0.0.0"; + return ( Oscar::getNumericalIP( address ) == 0 ); +} + +void IPAddrTest::CheckTest(bool TestPassed) +{ + if ( TestPassed ) + cout << "passed" << endl; + else + cout << "failed" << endl; +} + +int main(int argc, char ** argv) +{ + IPAddrTest a( argc, argv ); + + a.CheckTest(a.testDottedDecimal()); + a.CheckTest(a.testNumericalIP()); + a.CheckTest(a.testAllZeroDotted() ); + a.CheckTest( a.testAllZeroNumerical() ); +} + diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h new file mode 100644 index 00000000..9452ad82 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h @@ -0,0 +1,35 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef IPADDRTEST_H +#define IPADDRTEST_H + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarutils.h" + +#define QT_FATAL_ASSERT 1 + +class IPAddrTest : public QApplication +{ +public: + IPAddrTest(int argc, char ** argv); + ~IPAddrTest(); + + bool testDottedDecimal(); + bool testNumericalIP(); + bool testAllZeroDotted(); + bool testAllZeroNumerical(); + + void CheckTest(bool TestPassed); +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.cpp b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp new file mode 100644 index 00000000..9f7ba693 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp @@ -0,0 +1,167 @@ +/** + * kunittest.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kunittest.h" + +#include "tester.h" +#include "chatnavtests.h" + +#include <qapplication.h> +#include <qtimer.h> + +#include <iostream> +using namespace std; + +void KUnitTest::registerTests() +{ + ADD_TEST( ChatNavTests ); +// ADD_TEST( SampleTest ); +// ADD_TEST( OnePassTest ); +// ADD_TEST( TwoPassTest ); +// ADD_TEST( MultiFailTest ); +// ADD_TEST( ExpectedFailureTest ); +// ADD_TEST( UnexpectedPassTest ); +// ADD_TEST( OnlyUnexpectedPassTest ); +// ADD_TEST( SkipLogTest ); +// ADD_TEST( SkipWithFailTest ); +} + +KUnitTest::KUnitTest() +{ + QTimer::singleShot( 0, this, SLOT(checkRun()) ); + + m_tests.setAutoDelete( TRUE ); +// m_qtests.setAutoDelete( TRUE ); + + registerTests(); +} + +void KUnitTest::checkRun() +{ +// if ( m_qtests.isEmpty() ) +// qApp->exit(); +} + +int KUnitTest::runTests() +{ + int globalSteps = 0; + int globalPasses = 0; + int globalFails = 0; + int globalXFails = 0; + int globalXPasses = 0; + int globalSkipped = 0; + + cout << "# Running normal tests... #" << endl << endl; + QAsciiDictIterator<Tester> it( m_tests ); + + for( ; it.current(); ++it ) { + Tester* test = it.current(); + test->allTests(); + cout << it.currentKey() << " - "; + int numPass = test->testsFinished() - ( test->errorList().count() + test->xfailList().count() + test->skipList().count() ); + int numFail = test->errorList().count() + test->xfailList().count(); + int numXFail = test->xfailList().count(); + int numXPass = test->xpassList().count(); + int numSkip = test->skipList().count(); + + globalSteps += test->testsFinished(); + globalPasses += numPass; + globalFails += numFail; + globalXFails += numXFail; + globalXPasses += numXPass; + globalSkipped += numSkip; + + cout << numPass << " test" << ( ( 1 == numPass )?"":"s") << " passed"; + if ( 0 < test->xpassList().count() ) { + cout << " (" << numXPass << " unexpected pass" << ( ( 1 == numXPass )?"":"es") << ")"; + } + cout << ", " << numFail << " test" << ( ( 1 == numFail )?"":"s") << " failed"; + if ( 0 < numXFail ) { + cout << " (" << numXFail << " expected failure" << ( ( 1 == numXFail )?"":"s") << ")"; + } + if ( 0 < numSkip ) { + cout << "; also " << numSkip << " skipped"; + } + cout << endl; + + if ( 0 < numXPass ) { + cout << " Unexpected pass" << ( ( 1 == numXPass )?"":"es") << ":" << endl; + QStringList list = test->xpassList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < (numFail - numXFail) ) { + cout << " Unexpected failure" << ( ( 1 == numFail )?"":"s") << ":" << endl; + QStringList list = test->errorList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < numXFail ) { + cout << " Expected failure" << ( ( 1 == numXFail)?"":"s") << ":" << endl; + QStringList list = test->xfailList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + if ( 0 < numSkip ) { + cout << " Skipped test" << ( ( 1 == numSkip )?"":"s") << ":" << endl; + QStringList list = test->skipList(); + for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) { + cout << "\t" << (*itr).latin1() << endl; + } + } + cout << endl; + } + + cout << "# Done with normal tests:" << endl; + cout << " Total test cases: " << m_tests.count() << endl; + cout << " Total test steps : " << globalSteps << endl; + cout << " Total passed test steps (including unexpected) : " << globalPasses << endl; + cout << " Total unexpected passed test steps : " << globalXPasses << endl; + cout << " Total failed test steps (including expected) : " << globalFails << endl; + cout << " Total expected failed test steps : " << globalXFails << endl; + cout << " Total skipped test steps : " << globalSkipped << endl; + + return m_tests.count(); +} + +//void KUnitTest::addTester( QTester *test ) +//{ +// m_qtests.insert( test, test ); +// connect( test, SIGNAL(destroyed(QObject*)), +// SLOT(qtesterDone(QObject* )) ); +//} + +void KUnitTest::qtesterDone( QObject *obj ) +{ +// m_qtests.remove( obj ); +// if ( m_qtests.isEmpty() ) +// qApp->quit(); +} + +#include "kunittest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.h b/kopete/protocols/oscar/liboscar/tests/kunittest.h new file mode 100644 index 00000000..5148a82a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/kunittest.h @@ -0,0 +1,71 @@ +/** + * kunittest.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef KUNITTEST_H +#define KUNITTEST_H + +#include "tester.h" + +#include <qobject.h> +#include <qasciidict.h> +#include <qptrdict.h> + +#define ADD_TEST(x) addTester( #x, new x ) +#define ADD_QTEST(x) addTester( new x ) + +class KUnitTest : public QObject +{ + Q_OBJECT +public: + KUnitTest(); + + int runTests(); +public: + void addTester( const char *name, Tester* test ) + { + m_tests.insert( name, test ); + } +// void addTester( QTester *test ); + +private slots: + void qtesterDone( QObject *obj ); + void checkRun(); + +private: + void registerTests(); + +private: + QAsciiDict<Tester> m_tests; +// QPtrDict<QTester> m_qtests; + int globalTests; + int globalPasses; + int globalFails; + int globalXFails; + int globalXPasses; + int globalSkipped; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.cpp b/kopete/protocols/oscar/liboscar/tests/logintest.cpp new file mode 100644 index 00000000..6dbc9646 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/logintest.cpp @@ -0,0 +1,56 @@ +//Licensed under the GNU General Public License + +#include "logintest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +#include "logintest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.h b/kopete/protocols/oscar/liboscar/tests/logintest.h new file mode 100644 index 00000000..898a3d99 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/logintest.h @@ -0,0 +1,53 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/main.cpp b/kopete/protocols/oscar/liboscar/tests/main.cpp new file mode 100644 index 00000000..49966924 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/main.cpp @@ -0,0 +1,35 @@ +/** + * main.cpp + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "kunittest.h" + +int main( int argc, char** argv ) +{ + Q_UNUSED( argc ); + Q_UNUSED( argv ); + KUnitTest tests; + return tests.runTests(); +} diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp new file mode 100644 index 00000000..a220e13e --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp @@ -0,0 +1,117 @@ +//Licensed under the GNU General Public License + +#include <iostream> +#include "redirecttest.h" +#include <qcstring.h> + +using namespace std; +RedirectTest::RedirectTest(int argc, char ** argv) +: QApplication( argc, argv ), + m_transfer(0), + m_task(0) +{ + m_root = new Task(0, true); +} + +RedirectTest::~RedirectTest() +{ + delete m_root; +} + +void RedirectTest::Setup() +{ + m_transfer = new SnacTransfer; + m_task = new ServerRedirectTask( m_root ); +} + +void RedirectTest::Teardown() +{ + delete m_task; + m_task = 0; + m_transfer = 0; +} + +bool RedirectTest::testHandleRedirect() +{ + Buffer* b = SetupBuffer(0x0010, "REDF$"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return m_task->handleRedirect(); +} + +bool RedirectTest::testInvalidService() +{ + Buffer* b = SetupBuffer(0x4321, "REDF$"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return !m_task->handleRedirect(); +} + +bool RedirectTest::testInvalidCookie() +{ + Buffer* b = SetupBuffer(0x0010, ""); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + return !m_task->handleRedirect(); +} + +bool RedirectTest::testCookieIsSet() +{ + Buffer* b = SetupBuffer(0x0010, "grouch"); + m_transfer->setBuffer(b); + + m_task->setService(0x0010); + m_task->setTransfer(m_transfer); + m_task->handleRedirect(); + + return !m_task->cookie().isEmpty(); +} + +Buffer* RedirectTest::SetupBuffer(WORD Service, QString Cookie) +{ + Buffer* b = new Buffer; + b->addTLV16(0x000D, Service); + b->addWord(0x0005); + b->addWord(0x0010); + b->addString("65.86.43.45:5190", 16); + b->addWord(0x0006); + b->addWord(Cookie.length()); + b->addString(Cookie.latin1(), Cookie.length()); + return b; +} + +void RedirectTest::CheckTest(bool TestPassed) +{ + if ( TestPassed ) + cout << "passed" << endl; + else + cout << "failed" << endl; +} + +int main(int argc, char ** argv) +{ + RedirectTest a( argc, argv ); + + a.Setup(); + a.CheckTest(a.testHandleRedirect()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testInvalidService()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testInvalidCookie()); + a.Teardown(); + + a.Setup(); + a.CheckTest(a.testCookieIsSet()); + a.Teardown(); +} + diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.h b/kopete/protocols/oscar/liboscar/tests/redirecttest.h new file mode 100644 index 00000000..eda5d67a --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.h @@ -0,0 +1,51 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef RedirectTest_h +#define RedirectTest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "transfer.h" +#include "oscartypes.h" +#include "serverredirecttask.h" +#include "task.h" + +#define QT_FATAL_ASSERT 1 + +class RedirectTest : public QApplication +{ +public: + RedirectTest(int argc, char ** argv); + ~RedirectTest(); + + bool testHandleRedirect(); + bool testInvalidService(); + bool testInvalidCookie(); + bool testCookieIsSet(); + + void Setup(); + void Teardown(); + + void CheckTest(bool TestPassed); + +private: + //Helper functions + Buffer* SetupBuffer(WORD Service, QString Cookie); + + Task *m_root; + SnacTransfer * m_transfer; + ServerRedirectTask* m_task; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp new file mode 100644 index 00000000..a1a9e754 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp @@ -0,0 +1,73 @@ +//Licensed under the GNU General Public License + +#include "ssigrouptest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + QTimer::singleShot( 10000, this, SLOT(runAddGroupTest() ) ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +void LoginTest::runAddGroupTest() +{ + qDebug( "running ssi group add test" ); + QString group = QString::fromLatin1( "dummygroup" ); + myClient->addGroup( group ); + QTimer::singleShot( 5000, this, SLOT( runDelGroupTest() ) ); +} + +void LoginTest::runDelGroupTest() +{ + qDebug( "running ssi group del test" ); + QString group = QString::fromLatin1( "dummygroup" ); + myClient->removeGroup( group ); +} + + +#include "ssigrouptest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h new file mode 100644 index 00000000..361c053b --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h @@ -0,0 +1,54 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + void runAddGroupTest(); + void runDelGroupTest(); + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.cpp b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp new file mode 100644 index 00000000..d8e05b36 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp @@ -0,0 +1,111 @@ +//Licensed under the GNU General Public License + +#include "ssitest.h" + +#include <qstring.h> + +SSITest::SSITest(int argc, char ** argv) : QApplication( argc, argv ) +{ + m_manager = new SSIManager(this); + + testIt(); + +} + +SSITest::~SSITest() +{ + delete m_manager; +} + +void SSITest::testIt() +{ + QPtrList<TLV> tlvs; + + //add three groups + SSI *ssi = new SSI( "FirstGroup", 1, 1, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + ssi = new SSI( "SecondGroup", 2, 2, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + ssi = new SSI( "ThirdGroup", 3, 3, ROSTER_GROUP, tlvs); + m_manager->newGroup(ssi); + + //add six contacts + ssi = new SSI( "FirstContact", 1, 4, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "SecondContact", 1, 5, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "ThirdContact", 1, 6, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "FourthContact", 2, 7, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "FifthContact", 2, 8, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + ssi = new SSI( "SixthContact", 3, 9, ROSTER_CONTACT, tlvs); + m_manager->newContact(ssi); + + //try to find a group by name + ssi = m_manager->findGroup("SecondGroup"); + if ( ssi ) + qDebug( QString("Found group SecondGroup with gid=%1").arg( ssi->gid() ).latin1()); + else + qDebug( "Oops, group SecondGroup not found" ); + + //try to find a group by gid + ssi = m_manager->findGroup( 3 ); + if ( ssi ) + qDebug( QString("Found group 3 with name=%1").arg( ssi->name() ).latin1() ); + else + qDebug( "Oops, group 3 not found" ); + + //try to find a contact by name + ssi = m_manager->findContact("ThirdContact"); + if ( ssi ) + qDebug( QString( "Found contact ThirdContact with gid=%1" ).arg( ssi->gid() ).latin1() ); + else + qDebug( "Oops, contact ThirdContact not found" ); + + //try to find a contact using the name and the group name + ssi = m_manager->findContact("FourthContact","SecondGroup"); + if ( ssi ) + qDebug( QString("Found contact FourthContact with bid=%1").arg( ssi->bid() ).latin1() ); + else + qDebug( "Oops, contact FourthContact not found" ); + + + //try to find an invalid group + ssi = m_manager->findGroup("InvalidGroup"); + if ( !ssi ) + qDebug( "Good! It has detected the group is invalid :)" ); + + //contacts from a group + QValueList<SSI*> list = m_manager->contactsFromGroup("FirstGroup"); + QValueList<SSI*>::iterator it; + qDebug( "Contacts from group FirtsGroup:" ); + for ( it = list.begin(); it != list.end(); ++it) + qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() ); + + //the group list + QValueList<SSI*> list2 = m_manager->groupList(); + qDebug( "Group list:" ); + for ( it = list2.begin(); it != list2.end(); ++it) + qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() ); + + //remove a group - this shouldn't report any debug line + m_manager->removeGroup( "SecondGroup" ); + +} + +int main(int argc, char ** argv) +{ + SSITest a( argc, argv ); + a.exec(); +} + +#include "ssitest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.h b/kopete/protocols/oscar/liboscar/tests/ssitest.h new file mode 100644 index 00000000..19206465 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/ssitest.h @@ -0,0 +1,34 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef ssitest_h +#define ssitest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "ssimanager.h" + +#define QT_FATAL_ASSERT 1 + +class SSITest : public QApplication +{ +Q_OBJECT +public: + SSITest(int argc, char ** argv); + + ~SSITest(); + + void testIt(); +private: + SSIManager *m_manager; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/tester.h b/kopete/protocols/oscar/liboscar/tests/tester.h new file mode 100644 index 00000000..2cb1f3af --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/tester.h @@ -0,0 +1,121 @@ +/** + * tester.h + * + * Copyright (C) 2004 Zack Rusin <zack@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TESTER_H +#define TESTER_H + +#include <qstringlist.h> + +#define CHECK( x, y ) check( __FILE__, __LINE__, #x, x, y, false ) +#define XFAIL( x, y ) check( __FILE__, __LINE__, #x, x, y, true ) +#define SKIP( x ) skip( __FILE__, __LINE__, #x ) + +class Tester +{ +public: + Tester() + : m_tests( 0 ) + { + } + virtual ~Tester() {} + +public: + virtual void allTests() = 0; + +public: + int testsFinished() const { + return m_tests; + } + + QStringList errorList() const { + return m_errorList; + } + + QStringList xfailList() const { + return m_xfailList; + } + + QStringList xpassList() const { + return m_xpassList; + } + + QStringList skipList() const { + return m_skipList; + } + + void skip( const char *file, int line, QString msg ) + { + QString skipEntry; + QTextStream ts( &skipEntry, IO_WriteOnly ); + ts << file << "["<< line <<"]: " << msg; + m_skipList.append( skipEntry ); + + ++m_tests; + } + +protected: + template<typename T> + void check( const char *file, int line, const char *str, + const T &result, const T &expectedResult, + bool expectedFailure ) + { + if ( result != expectedResult ) { + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]:" + <<" failed on \""<< str <<"\"" + << "\n\t\t result = '" + << result + << "', expected = '"<< expectedResult<<"'"; + if ( expectedFailure ) { + m_xfailList.append( error ); + } else { + m_errorList.append( error ); + } + } else { + // then the test passed, but we want to record it if + // we were expecting a failure + if (expectedFailure) { + QString error; + QTextStream ts( &error, IO_WriteOnly ); + ts << file << "["<< line <<"]:" + <<" unexpectedly passed on \"" + << str <<"\""; + m_xpassList.append( error ); + } + } + ++m_tests; + } + +private: + QStringList m_errorList; + QStringList m_xfailList; + QStringList m_xpassList; + QStringList m_skipList; + int m_tests; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp new file mode 100644 index 00000000..72ef5acb --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp @@ -0,0 +1,67 @@ +//Licensed under the GNU General Public License + +#include "userinfotest.h" + +LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv ) +{ + // set up client stream + myConnector = new KNetworkConnector( 0 ); + myConnector->setOptHostPort( "login.oscar.aol.com", 5190 ); + myTestObject = new ClientStream( myConnector, myConnector); + + // notify when the transport layer is connected + //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) ); + myClient = new Client(); + + myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" ); + myConnection->setClient( myClient ); + // do test once the event loop is running + QTimer::singleShot( 0, this, SLOT( slotDoTest() ) ); + connected = false; +} + +LoginTest::~LoginTest() +{ + delete myTestObject; + delete myConnector; + delete myClient; +} + +void LoginTest::slotDoTest() +{ + QString server = QString::fromLatin1("login.oscar.aol.com"); + // connect to server + qDebug( "connecting to server "); + + myClient->setIsIcq( true ); + myClient->start( server, 5190, "userid", "password" ); + myClient->connectToServer( myConnection, server, true ); + //QObject::connect( myClient, SIGNAL( userIsOnline( const QString& ) ), this, SLOT( runUserInfoTest())); + //QTimer::singleShot( 6000, this, SLOT(runUserInfoTest() ) ); + connected = true; +} + +void LoginTest::slotConnected() +{ + qDebug( "connection is up"); + connected = true; +} + +int main(int argc, char ** argv) +{ + LoginTest a( argc, argv ); + a.exec(); + if ( !a.isConnected() ) + return 0; +} + +void LoginTest::runUserInfoTest() +{ + qDebug( "running user info test" ); + QString contact = QString::fromLatin1( "userid" ); + myClient->requestFullInfo( contact ); + +} + + +#include "userinfotest.moc" diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.h b/kopete/protocols/oscar/liboscar/tests/userinfotest.h new file mode 100644 index 00000000..433a6c48 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.h @@ -0,0 +1,53 @@ +// +// C++ Implementation: clientstream_test +// +// Description: +// +// +// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004 +// Licensed under the GNU General Public License + +#ifndef logintest_h +#define logintest_h + +#include <qglobal.h> +#include <qapplication.h> +#include <qtimer.h> + +#include "oscarclientstream.h" +#include "oscarconnector.h" +#include "client.h" +#include "connection.h" +#include "coreprotocol.h" + +#define QT_FATAL_ASSERT 1 + +class LoginTest : public QApplication +{ +Q_OBJECT +public: + LoginTest(int argc, char ** argv); + + ~LoginTest(); + + bool isConnected() { return connected; } + +public slots: + void slotDoTest(); + void runUserInfoTest(); + void slotConnected(); + + //void slotWarning(int warning); + + //void slotsend(int layer); + +private: + KNetworkConnector *myConnector; + ClientStream *myTestObject; + Client* myClient; + Connection* myConnection; + + bool connected; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/transfer.cpp b/kopete/protocols/oscar/liboscar/transfer.cpp new file mode 100644 index 00000000..b442a97c --- /dev/null +++ b/kopete/protocols/oscar/liboscar/transfer.cpp @@ -0,0 +1,367 @@ +/* + transfer.cpp - Kopete Groupwise Protocol + + Copyright (c) 2004 Matt Rogers <mattr@kde.org> + + Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "transfer.h" +#include <ctype.h> +#include <qdeepcopy.h> +#include <kdebug.h> + +Transfer::Transfer() +{ + m_isBufferValid = false; +} + +Transfer::Transfer( Buffer* buf ) +{ + m_buffer = buf; + m_isBufferValid = true; +} + +Transfer::TransferType Transfer::type() const +{ + return Transfer::RawTransfer; +} + +QByteArray Transfer::toWire() +{ + m_wireFormat.duplicate( m_buffer->buffer(), m_buffer->length() ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +Transfer::~Transfer() +{ + delete m_buffer; + m_buffer = 0; +} + +void Transfer::setBuffer( Buffer* buffer ) +{ + m_buffer = buffer; +} + +Buffer* Transfer::buffer() +{ + return m_buffer; +} + +const Buffer* Transfer::buffer() const +{ + return m_buffer; +} + +bool Transfer::dataValid() const +{ + return m_isBufferValid; +} + +QString Transfer::toString() const +{ + // line format: + //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........| + + int i = 0; + QString output = "\n"; + QString hex, ascii; + + QByteArray::ConstIterator it; + QByteArray::ConstIterator end = m_wireFormat.end(); + for ( it = m_wireFormat.begin(); it != end; ++it ) + { + i++; + + unsigned char c = static_cast<unsigned char>(*it); + + if(c < 0x10) + hex.append("0"); + hex.append(QString("%1 ").arg(c, 0, 16)); + + ascii.append(isprint(c) ? c : '.'); + + if (i == 16) + { + output += hex + " |" + ascii + "|\n"; + i=0; + hex=QString::null; + ascii=QString::null; + } + } + + if(!hex.isEmpty()) + output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|'; + output.append('\n'); + + return output; +} + +void Transfer::populateWireBuffer( int offset, const QByteArray& buffer ) +{ + int j; + for ( uint i = 0; i < buffer.size(); ++i ) + { + j = i + offset; + m_wireFormat[j] = buffer[i]; + } +} + + +FlapTransfer::FlapTransfer() + : Transfer() +{ + m_isFlapValid = false; +} + +FlapTransfer::FlapTransfer( struct FLAP f, Buffer* buffer ) + : Transfer( buffer ) +{ + m_flapChannel = f.channel; + m_flapSequence = f.sequence; + m_flapLength = f.length; + + if ( m_flapChannel == 0 || m_flapLength < 6 ) + m_isFlapValid = false; + else + m_isFlapValid = true; + +} + +FlapTransfer::FlapTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len ) + : Transfer( buffer ) +{ + m_flapChannel = chan; + m_flapSequence = seq; + m_flapLength = len; + + if ( m_flapChannel == 0 || m_flapLength < 6 ) + m_isFlapValid = false; + else + m_isFlapValid = true; +} + +FlapTransfer::~FlapTransfer() +{ + +} + +QByteArray FlapTransfer::toWire() +{ + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer length is " << m_buffer.length() << endl; + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer is " << m_buffer.toString() << endl; + + m_wireFormat.truncate( 0 ); + QByteArray useBuf; + useBuf.duplicate( m_buffer->buffer(), m_buffer->length() ); + m_flapLength = useBuf.size(); + m_wireFormat.resize( 6 + m_flapLength ); + m_wireFormat[0] = 0x2A; + m_wireFormat[1] = m_flapChannel; + m_wireFormat[2] = (m_flapSequence & 0xFF00) >> 8; + m_wireFormat[3] = (m_flapSequence & 0x00FF); + m_wireFormat[4] = (m_flapLength & 0xFF00) >> 8; + m_wireFormat[5] = (m_flapLength & 0x00FF); + + //deepcopy the high-level buffer to the wire format buffer + populateWireBuffer( 6, useBuf ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +void FlapTransfer::setFlapChannel( BYTE channel ) +{ + if ( channel != 0 ) + { + m_flapChannel = channel; + m_isFlapValid = true; + } +} + + +BYTE FlapTransfer::flapChannel() const +{ + return m_flapChannel; +} + + +void FlapTransfer::setFlapSequence( WORD seq ) +{ + m_flapSequence = seq; +} + + +WORD FlapTransfer::flapSequence() const +{ + return m_flapSequence; +} + +void FlapTransfer::setFlapLength( WORD len ) +{ + m_flapLength = len; +} + +WORD FlapTransfer::flapLength() const +{ + return m_flapLength; +} + +bool FlapTransfer::flapValid() const +{ + return m_isFlapValid; +} + +Transfer::TransferType FlapTransfer::type() const +{ + return Transfer::FlapTransfer; +} + + + +SnacTransfer::SnacTransfer() + : FlapTransfer() +{ + m_isSnacValid = false; +} + + +SnacTransfer::SnacTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len, WORD service, + WORD subtype, WORD flags, DWORD reqId ) + : FlapTransfer( buffer, chan, seq, len ) +{ + m_snacService = service; + m_snacSubtype = subtype; + m_snacFlags = flags; + m_snacReqId = reqId; + + if ( m_snacService == 0 || m_snacSubtype == 0 ) + m_isSnacValid = false; + else + m_isSnacValid = true; + +} + +SnacTransfer::SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ) + : FlapTransfer( f, buffer ) +{ + m_snacService = s.family; + m_snacSubtype = s.subtype; + m_snacFlags = s.flags; + m_snacReqId = s.id; + + if ( m_snacService == 0 || m_snacSubtype == 0 ) + m_isSnacValid = false; + else + m_isSnacValid = true; +} + +SnacTransfer::~SnacTransfer() +{ + +} + +QByteArray SnacTransfer::toWire() +{ + + m_wireFormat.truncate( 0 ); + QByteArray useBuf; + useBuf.duplicate( m_buffer->buffer(), m_buffer->length() ); + setFlapLength( useBuf.size() + 10 ); + m_wireFormat.resize( 16 + useBuf.size() ); + + //Transfer the flap - 6 bytes + m_wireFormat[0] = 0x2A; + m_wireFormat[1] = flapChannel(); + m_wireFormat[2] = (flapSequence() & 0xFF00) >> 8; + m_wireFormat[3] = (flapSequence() & 0x00FF); + m_wireFormat[4] = (flapLength() & 0xFF00) >> 8; + m_wireFormat[5] = (flapLength() & 0x00FF); + + //Transfer the Snac - 10 bytes + m_wireFormat[6] = (m_snacService & 0xFF00) >> 8; + m_wireFormat[7] = (m_snacService & 0x00FF); + m_wireFormat[8] = (m_snacSubtype & 0xFF00) >> 8; + m_wireFormat[9] = (m_snacSubtype & 0x00FF); + m_wireFormat[10] = (m_snacFlags & 0xFF00) >> 8; + m_wireFormat[11] = (m_snacFlags & 0x00FF); + m_wireFormat[12] = (m_snacReqId & 0xFF000000) >> 24; + m_wireFormat[13] = (m_snacReqId & 0x00FF0000) >> 16; + m_wireFormat[14] = (m_snacReqId & 0x0000FF00) >> 8; + m_wireFormat[15] = (m_snacReqId & 0x000000FF); + + //deepcopy the high-level buffer to the wire format buffer + populateWireBuffer( 16, useBuf ); + QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat ); + return wire; +} + +Transfer::TransferType SnacTransfer::type() const +{ + return Transfer::SnacTransfer; +} + +bool SnacTransfer::snacValid() const +{ + return m_isSnacValid; +} + +void SnacTransfer::setSnacService( WORD service ) +{ + m_snacService = service; +} + +WORD SnacTransfer::snacService() const +{ + return m_snacService; +} + +void SnacTransfer::setSnacSubtype( WORD subtype ) +{ + m_snacSubtype = subtype; +} + +WORD SnacTransfer::snacSubtype() const +{ + return m_snacSubtype; +} + +void SnacTransfer::setSnacFlags( WORD flags ) +{ + m_snacFlags = flags; +} + +WORD SnacTransfer::snacFlags() const +{ + return m_snacFlags; +} + +void SnacTransfer::setSnacRequest( DWORD id ) +{ + m_snacReqId = id; +} + +DWORD SnacTransfer::snacRequest() const +{ + return m_snacReqId; +} + +SNAC SnacTransfer::snac() const +{ + SNAC s = { m_snacService, m_snacSubtype, m_snacFlags, m_snacReqId }; + return s; +} + + diff --git a/kopete/protocols/oscar/liboscar/transfer.h b/kopete/protocols/oscar/liboscar/transfer.h new file mode 100644 index 00000000..f42b1e83 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/transfer.h @@ -0,0 +1,169 @@ +/* + transfer.h - Kopete Groupwise Protocol + + Copyright (c) 2004 SUSE Linux AG http://www.suse.com + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef TRANSFER_H +#define TRANSFER_H + +#include "oscartypes.h" +#include "buffer.h" + + +using namespace Oscar; + +class Transfer +{ +public: + enum TransferType { RawTransfer, FlapTransfer, SnacTransfer, DIMTransfer, FileTransfer }; + Transfer(); + Transfer( Buffer* buf ); + virtual ~Transfer(); + + virtual TransferType type() const; + + virtual QByteArray toWire(); + + //! Set the data buffer + void setBuffer( Buffer* buffer ); + + //! Get the data buffer + Buffer* buffer(); + + const Buffer* buffer() const; //used for const transfer objects + + //! Get the validity of the data after the flap header + bool dataValid() const; + + QString toString() const; + + void populateWireBuffer( int offset, const QByteArray& buffer ); + +protected: + //! The wire-format representation of our buffer + QByteArray m_wireFormat; + + //! The high-level representation of our data + Buffer* m_buffer; + +private: + + //! Flag to indicate whether we're a valid transfer + bool m_isBufferValid; + +}; + +class FlapTransfer : public Transfer +{ +public: + + FlapTransfer( Buffer* buffer, BYTE chan = 0, WORD seq = 0, WORD len = 0 ); + FlapTransfer( FLAP f, Buffer* buffer ); + FlapTransfer(); + virtual ~FlapTransfer(); + + virtual TransferType type() const; + virtual QByteArray toWire(); + + + //! Set the FLAP channel + void setFlapChannel( BYTE channel ); + + //! Get the FLAP channel + BYTE flapChannel() const; + + //! Set the FLAP sequence + void setFlapSequence( WORD seq ); + + //! Get the FLAP sequence + WORD flapSequence() const; + + //! Set the length of the data after the FLAP + void setFlapLength( WORD len ); + + //! Get the length of the data after the FLAP + WORD flapLength() const; + + //! Get the validity of the FLAP header + bool flapValid() const; + +private: + BYTE m_flapChannel; + WORD m_flapSequence; + WORD m_flapLength; + + bool m_isFlapValid; + +}; + +/** +@author Matt Rogers +*/ +class SnacTransfer : public FlapTransfer +{ +public: + + /*SnacTransfer();*/ + SnacTransfer( Buffer*, BYTE chan = 0, WORD seq = 0, WORD len = 0, WORD service = 0, + WORD subtype = 0, WORD flags = 0, DWORD reqId = 0 ); + SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer ); + SnacTransfer(); + virtual ~SnacTransfer(); + + TransferType type() const; + virtual QByteArray toWire(); + + + //! Set the SNAC service + void setSnacService( WORD service ); + + //! Get the SNAC service + WORD snacService() const; + + //! Set the SNAC subtype + void setSnacSubtype( WORD subtype ); + + //! Get the SNAC subtype + WORD snacSubtype() const; + + //! Set the SNAC flags + void setSnacFlags( WORD flags ); + + //! Get the SNAC flags + WORD snacFlags() const; + + //! Set the SNAC request id + void setSnacRequest( DWORD id ); + + //! Get the SNAC request id + DWORD snacRequest() const; + + //! Get the validity of the SNAC header + bool snacValid() const; + + //! Get the SNAC header + SNAC snac() const; + +private: + + WORD m_snacService; + WORD m_snacSubtype; + WORD m_snacFlags; + WORD m_snacReqId; + + bool m_isSnacValid; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.cpp b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp new file mode 100644 index 00000000..76503116 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp @@ -0,0 +1,124 @@ +/* + typingnotifytask.h - Send/Recieve typing notifications + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "typingnotifytask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + + + + +TypingNotifyTask::TypingNotifyTask( Task* parent ) +: Task( parent ) +{ + m_notificationType = 0x0000; +} + +TypingNotifyTask::~TypingNotifyTask() +{ +} + +bool TypingNotifyTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0004 && st->snacSubtype() == 0x0014 ) + return true; + else + return false; +} + +bool TypingNotifyTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + handleNotification(); + setTransfer( 0 ); + return true; + } + + return false; +} + +void TypingNotifyTask::onGo() +{ + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0004, 0x0014, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer(); + + //notification id cookie. it's a quad-word + b->addDWord( 0x00000000 ); + b->addDWord( 0x00000000 ); + + b->addWord( 0x0001 ); //mtn messages are always sent as type 1 messages + + b->addBUIN( m_contact.latin1() ); + + b->addWord( m_notificationType ); + + Transfer* t = createTransfer( f, s, b ); + send( t ); + + setSuccess( 0, QString::null ); +} + +void TypingNotifyTask::handleNotification() +{ + /* NB ICQ5 (windows) seems to only send 0x0002 and 0x0001, so I'm interpreting 0x001 as typing finished here - Will */ + Buffer* b = transfer()->buffer(); + + //I don't care about the QWORD or the channel + b->skipBytes( 10 ); + + QString contact( b->getBUIN() ); + + Q_UINT32 word = b->getWord(); + switch ( word ) + { + case 0x0000: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has finished typing" << endl; + emit typingFinished( contact ); + break; + case 0x0001: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has typed a word" << endl; + emit typingFinished( contact ); + break; + case 0x0002: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has started typing" << endl; + emit typingStarted( contact ); + break; + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " typed an unknown typing notification - " << word << endl; + } +} + +void TypingNotifyTask::setParams( const QString& contact, int notifyType ) +{ + m_contact = contact; + m_notificationType = notifyType; +} + +#include "typingnotifytask.moc" + +// kate: indent-mode csands; space-indent off; replace-tabs off; + diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.h b/kopete/protocols/oscar/liboscar/typingnotifytask.h new file mode 100644 index 00000000..b2c9bfef --- /dev/null +++ b/kopete/protocols/oscar/liboscar/typingnotifytask.h @@ -0,0 +1,62 @@ +/* + typingnotifytask.h - Send/Recieve typing notifications + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef _TYPINGNOTIFYTASK_H_ +#define _TYPINGNOTIFYTASK_H_ + +#include "task.h" +#include <qstring.h> +#include "oscartypeclasses.h" + +/** + * Handles sending and receiving mini typing notifications + * @author Matt Rogers + */ +class TypingNotifyTask : public Task +{ +Q_OBJECT +public: + enum { Finished = 0x0000, Typed = 0x0001, Begin = 0x0002 }; + + TypingNotifyTask( Task* parent ); + ~TypingNotifyTask(); + + virtual bool forMe( const Transfer* transfer) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + + void setParams( const QString & contact, int notifyType ); + +signals: + //! somebody started typing on the other end + void typingStarted( const QString& contact ); + + //! somebody finished typing + void typingFinished( const QString& contact ); + +private: + + //! Parse the incoming SNAC(0x04, 0x14) + void handleNotification(); + +private: + WORD m_notificationType; + QString m_contact; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/userdetails.cpp b/kopete/protocols/oscar/liboscar/userdetails.cpp new file mode 100644 index 00000000..db7d4d1d --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userdetails.cpp @@ -0,0 +1,555 @@ +/* + Kopete Oscar Protocol + userdetails.cpp - user details from the extended status packet + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#include "userdetails.h" + +#include "buffer.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <kdebug.h> +#include <klocale.h> +#include <qptrlist.h> +#include "oscarutils.h" +#include "oscardebug.h" + +using namespace Oscar; + +UserDetails::UserDetails() +{ + m_warningLevel = 0; + m_userClass = 0; + m_idleTime = 0; + m_extendedStatus = 0; + m_capabilities = 0; + m_dcPort = 0; + m_dcType = 0; + m_dcProtoVersion = 0; + m_dcAuthCookie = 0; + m_dcWebFrontPort = 0; + m_dcClientFeatures = 0; + m_dcLastInfoUpdateTime = 0; + m_dcLastExtInfoUpdateTime = 0; + m_dcLastExtStatusUpdateTime = 0; + m_userClassSpecified = false; + m_memberSinceSpecified = false; + m_onlineSinceSpecified = false; + m_numSecondsOnlineSpecified = false; + m_idleTimeSpecified = false; + m_extendedStatusSpecified = false; + m_capabilitiesSpecified = false; + m_dcOutsideSpecified = false; + m_dcInsideSpecified = false; + m_iconSpecified = false; +} + + +UserDetails::~UserDetails() +{ +} + +int UserDetails::warningLevel() const +{ + return m_warningLevel; +} + +QString UserDetails::userId() const +{ + return m_userId; +} + +WORD UserDetails::idleTime() const +{ + return m_idleTime; +} + +KNetwork::KIpAddress UserDetails::dcInternalIp() const +{ + return m_dcInsideIp; +} + +KNetwork::KIpAddress UserDetails::dcExternalIp() const +{ + return m_dcOutsideIp; +} + +DWORD UserDetails::dcPort() const +{ + return m_dcPort; +} + +QDateTime UserDetails::onlineSinceTime() const +{ + return m_onlineSince; +} + +QDateTime UserDetails::memberSinceTime() const +{ + return m_memberSince; +} + +int UserDetails::userClass() const +{ + return m_userClass; +} + +DWORD UserDetails::extendedStatus() const +{ + return m_extendedStatus; +} + +BYTE UserDetails::iconCheckSumType() const +{ + return m_iconChecksumType; +} + +QByteArray UserDetails::buddyIconHash() const +{ + return m_md5IconHash; +} + +QString UserDetails::clientName() const +{ + if ( !m_clientVersion.isEmpty() ) + return i18n("Translators: client-name client-version", + "%1 %2").arg(m_clientName, m_clientVersion); + else + return m_clientName; +} + +void UserDetails::fill( Buffer * buffer ) +{ + BYTE snLen = buffer->getByte(); + QString user = QString( buffer->getBlock( snLen ) ); + if ( !user.isEmpty() ) + m_userId = user; + m_warningLevel = buffer->getWord(); + WORD numTLVs = buffer->getWord(); + + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got user info for " << user << endl; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Warning level is " << m_warningLevel << endl; +#endif + //start parsing TLVs + for( int i = 0; i < numTLVs; ++i ) + { + TLV t = buffer->getTLV(); + if ( t ) + { + Buffer b( t.data, t.length ); + switch( t.type ) + { + case 0x0001: //user class + m_userClass = b.getWord(); + m_userClassSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "User class is " << m_userClass << endl; +#endif + break; + case 0x0002: //member since + case 0x0005: //member since + m_memberSince.setTime_t( b.getDWord() ); + m_memberSinceSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Member since " << m_memberSince << endl; +#endif + break; + case 0x0003: //sigon time + m_onlineSince.setTime_t( b.getDWord() ); + m_onlineSinceSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Signed on at " << m_onlineSince << endl; +#endif + break; + case 0x0004: //idle time + m_idleTime = b.getWord() * 60; +#ifdef OSCAR_USERINFO_DEBUG + m_idleTimeSpecified = true; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Idle time is " << m_idleTime << endl; +#endif + break; + case 0x0006: //extended user status + m_extendedStatus = b.getDWord(); + m_extendedStatusSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Extended status is " << QString::number( m_extendedStatus, 16 ) << endl; +#endif + break; + case 0x000A: //external IP address + m_dcOutsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) ); + m_dcOutsideSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "External IP address is " << m_dcOutsideIp.toString() << endl; +#endif + break; + case 0x000C: //DC info + m_dcInsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) ); + m_dcPort = b.getDWord(); +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Internal IP address is " << m_dcInsideIp.toString() << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Port number is " << m_dcPort << endl; +#endif + m_dcType = b.getByte(); + m_dcProtoVersion = b.getWord(); + m_dcAuthCookie = b.getDWord(); + m_dcWebFrontPort = b.getDWord(); + m_dcClientFeatures = b.getDWord(); + m_dcLastInfoUpdateTime = b.getDWord(); + m_dcLastExtInfoUpdateTime = b.getDWord(); + m_dcLastExtStatusUpdateTime = b.getDWord(); + b.getWord(); //unknown. + m_dcInsideSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got DC info" << endl; +#endif + break; + case 0x000D: //capability info + m_capabilities = Oscar::parseCapabilities( b, m_clientVersion ); + m_capabilitiesSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got capability info" << endl; +#endif + break; + case 0x0010: + case 0x000F: //online time + m_numSecondsOnline = b.getDWord(); + m_numSecondsOnlineSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Online for " << m_numSecondsOnline << endl; +#endif + break; + case 0x001D: + { + if ( t.length == 0 ) + break; + + while ( b.length() > 0 ) + { +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Icon and available message info" << endl; +#endif + WORD type2 = b.getWord(); + BYTE number = b.getByte(); + BYTE length = b.getByte(); + switch( type2 ) + { + case 0x0000: + b.skipBytes(length); + break; + case 0x0001: + if ( length > 0 && ( number == 0x01 || number == 0x00 ) ) + { + m_iconChecksumType = number; + m_md5IconHash.duplicate( b.getBlock( length ), length ); + m_iconSpecified = true; +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checksum:" << m_md5IconHash << endl; +#endif + } + else + { + kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "icon checksum indicated" + << " but unable to parse checksum" << endl; + b.skipBytes( length ); + } + break; + case 0x0002: + if ( length > 0 ) + { + m_availableMessage = QString( b.getBSTR() ); +#ifdef OSCAR_USERINFO_DEBUG + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "available message:" << m_availableMessage << endl; +#endif + if ( b.length() >= 4 && b.getWord() == 0x0001 ) + { + b.skipBytes( 2 ); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Encoding:" << b.getBSTR() << endl; + } + } + else + kdDebug(OSCAR_RAW_DEBUG) << "not enough bytes for available message" << endl; + break; + default: + break; + } + } + break; + } + default: + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Unknown TLV, type=" << t.type << ", length=" << t.length + << " in userinfo" << endl; + break; + }; + //detach buffer and free TLV data + b.clear(); + } + } + + //do client detection on fill + if ( m_capabilitiesSpecified ) + detectClient(); +} + +void UserDetails::detectClient() +{ + + /* My thanks to mETz for stealing^Wusing this code from SIM. + * Client type detection --- + * Most of this code is based on sim-icq code + * Thanks a lot for all the tests you guys must have made + * without sim-icq I would have only checked for the capabilities + */ + + bool clientMatched = false; + if (m_capabilities != 0) + { + bool clientMatched = false; + if (hasCap(CAP_KOPETE)) + { + m_clientName=i18n("Kopete"); + return; + } + else if (hasCap(CAP_MICQ)) + { + m_clientName=i18n("MICQ"); + return; + } + else if (hasCap(CAP_SIMNEW) || hasCap(CAP_SIMOLD)) + { + m_clientName=i18n("SIM"); + return; + } + else if (hasCap(CAP_TRILLIANCRYPT) || hasCap(CAP_TRILLIAN)) + { + m_clientName=i18n("Trillian"); + return; + } + else if (hasCap(CAP_MACICQ)) + { + m_clientName=i18n("MacICQ"); + return; + } + else if ((m_dcLastInfoUpdateTime & 0xFF7F0000L) == 0x7D000000L) + { + unsigned ver = m_dcLastInfoUpdateTime & 0xFFFF; + if (m_dcLastInfoUpdateTime & 0x00800000L) + m_clientName=i18n("Licq SSL"); + else + m_clientName=i18n("Licq"); + + if (ver % 10) + m_clientVersion.sprintf("%d.%d.%u", ver/1000, (ver/10)%100, ver%10); + else + m_clientVersion.sprintf("%d.%u", ver/1000, (ver/10)%100); + return; + } + else // some client we could not detect using capabilities + { + + clientMatched=true; // default case will set it to false again if we did not find anything + switch (m_dcLastInfoUpdateTime) + { + case 0xFFFFFFFFL: //gaim behaves like official AIM so we can't detect them, only look for miranda + { + if (m_dcLastExtStatusUpdateTime & 0x80000000) + m_clientName=QString::fromLatin1("Miranda alpha"); + else + m_clientName=QString::fromLatin1("Miranda"); + + DWORD version = (m_dcLastExtInfoUpdateTime & 0xFFFFFF); + BYTE major1 = ((version >> 24) & 0xFF); + BYTE major2 = ((version >> 16) & 0xFF); + BYTE minor1 = ((version >> 8) & 0xFF); + BYTE minor2 = (version & 0xFF); + if (minor2 > 0) // w.x.y.z + { + m_clientVersion.sprintf("%u.%u.%u.%u", major1, major2, + minor1, minor2); + } + else if (minor1 > 0) // w.x.y + { + m_clientVersion.sprintf("%u.%u.%u", major1, major2, minor1); + } + else // w.x + { + m_clientVersion.sprintf("%u.%u", major1, major2); + } + } + break; + case 0xFFFFFF8FL: + m_clientName = QString::fromLatin1("StrICQ"); + break; + case 0xFFFFFF42L: + m_clientName = QString::fromLatin1("mICQ"); + break; + case 0xFFFFFFBEL: + m_clientName = QString::fromLatin1("alicq"); + break; + case 0xFFFFFF7FL: + m_clientName = QString::fromLatin1("&RQ"); + break; + case 0xFFFFFFABL: + m_clientName = QString::fromLatin1("YSM"); + break; + case 0x3AA773EEL: + if ((m_dcLastExtStatusUpdateTime == 0x3AA66380L) && + (m_dcLastExtInfoUpdateTime == 0x3A877A42L)) + { + m_clientName=QString::fromLatin1("libicq2000"); + } + break; + default: + clientMatched=false; + break; + } + } + } + + if (!clientMatched) // now the fuzzy clientsearch starts =) + { + if (hasCap(CAP_TYPING)) + { + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Client protocol version = " << m_dcProtoVersion << endl; + switch (m_dcProtoVersion) + { + case 10: + m_clientName=QString::fromLatin1("ICQ 2003b"); + break; + case 9: + m_clientName=QString::fromLatin1("ICQ Lite"); + break; + case 8: + m_clientName=QString::fromLatin1("Miranda"); + break; + default: + m_clientName=QString::fromLatin1("ICQ2go"); + } + } + else if (hasCap(CAP_BUDDYICON)) // only gaim seems to advertize this on ICQ + { + m_clientName = QString::fromLatin1("Gaim"); + } + else if (hasCap(CAP_XTRAZ)) + { + m_clientName = QString::fromLatin1("ICQ 4.0 Lite"); + } + else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) && + hasCap(CAP_IS_2001)) + { + m_clientName = QString::fromLatin1( "ICQ 2001"); + } + else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) && + hasCap(CAP_STR_2002)) + { + m_clientName = QString::fromLatin1("ICQ 2002"); + } + else if (hasCap(CAP_RTFMSGS) && hasCap(CAP_UTF8) && + hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ)) + { + m_clientName = QString::fromLatin1("ICQ 2003a"); + } + else if (hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ)) + { + m_clientName =QString::fromLatin1("ICQ 2001b"); + } + else if ((m_dcProtoVersion == 7) && hasCap(CAP_RTFMSGS)) + { + m_clientName = QString::fromLatin1("GnomeICU"); + } + } + + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "detected client as: " << m_clientName + << " " << m_clientVersion << endl; + +} + +bool UserDetails::hasCap( int capNumber ) const +{ + bool capPresent = ( ( m_capabilities & ( 1 << capNumber ) ) != 0 ); + return capPresent; +} + +void UserDetails::merge( const UserDetails& ud ) +{ + m_userId = ud.m_userId; + m_warningLevel = ud.m_warningLevel; + if ( ud.m_userClassSpecified ) + { + m_userClass = ud.m_userClass; + m_userClassSpecified = true; + } + if ( ud.m_memberSinceSpecified ) + { + m_memberSince = ud.m_memberSince; + m_memberSinceSpecified = true; + } + if ( ud.m_onlineSinceSpecified ) + { + m_onlineSince = ud.m_onlineSince; + m_onlineSinceSpecified = true; + } + if ( ud.m_numSecondsOnlineSpecified ) + { + m_numSecondsOnline = ud.m_numSecondsOnline; + m_numSecondsOnlineSpecified = true; + } + if ( ud.m_idleTimeSpecified ) + { + m_idleTime = ud.m_idleTime; + m_idleTimeSpecified = true; + } + if ( ud.m_extendedStatusSpecified ) + { + m_extendedStatus = ud.m_extendedStatus; + m_extendedStatusSpecified = true; + } + if ( ud.m_capabilitiesSpecified ) + { + m_capabilities = ud.m_capabilities; + m_clientVersion = ud.m_clientVersion; + m_clientName = ud.m_clientName; + m_capabilitiesSpecified = true; + } + if ( ud.m_dcOutsideSpecified ) + { + m_dcOutsideIp = ud.m_dcOutsideIp; + m_dcOutsideSpecified = true; + } + if ( ud.m_dcInsideSpecified ) + { + m_dcInsideIp = ud.m_dcInsideIp; + m_dcPort = ud.m_dcPort; + m_dcType = ud.m_dcType; + m_dcProtoVersion = ud.m_dcProtoVersion; + m_dcAuthCookie = ud.m_dcAuthCookie; + m_dcWebFrontPort = ud.m_dcWebFrontPort; + m_dcClientFeatures = ud.m_dcClientFeatures; + m_dcLastInfoUpdateTime = ud.m_dcLastInfoUpdateTime; + m_dcLastExtInfoUpdateTime = ud.m_dcLastExtInfoUpdateTime; + m_dcLastExtStatusUpdateTime = ud.m_dcLastExtStatusUpdateTime; + m_dcInsideSpecified = true; + } + if ( ud.m_iconSpecified ) + { + m_iconChecksumType = ud.m_iconChecksumType; + m_md5IconHash = ud.m_md5IconHash; + m_iconSpecified = true; + } + m_availableMessage = ud.m_availableMessage; +} + +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/userdetails.h b/kopete/protocols/oscar/liboscar/userdetails.h new file mode 100644 index 00000000..fad79172 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userdetails.h @@ -0,0 +1,121 @@ +/* + Kopete Oscar Protocol + userdetails.h - user details from the extended status packet + + Copyright (c) 2004 by Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef USERDETAILS_H +#define USERDETAILS_H + +#include <ksocketaddress.h> +#include "oscartypes.h" +#include "kopete_export.h" + +class Buffer; +using namespace Oscar; + +/** + * Holds information from the extended user info packet + * @author Matt Rogers + */ +class KOPETE_EXPORT UserDetails +{ +public: + UserDetails(); + ~UserDetails(); + + QString userId() const; //! User ID accessor + int warningLevel() const; //! Warning level accessor + WORD idleTime() const; //! Idle time accessor + KNetwork::KIpAddress dcInternalIp() const; //! DC local IP accessor + KNetwork::KIpAddress dcExternalIp() const; //! DC outside IP accessor + DWORD dcPort() const; //! DC port number + QDateTime onlineSinceTime() const; //! Online since accessor + QDateTime memberSinceTime() const; //! Member since accessor + int userClass() const; //! User class accessor + DWORD extendedStatus() const; //!User status accessor + BYTE iconCheckSumType() const; //!Buddy icon hash type + QByteArray buddyIconHash() const; //! Buddy icon md5 hash accessor + QString clientName() const; //! Client name and version + bool hasCap( int capNumber ) const; //! Tell if we have this capability + + /** + * Fill the class with data from a buffer + * It only updates what's available. + */ + void fill( Buffer* buffer ); + + /** + * Merge only those data from another UserDetails + * which are marked as specified. + */ + void merge( const UserDetails& ud ); + + bool userClassSpecified() const { return m_userClassSpecified; } + bool memberSinceSpecified() const { return m_memberSinceSpecified; } + bool onlineSinceSpecified() const { return m_onlineSinceSpecified; } + bool numSecondsOnlineSpecified() const { return m_numSecondsOnlineSpecified; } + bool idleTimeSpecified() const { return m_idleTimeSpecified; } + bool extendedStatusSpecified() const { return m_extendedStatusSpecified; } + bool capabilitiesSpecified() const { return m_capabilitiesSpecified; } + bool dcOutsideSpecified() const { return m_dcOutsideSpecified; } + bool dcInsideSpecified() const { return m_dcInsideSpecified; } + bool iconSpecified() const { return m_iconSpecified; } +private: + //! Do client detection + void detectClient(); + + +private: + QString m_userId; /// the screename/uin of the contact + int m_warningLevel; /// the warning level of the contact + int m_userClass; /// the class of the user - TLV 0x01 + QDateTime m_memberSince; /// how long the user's been a member - TLV 0x05 + QDateTime m_onlineSince; /// how long the contact's been online - TLV 0x03 + DWORD m_numSecondsOnline; /// how long the contact's been online in seconds + WORD m_idleTime; /// the idle time of the contact - TLV 0x0F + DWORD m_extendedStatus; /// the extended status of the contact - TLV 0x06 + DWORD m_capabilities; //TLV 0x05 + QString m_clientVersion; /// the version of client they're using + QString m_clientName; /// the name of the client they're using + KNetwork::KIpAddress m_dcOutsideIp; /// DC Real IP Address - TLV 0x0A + KNetwork::KIpAddress m_dcInsideIp; /// DC Internal IP Address - TLV 0x0C + DWORD m_dcPort; /// DC Port - TLV 0x0C + BYTE m_dcType; /// DC Type - TLV 0x0C + WORD m_dcProtoVersion; /// DC Protocol Version - TLV 0x0C + DWORD m_dcAuthCookie; /// DC Authorization Cookie - TLV 0x0C + DWORD m_dcWebFrontPort; /// DC Web Front Port - TLV 0x0C + DWORD m_dcClientFeatures; /// DC client features( whatever they are ) - TLV 0x0C + DWORD m_dcLastInfoUpdateTime; /// DC last info update time - TLV 0x0C + DWORD m_dcLastExtInfoUpdateTime; /// DC last exteneded info update time - TLV 0x0C + DWORD m_dcLastExtStatusUpdateTime; /// DC last extended status update time - TLV 0x0C + BYTE m_iconChecksumType; /// The OSCAR checksum type for the buddy icon TLV 0x1D + QByteArray m_md5IconHash; /// Buddy Icon MD5 Hash - TLV 0x1D + QString m_availableMessage; /// Message a person can have when available - TLV 0x0D + + bool m_userClassSpecified; + bool m_memberSinceSpecified; + bool m_onlineSinceSpecified; + bool m_numSecondsOnlineSpecified; + bool m_idleTimeSpecified; + bool m_extendedStatusSpecified; + bool m_capabilitiesSpecified; + bool m_dcOutsideSpecified; + bool m_dcInsideSpecified; + bool m_iconSpecified; + +}; + +#endif +//kate: tab-width 4; indent-mode csands; diff --git a/kopete/protocols/oscar/liboscar/userinfotask.cpp b/kopete/protocols/oscar/liboscar/userinfotask.cpp new file mode 100644 index 00000000..a204c475 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userinfotask.cpp @@ -0,0 +1,156 @@ +/* +Kopete Oscar Protocol +userinfotask.h - Handle sending and receiving info requests for users + +Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + +Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + +************************************************************************* +* * +* 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 of the License, or (at your option) any later version. * +* * +************************************************************************* +*/ +#include "userinfotask.h" + +#include <kdebug.h> + +#include "buffer.h" +#include "connection.h" +#include "transfer.h" +#include "userdetails.h" + + + +UserInfoTask::UserInfoTask( Task* parent ) +: Task( parent ) +{ +} + + +UserInfoTask::~UserInfoTask() +{ +} + +bool UserInfoTask::forMe( const Transfer * transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + + if ( st->snacService() == 0x0002 && st->snacSubtype() == 0x0006 ) + { + if ( m_contactSequenceMap.find( st->snacRequest() ) == m_contactSequenceMap.end() ) + return false; + else + { + //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found sequence. taking packet" << endl; + return true; + } + } + else + return false; +} + +bool UserInfoTask::take( Transfer * transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + Q_UINT16 seq = 0; + SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer ); + if ( st ) + seq = st->snacRequest(); + + if ( seq != 0 ) + { + //AFAIK location info packets always have user info + Buffer* b = transfer->buffer(); + UserDetails ud; + ud.fill( b ); + m_sequenceInfoMap[seq] = ud; + emit gotInfo( seq ); + + QValueList<TLV> list = b->getTLVList(); + QValueList<TLV>::iterator it = list.begin(); + QString profile; + QString away; + for ( ; ( *it ); ++it ) + { + switch( ( *it ).type ) + { + case 0x0001: //profile text encoding + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "text encoding is " << QString( ( *it ).data )<< endl; + break; + case 0x0002: //profile text + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "The profile is '" << QString( ( *it ).data ) << "'" << endl; + profile = QString( ( *it ).data ); // aim always seems to use us-ascii encoding + emit receivedProfile( m_contactSequenceMap[seq], profile ); + break; + case 0x0003: //away message encoding + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message encoding is " << QString( ( *it ).data ) << endl; + break; + case 0x0004: //away message + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message is '" << QString( ( *it ).data ) << "'" << endl; + away = QString( (*it ).data ); // aim always seems to use us-ascii encoding + emit receivedAwayMessage( m_contactSequenceMap[seq], away ); + break; + case 0x0005: //capabilities + break; + default: //unknown + kdDebug(14151) << k_funcinfo << "Unknown user info type " << ( *it ).type << endl; + break; + }; + } + list.clear(); + } + setTransfer( 0 ); + return true; + } + return false; +} + +void UserInfoTask::onGo() +{ + if ( m_contactSequenceMap[m_seq].isEmpty() ) + { + kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Info requested for empty contact!" << endl; + return; + } + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0002, 0x0005, 0, m_seq }; + Buffer* buffer = new Buffer(); + + buffer->addWord( m_typesSequenceMap[m_seq] ); + buffer->addBUIN( m_contactSequenceMap[m_seq].local8Bit() ); + + Transfer* t = createTransfer( f, s, buffer ); + send( t ); +} + +void UserInfoTask::requestInfoFor( const QString& contact, unsigned int types ) +{ + Q_UINT16 seq = client()->snacSequence(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting sequence " << seq << " for contact " << contact << endl; + m_contactSequenceMap[seq] = contact; + m_typesSequenceMap[seq] = types; + m_seq = seq; + onGo(); +} + +UserDetails UserInfoTask::getInfoFor( Q_UINT16 sequence ) const +{ + return m_sequenceInfoMap[sequence]; +} + + + +//kate: indent-mode csands; tab-width 4; + + +#include "userinfotask.moc" diff --git a/kopete/protocols/oscar/liboscar/userinfotask.h b/kopete/protocols/oscar/liboscar/userinfotask.h new file mode 100644 index 00000000..063eedd7 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/userinfotask.h @@ -0,0 +1,69 @@ +/* + Kopete Oscar Protocol + userinfotask.h - Handle sending and receiving info requests for users + + Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ +#ifndef USERINFOTASK_H +#define USERINFOTASK_H + +#include "task.h" + +#include <qstring.h> +#include "userdetails.h" + +class Transfer; + +/** +Handles user information requests that are done via SNAC 02,05 and 02,06 + +@author Kopete Developers +*/ +class UserInfoTask : public Task +{ +Q_OBJECT +public: + UserInfoTask( Task* parent ); + ~UserInfoTask(); + + enum { Profile = 0x0001, General = 0x0002, AwayMessage = 0x0003, Capabilities = 0x0004 }; + + //! Task implementation + bool forMe( const Transfer* transfer ) const; + bool take( Transfer* transfer ); + void onGo(); + + void requestInfoFor( const QString& userId, unsigned int types ); + UserDetails getInfoFor( Q_UINT16 sequence ) const; + QString contactForSequence( Q_UINT16 sequence ) const; + + +signals: + void gotInfo( Q_UINT16 seqNumber ); + void receivedProfile( const QString& contact, const QString& profile ); + void receivedAwayMessage( const QString& contact, const QString& message ); + +private: + QMap<Q_UINT16, UserDetails> m_sequenceInfoMap; + QMap<Q_UINT16, QString> m_contactSequenceMap; + QMap<Q_UINT16, unsigned int> m_typesSequenceMap; + Q_UINT16 m_seq; + +}; + + + +#endif + +//kate: indent-mode csands; tab-width 4; diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.cpp b/kopete/protocols/oscar/liboscar/usersearchtask.cpp new file mode 100644 index 00000000..3fd31010 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/usersearchtask.cpp @@ -0,0 +1,315 @@ +/* + Kopete Oscar Protocol + usersearchtask.cpp - Search for contacts + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "usersearchtask.h" + +#include "transfer.h" +#include "buffer.h" +#include "connection.h" + +UserSearchTask::UserSearchTask( Task* parent ) + : ICQTask( parent ) +{ +} + + +UserSearchTask::~UserSearchTask() +{ +} + +void UserSearchTask::onGo() +{ +} + +bool UserSearchTask::forMe( const Transfer* t ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t ); + + if ( !st ) + return false; + + if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 ) + return false; + + Buffer buf( st->buffer()->buffer(), st->buffer()->length() ); + const_cast<UserSearchTask*>(this)->parseInitialData( buf ); + + if ( requestType() == 0x07da && ( requestSubType() == 0x01a4 || requestSubType() == 0x01ae ) ) + return true; + + return false; +} + +bool UserSearchTask::take( Transfer* t ) +{ + if ( forMe( t ) ) + { + setTransfer( t ); + + Q_UINT16 seq = 0; + SnacTransfer* st = dynamic_cast<SnacTransfer*>( t ); + if ( st ) + seq = st->snacRequest(); + + TLV tlv1 = transfer()->buffer()->getTLV(); + + if ( seq == 0 ) + { + setTransfer( 0 ); + return false; + } + + Buffer* buffer = new Buffer( tlv1.data, tlv1.length ); + ICQSearchResult result; + buffer->getLEWord(); // data chunk size + /*DWORD receiverUin =*/ buffer->getLEDWord(); // target uin + buffer->getLEWord(); // request type + buffer->getLEWord(); // request sequence number: 0x0002 + buffer->getLEWord(); // request subtype + + BYTE success = buffer->getByte(); // Success byte: always 0x0a + + if ( ( success == 0x32 ) || ( success == 0x14 ) || ( success == 0x1E ) ) + result.uin = 1; + else + result.fill( buffer ); + + m_results.append( result ); + + emit foundUser( result ); + + // Last user found reply + if ( requestSubType() == 0x01ae ) + { + int moreUsersCount = buffer->getLEDWord(); + emit searchFinished( moreUsersCount ); + setSuccess( 0, QString::null ); + } + setTransfer( 0 ); + } + return true; +} + +void UserSearchTask::searchUserByUIN( const QString& uin ) +{ + //create a new result list + m_type = UINSearch; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x07D0 ); //meta-information request + setRequestSubType( 0x0569 ); //subtype: META_SEARCH_BY_UIN + setSequence( f.sequence ); + Buffer* tlvdata = new Buffer(); + tlvdata->addLEWord( 0x0136 ); //tlv of type 0x0136 with length 4. all little endian + tlvdata->addLEWord( 0x0004 ); + tlvdata->addLEDWord( uin.toULong() ); + Buffer* buf = addInitialData( tlvdata ); + delete tlvdata; + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + +void UserSearchTask::searchWhitePages( const ICQWPSearchInfo& info ) +{ + m_type = WhitepageSearch; + + FLAP f = { 0x02, 0, 0 }; + SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() }; + + setRequestType( 0x07D0 ); + setRequestSubType( 0x055F ); + setSequence( f.sequence ); + Buffer* tlvData = new Buffer(); + /* + search.addLEWord(0x0533); // subtype: 1331 + + //LNTS FIRST + search.addLEWord(first.length()); + if(first.length()>0) + search.addLEString(first.latin1(), first.length()); + + // LNTS LAST + search.addLEWord(last.length()); + if(last.length()>0) + search.addLEString(last.latin1(), last.length()); + + // LNTS NICK + search.addLEWord(nick.length()); + if(nick.length()>0) + search.addLEString(nick.latin1(), nick.length()); + + // LNTS EMAIL + search.addLEWord(mail.length()); + if(mail.length()>0) + search.addLEString(mail.latin1(), mail.length()); + + // WORD.L MINAGE + search.addLEWord(minage); + + // WORD.L MAXAGE + search.addLEWord(maxage); + + // BYTE xx SEX 1=fem, 2=mal, 0=dontcare + if (sex==1) + search.addLEByte(0x01); + else if(sex==2) + search.addLEByte(0x02); + else + search.addLEByte(0x00); + + // BYTE xx LANGUAGE + search.addLEByte(lang); + + // LNTS CITY + search.addLEWord(city.length()); + if(city.length()>0) + search.addLEString(city.latin1(), city.length()); + + // LNTS STATE + search.addLEWord(state.length()); + if(state.length()>0) + search.addLEString(state.latin1(), state.length()); + + // WORD.L xx xx COUNTRY + search.addLEWord(country); + + // LNTS COMPANY + search.addLEWord(company.length()); + if(company.length()>0) + search.addLEString(company.latin1(), company.length()); + + // LNTS DEPARTMENT + search.addLEWord(department.length()); + if(department.length()>0) + search.addLEString(department.latin1(), department.length()); + + // LNTS POSITION + search.addLEWord(position.length()); + if(position.length()>0) + search.addLEString(position.latin1(), position.length()); + + // BYTE xx OCCUPATION + search.addLEByte(occupation); + + //WORD.L xx xx PAST + search.addLEWord(0x0000); + + //LNTS PASTDESC - The past description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx INTERESTS - The interests category to search for. + search.addLEWord(0x0000); + + // LNTS INTERDESC - The interests description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx AFFILIATION - The affiliation to search for. + search.addLEWord(0x0000); + + // LNTS AFFIDESC - The affiliation description to search for. + search.addLEWord(0x0000); + + // WORD.L xx xx HOMEPAGE - The home page category to search for. + search.addLEWord(0x0000); + + // LNTS HOMEDESC - The home page description to search for. + search.addLEWord(0x0000); + + // BYTE xx ONLINE 1=online onliners, 0=dontcare + if(onlineOnly) + search.addLEByte(0x01); + else + search.addLEByte(0x00); + */ + if ( !info.firstName.isEmpty() ) + { + Buffer bufFileName; + bufFileName.addLEWord( info.firstName.length() ); + bufFileName.addLEString( info.firstName, info.firstName.length() ); + tlvData->addLETLV( 0x0140, bufFileName.length(), bufFileName.buffer() ); + } + + if ( !info.lastName.isEmpty() ) + { + Buffer bufLastName; + bufLastName.addLEWord( info.lastName.length() ); + bufLastName.addLEString( info.lastName, info.lastName.length() ); + tlvData->addLETLV( 0x014A, bufLastName.length(), bufLastName.buffer() ); + } + + if ( !info.nickName.isEmpty() ) + { + Buffer bufNickName; + bufNickName.addLEWord( info.nickName.length() ); + bufNickName.addLEString( info.nickName, info.nickName.length() ); + tlvData->addLETLV( 0x0154, bufNickName.length(), bufNickName.buffer() ); + } + + if ( !info.email.isEmpty() ) + { + Buffer bufEmail; + bufEmail.addLEWord( info.email.length() ); + bufEmail.addLEString( info.email, info.email.length() ); + tlvData->addLETLV( 0x015E, bufEmail.length(), bufEmail.buffer() ); + } + + if ( info.age > 0 ) + { + Buffer bufAge; + bufAge.addWord( info.age ); + bufAge.addWord( info.age ); + tlvData->addLETLV( 0x0168, bufAge.length(), bufAge.buffer() ); + } + + if ( info.gender > 0 ) + tlvData->addLETLV8( 0x017C, info.gender ); + + if ( info.language > 0 ) + tlvData->addLETLV16( 0x0186, info.language ); + + if ( info.country > 0 ) + tlvData->addLETLV16( 0x01A4, info.country ); + + if ( !info.city.isEmpty() ) + { + Buffer bufCity; + bufCity.addLEWord( info.city.length() ); + bufCity.addLEString( info.city, info.city.length() ); + tlvData->addLETLV( 0x0190, bufCity.length(), bufCity.buffer() ); + } + + if ( info.occupation > 0 ) + tlvData->addLETLV16( 0x01CC, info.occupation ); + + if ( info.onlineOnly ) + tlvData->addLETLV8( 0x0230, 0x01 ); + + Buffer* buf = addInitialData( tlvData ); + delete tlvData; //we're done with it + + Transfer* t = createTransfer( f, s, buf ); + send( t ); +} + + +#include "usersearchtask.moc" + +//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off; diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.h b/kopete/protocols/oscar/liboscar/usersearchtask.h new file mode 100644 index 00000000..239efe36 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/usersearchtask.h @@ -0,0 +1,61 @@ +/* + Kopete Oscar Protocol + usersearchtask.h - Search for contacts + + Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net> + + Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef USERSEARCHTASK_H +#define USERSEARCHTASK_H + +#include "icqtask.h" +#include <qstring.h> +#include "icquserinfo.h" + +/** +Search for contacts + +@author Kopete Developers +*/ +class UserSearchTask : public ICQTask +{ +Q_OBJECT +public: + UserSearchTask( Task* parent ); + + ~UserSearchTask(); + + enum SearchType { UINSearch, WhitepageSearch }; + + virtual void onGo(); + virtual bool forMe( const Transfer* t ) const; + virtual bool take( Transfer* t ); + + /** Search by UIN */ + void searchUserByUIN( const QString& uin ); + + void searchWhitePages( const ICQWPSearchInfo& info ); + +signals: + void foundUser( const ICQSearchResult& result ); + void searchFinished( int ); + +private: + QValueList<ICQSearchResult> m_results; + QString m_uin; + Q_UINT16 m_seq; + SearchType m_type; +}; + +#endif diff --git a/kopete/protocols/oscar/liboscar/warningtask.cpp b/kopete/protocols/oscar/liboscar/warningtask.cpp new file mode 100644 index 00000000..56051dc8 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/warningtask.cpp @@ -0,0 +1,96 @@ +/* + Kopete Oscar Protocol + warningtask.cpp - send warnings to aim users + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "warningtask.h" + +#include <qstring.h> +#include <kdebug.h> +#include "transfer.h" +#include "connection.h" + +WarningTask::WarningTask( Task* parent ): Task( parent ) +{ +} + + +WarningTask::~WarningTask() +{ +} + +void WarningTask::setContact( const QString& contact ) +{ + m_contact = contact; +} + +void WarningTask::setAnonymous( bool anon ) +{ + m_sendAnon = anon; +} + +bool WarningTask::forMe( const Transfer* transfer ) const +{ + const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer ); + if ( !st ) + return false; + if ( st->snacService() == 0x04 && st->snacSubtype() == 0x09 && st->snacRequest() == m_sequence ) + return true; + + return false; +} + +bool WarningTask::take( Transfer* transfer ) +{ + if ( forMe( transfer ) ) + { + setTransfer( transfer ); + Buffer *b = transfer->buffer(); + m_increase = b->getWord(); + m_newLevel = b->getWord(); + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got warning ack for " << m_contact << endl; + kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Warning level increased " << m_increase + << " to " << m_newLevel << endl; + emit userWarned( m_contact, m_increase, m_newLevel ); + setSuccess( 0, QString::null ); + setTransfer( 0 ); + return true; + } + else + { + setError( 0, QString::null ); + return false; + } +} + +void WarningTask::onGo() +{ + FLAP f = { 0x0002, 0, 0 }; + SNAC s = { 0x0004, 0x0008, 0x0000, client()->snacSequence() }; + Buffer* b = new Buffer; + if ( m_sendAnon ) + b->addWord( 0x0001 ); + else + b->addWord( 0x0000 ); + + b->addBUIN( m_contact.latin1() ); //TODO i should probably check the encoding here. nyeh + Transfer* t = createTransfer( f, s, b ); + send( t ); +} + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; + +#include "warningtask.moc" diff --git a/kopete/protocols/oscar/liboscar/warningtask.h b/kopete/protocols/oscar/liboscar/warningtask.h new file mode 100644 index 00000000..1280fab9 --- /dev/null +++ b/kopete/protocols/oscar/liboscar/warningtask.h @@ -0,0 +1,59 @@ +/* + Kopete Oscar Protocol + warningtask.cpp - send warnings to aim users + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifndef WARNINGTASK_H +#define WARNINGTASK_H + +#include "task.h" +#include <qmap.h> +#include "oscartypes.h" + +/** +@author Matt Rogers +*/ +class WarningTask : public Task +{ +Q_OBJECT +public: + WarningTask( Task* parent ); + ~WarningTask(); + + void setContact( const QString& contact ); + void setAnonymous( bool anon ); + + WORD levelIncrease(); + WORD newLevel(); + + virtual bool forMe( const Transfer* transfer ) const; + virtual bool take( Transfer* transfer ); + virtual void onGo(); + +signals: + void userWarned( const QString&, Q_UINT16, Q_UINT16 ); + +private: + QString m_contact; + bool m_sendAnon; + WORD m_sequence; + WORD m_increase; + WORD m_newLevel; +}; + +#endif + +//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4; |