summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/oscar/liboscar
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/oscar/liboscar')
-rw-r--r--kopete/protocols/oscar/liboscar/DESIGN12
-rw-r--r--kopete/protocols/oscar/liboscar/HACKING194
-rw-r--r--kopete/protocols/oscar/liboscar/Makefile.am26
-rw-r--r--kopete/protocols/oscar/liboscar/TODO37
-rw-r--r--kopete/protocols/oscar/liboscar/aimlogintask.cpp254
-rw-r--r--kopete/protocols/oscar/liboscar/aimlogintask.h82
-rw-r--r--kopete/protocols/oscar/liboscar/blmlimitstask.cpp94
-rw-r--r--kopete/protocols/oscar/liboscar/blmlimitstask.h43
-rw-r--r--kopete/protocols/oscar/liboscar/buddyicontask.cpp245
-rw-r--r--kopete/protocols/oscar/liboscar/buddyicontask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/buffer.cpp519
-rw-r--r--kopete/protocols/oscar/liboscar/buffer.h268
-rw-r--r--kopete/protocols/oscar/liboscar/bytestream.cpp270
-rw-r--r--kopete/protocols/oscar/liboscar/bytestream.h78
-rw-r--r--kopete/protocols/oscar/liboscar/changevisibilitytask.cpp150
-rw-r--r--kopete/protocols/oscar/liboscar/changevisibilitytask.h58
-rw-r--r--kopete/protocols/oscar/liboscar/chatnavservicetask.cpp355
-rw-r--r--kopete/protocols/oscar/liboscar/chatnavservicetask.h67
-rw-r--r--kopete/protocols/oscar/liboscar/chatservicetask.cpp359
-rw-r--r--kopete/protocols/oscar/liboscar/chatservicetask.h65
-rw-r--r--kopete/protocols/oscar/liboscar/client.cpp1353
-rw-r--r--kopete/protocols/oscar/liboscar/client.h521
-rw-r--r--kopete/protocols/oscar/liboscar/clientreadytask.cpp109
-rw-r--r--kopete/protocols/oscar/liboscar/clientreadytask.h46
-rw-r--r--kopete/protocols/oscar/liboscar/closeconnectiontask.cpp146
-rw-r--r--kopete/protocols/oscar/liboscar/closeconnectiontask.h62
-rw-r--r--kopete/protocols/oscar/liboscar/connection.cpp248
-rw-r--r--kopete/protocols/oscar/liboscar/connection.h209
-rw-r--r--kopete/protocols/oscar/liboscar/connectionhandler.cpp174
-rw-r--r--kopete/protocols/oscar/liboscar/connectionhandler.h118
-rw-r--r--kopete/protocols/oscar/liboscar/connector.cpp62
-rw-r--r--kopete/protocols/oscar/liboscar/connector.h59
-rw-r--r--kopete/protocols/oscar/liboscar/coreprotocol.cpp285
-rw-r--r--kopete/protocols/oscar/liboscar/coreprotocol.h108
-rw-r--r--kopete/protocols/oscar/liboscar/errortask.cpp66
-rw-r--r--kopete/protocols/oscar/liboscar/errortask.h39
-rw-r--r--kopete/protocols/oscar/liboscar/flapprotocol.cpp72
-rw-r--r--kopete/protocols/oscar/liboscar/flapprotocol.h46
-rw-r--r--kopete/protocols/oscar/liboscar/icbmparamstask.cpp143
-rw-r--r--kopete/protocols/oscar/liboscar/icbmparamstask.h56
-rw-r--r--kopete/protocols/oscar/liboscar/icqlogintask.cpp117
-rw-r--r--kopete/protocols/oscar/liboscar/icqlogintask.h47
-rw-r--r--kopete/protocols/oscar/liboscar/icqtask.cpp151
-rw-r--r--kopete/protocols/oscar/liboscar/icqtask.h63
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfo.cpp262
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfo.h213
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfotask.cpp234
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfotask.h77
-rw-r--r--kopete/protocols/oscar/liboscar/inputprotocolbase.cpp100
-rw-r--r--kopete/protocols/oscar/liboscar/inputprotocolbase.h72
-rw-r--r--kopete/protocols/oscar/liboscar/locationrightstask.cpp87
-rw-r--r--kopete/protocols/oscar/liboscar/locationrightstask.h57
-rw-r--r--kopete/protocols/oscar/liboscar/logintask.cpp218
-rw-r--r--kopete/protocols/oscar/liboscar/logintask.h144
-rw-r--r--kopete/protocols/oscar/liboscar/md5.c392
-rw-r--r--kopete/protocols/oscar/liboscar/md5.h93
-rw-r--r--kopete/protocols/oscar/liboscar/messagereceivertask.cpp461
-rw-r--r--kopete/protocols/oscar/liboscar/messagereceivertask.h79
-rw-r--r--kopete/protocols/oscar/liboscar/offlinemessagestask.cpp166
-rw-r--r--kopete/protocols/oscar/liboscar/offlinemessagestask.h54
-rw-r--r--kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp99
-rw-r--r--kopete/protocols/oscar/liboscar/onlinenotifiertask.h60
-rw-r--r--kopete/protocols/oscar/liboscar/oscarbytestream.cpp141
-rw-r--r--kopete/protocols/oscar/liboscar/oscarbytestream.h72
-rw-r--r--kopete/protocols/oscar/liboscar/oscarclientstream.cpp437
-rw-r--r--kopete/protocols/oscar/liboscar/oscarclientstream.h164
-rw-r--r--kopete/protocols/oscar/liboscar/oscarconnector.cpp108
-rw-r--r--kopete/protocols/oscar/liboscar/oscarconnector.h69
-rw-r--r--kopete/protocols/oscar/liboscar/oscardebug.h35
-rw-r--r--kopete/protocols/oscar/liboscar/oscarmessage.cpp301
-rw-r--r--kopete/protocols/oscar/liboscar/oscarmessage.h182
-rw-r--r--kopete/protocols/oscar/liboscar/oscarsettings.cpp69
-rw-r--r--kopete/protocols/oscar/liboscar/oscarsettings.h62
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypeclasses.cpp284
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypeclasses.h144
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypes.h292
-rw-r--r--kopete/protocols/oscar/liboscar/oscarutils.cpp300
-rw-r--r--kopete/protocols/oscar/liboscar/oscarutils.h93
-rw-r--r--kopete/protocols/oscar/liboscar/ownuserinfotask.cpp137
-rw-r--r--kopete/protocols/oscar/liboscar/ownuserinfotask.h59
-rw-r--r--kopete/protocols/oscar/liboscar/prmparamstask.cpp72
-rw-r--r--kopete/protocols/oscar/liboscar/prmparamstask.h42
-rw-r--r--kopete/protocols/oscar/liboscar/profiletask.cpp119
-rw-r--r--kopete/protocols/oscar/liboscar/profiletask.h56
-rw-r--r--kopete/protocols/oscar/liboscar/rateclass.cpp246
-rw-r--r--kopete/protocols/oscar/liboscar/rateclass.h132
-rw-r--r--kopete/protocols/oscar/liboscar/rateclassmanager.cpp177
-rw-r--r--kopete/protocols/oscar/liboscar/rateclassmanager.h83
-rw-r--r--kopete/protocols/oscar/liboscar/rateinfotask.cpp173
-rw-r--r--kopete/protocols/oscar/liboscar/rateinfotask.h64
-rw-r--r--kopete/protocols/oscar/liboscar/rtf.cc2427
-rw-r--r--kopete/protocols/oscar/liboscar/rtf.ll864
-rw-r--r--kopete/protocols/oscar/liboscar/rtf2html.h207
-rw-r--r--kopete/protocols/oscar/liboscar/safedelete.cpp139
-rw-r--r--kopete/protocols/oscar/liboscar/safedelete.h79
-rw-r--r--kopete/protocols/oscar/liboscar/senddcinfotask.cpp107
-rw-r--r--kopete/protocols/oscar/liboscar/senddcinfotask.h41
-rw-r--r--kopete/protocols/oscar/liboscar/sendidletimetask.cpp57
-rw-r--r--kopete/protocols/oscar/liboscar/sendidletimetask.h46
-rw-r--r--kopete/protocols/oscar/liboscar/sendmessagetask.cpp447
-rw-r--r--kopete/protocols/oscar/liboscar/sendmessagetask.h55
-rw-r--r--kopete/protocols/oscar/liboscar/serverredirecttask.cpp173
-rw-r--r--kopete/protocols/oscar/liboscar/serverredirecttask.h72
-rw-r--r--kopete/protocols/oscar/liboscar/serverversionstask.cpp169
-rw-r--r--kopete/protocols/oscar/liboscar/serverversionstask.h59
-rw-r--r--kopete/protocols/oscar/liboscar/servicesetuptask.cpp135
-rw-r--r--kopete/protocols/oscar/liboscar/servicesetuptask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/snacprotocol.cpp109
-rw-r--r--kopete/protocols/oscar/liboscar/snacprotocol.h46
-rw-r--r--kopete/protocols/oscar/liboscar/ssiactivatetask.cpp50
-rw-r--r--kopete/protocols/oscar/liboscar/ssiactivatetask.h38
-rw-r--r--kopete/protocols/oscar/liboscar/ssiauthtask.cpp188
-rw-r--r--kopete/protocols/oscar/liboscar/ssiauthtask.h60
-rw-r--r--kopete/protocols/oscar/liboscar/ssilisttask.cpp174
-rw-r--r--kopete/protocols/oscar/liboscar/ssilisttask.h106
-rw-r--r--kopete/protocols/oscar/liboscar/ssimanager.cpp658
-rw-r--r--kopete/protocols/oscar/liboscar/ssimanager.h154
-rw-r--r--kopete/protocols/oscar/liboscar/ssimodifytask.cpp637
-rw-r--r--kopete/protocols/oscar/liboscar/ssimodifytask.h156
-rw-r--r--kopete/protocols/oscar/liboscar/ssiparamstask.cpp102
-rw-r--r--kopete/protocols/oscar/liboscar/ssiparamstask.h43
-rw-r--r--kopete/protocols/oscar/liboscar/stream.cpp31
-rw-r--r--kopete/protocols/oscar/liboscar/stream.h75
-rw-r--r--kopete/protocols/oscar/liboscar/task.cpp291
-rw-r--r--kopete/protocols/oscar/liboscar/task.h116
-rw-r--r--kopete/protocols/oscar/liboscar/tests/Makefile.am28
-rw-r--r--kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp305
-rw-r--r--kopete/protocols/oscar/liboscar/tests/chatnavtests.h50
-rw-r--r--kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp49
-rw-r--r--kopete/protocols/oscar/liboscar/tests/clientstream_test.h49
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp58
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ipaddrtest.h35
-rw-r--r--kopete/protocols/oscar/liboscar/tests/kunittest.cpp167
-rw-r--r--kopete/protocols/oscar/liboscar/tests/kunittest.h71
-rw-r--r--kopete/protocols/oscar/liboscar/tests/logintest.cpp56
-rw-r--r--kopete/protocols/oscar/liboscar/tests/logintest.h53
-rw-r--r--kopete/protocols/oscar/liboscar/tests/main.cpp35
-rw-r--r--kopete/protocols/oscar/liboscar/tests/redirecttest.cpp117
-rw-r--r--kopete/protocols/oscar/liboscar/tests/redirecttest.h51
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp73
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssigrouptest.h54
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssitest.cpp111
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssitest.h34
-rw-r--r--kopete/protocols/oscar/liboscar/tests/tester.h121
-rw-r--r--kopete/protocols/oscar/liboscar/tests/userinfotest.cpp67
-rw-r--r--kopete/protocols/oscar/liboscar/tests/userinfotest.h53
-rw-r--r--kopete/protocols/oscar/liboscar/transfer.cpp367
-rw-r--r--kopete/protocols/oscar/liboscar/transfer.h169
-rw-r--r--kopete/protocols/oscar/liboscar/typingnotifytask.cpp124
-rw-r--r--kopete/protocols/oscar/liboscar/typingnotifytask.h62
-rw-r--r--kopete/protocols/oscar/liboscar/userdetails.cpp555
-rw-r--r--kopete/protocols/oscar/liboscar/userdetails.h121
-rw-r--r--kopete/protocols/oscar/liboscar/userinfotask.cpp156
-rw-r--r--kopete/protocols/oscar/liboscar/userinfotask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/usersearchtask.cpp315
-rw-r--r--kopete/protocols/oscar/liboscar/usersearchtask.h61
-rw-r--r--kopete/protocols/oscar/liboscar/warningtask.cpp96
-rw-r--r--kopete/protocols/oscar/liboscar/warningtask.h59
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("&"), "&amp;");
+ str.replace(QRegExp("<"), "&lt;");
+ str.replace(QRegExp(">"), "&gt;");
+ str.replace(QRegExp("\""), "&quot;");
+ 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 += "&nbsp;";
+ 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("&"), "&amp;");
+ str.replace(QRegExp("<"), "&lt;");
+ str.replace(QRegExp(">"), "&gt;");
+ str.replace(QRegExp("\""), "&quot;");
+ 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 += "&nbsp;";
+ 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;