aboutsummaryrefslogtreecommitdiffstats
path: root/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'protocols')
-rw-r--r--protocols/Makefile49
-rw-r--r--protocols/jabber/Makefile37
-rw-r--r--protocols/jabber/asciitab.h62
-rw-r--r--protocols/jabber/expat.c54
-rw-r--r--protocols/jabber/genhash.c0
-rw-r--r--protocols/jabber/hashtable.c142
-rw-r--r--protocols/jabber/hashtable.h69
-rw-r--r--protocols/jabber/iasciitab.h63
-rw-r--r--protocols/jabber/jabber.c2445
-rw-r--r--protocols/jabber/jabber.h315
-rw-r--r--protocols/jabber/jconn.c0
-rw-r--r--protocols/jabber/jid.c170
-rw-r--r--protocols/jabber/jpacket.c159
-rw-r--r--protocols/jabber/jutil.c122
-rw-r--r--protocols/jabber/karma.c0
-rw-r--r--protocols/jabber/latin1tab.h62
-rw-r--r--protocols/jabber/lib.h343
-rw-r--r--protocols/jabber/libxode.h398
-rw-r--r--protocols/jabber/log.c44
-rw-r--r--protocols/jabber/log.h36
-rw-r--r--protocols/jabber/nametab.h150
-rw-r--r--protocols/jabber/pool.c247
-rw-r--r--protocols/jabber/pproxy.c0
-rw-r--r--protocols/jabber/rate.c0
-rw-r--r--protocols/jabber/str.c215
-rw-r--r--protocols/jabber/utf8tab.h63
-rw-r--r--protocols/jabber/xhash.c0
-rw-r--r--protocols/jabber/xmldef.h34
-rw-r--r--protocols/jabber/xmlnode.c705
-rw-r--r--protocols/jabber/xmlparse.c2629
-rw-r--r--protocols/jabber/xmlparse.h476
-rw-r--r--protocols/jabber/xmlrole.c1104
-rw-r--r--protocols/jabber/xmlrole.h111
-rw-r--r--protocols/jabber/xmltok.c1518
-rw-r--r--protocols/jabber/xmltok.h307
-rw-r--r--protocols/jabber/xmltok_impl.c1737
-rw-r--r--protocols/jabber/xmltok_impl.h71
-rw-r--r--protocols/jabber/xmltok_ns.c117
-rw-r--r--protocols/jabber/xstream.c0
-rw-r--r--protocols/md5.c392
-rw-r--r--protocols/md5.h85
-rw-r--r--protocols/msn/Makefile39
-rw-r--r--protocols/msn/msn.c402
-rw-r--r--protocols/msn/msn.h170
-rw-r--r--protocols/msn/msn_util.c359
-rw-r--r--protocols/msn/ns.c661
-rw-r--r--protocols/msn/passport.c313
-rw-r--r--protocols/msn/passport.h47
-rw-r--r--protocols/msn/sb.c659
-rw-r--r--protocols/msn/tables.c166
-rw-r--r--protocols/nogaim.c1082
-rw-r--r--protocols/nogaim.h347
-rw-r--r--protocols/oscar/AUTHORS31
-rw-r--r--protocols/oscar/COPYING504
-rw-r--r--protocols/oscar/Makefile37
-rw-r--r--protocols/oscar/admin.c196
-rw-r--r--protocols/oscar/admin.h13
-rw-r--r--protocols/oscar/aim.h921
-rw-r--r--protocols/oscar/aim_cbtypes.h0
-rw-r--r--protocols/oscar/aim_internal.h216
-rw-r--r--protocols/oscar/auth.c543
-rw-r--r--protocols/oscar/bos.c161
-rw-r--r--protocols/oscar/bos.h14
-rw-r--r--protocols/oscar/buddylist.c150
-rw-r--r--protocols/oscar/buddylist.h23
-rw-r--r--protocols/oscar/chat.c713
-rw-r--r--protocols/oscar/chat.h17
-rw-r--r--protocols/oscar/chatnav.c421
-rw-r--r--protocols/oscar/chatnav.h14
-rw-r--r--protocols/oscar/conn.c690
-rw-r--r--protocols/oscar/faimconfig.h0
-rw-r--r--protocols/oscar/ft.c2005
-rw-r--r--protocols/oscar/ft.h78
-rw-r--r--protocols/oscar/icq.c441
-rw-r--r--protocols/oscar/icq.h98
-rw-r--r--protocols/oscar/im.c2029
-rw-r--r--protocols/oscar/im.h198
-rw-r--r--protocols/oscar/info.c725
-rw-r--r--protocols/oscar/info.h44
-rw-r--r--protocols/oscar/misc.c396
-rw-r--r--protocols/oscar/msgcookie.c196
-rw-r--r--protocols/oscar/oscar.c2491
-rw-r--r--protocols/oscar/oscar_util.c167
-rw-r--r--protocols/oscar/rxhandlers.c408
-rw-r--r--protocols/oscar/rxqueue.c508
-rw-r--r--protocols/oscar/search.c121
-rw-r--r--protocols/oscar/search.h6
-rw-r--r--protocols/oscar/service.c946
-rw-r--r--protocols/oscar/snac.c145
-rw-r--r--protocols/oscar/ssi.c1523
-rw-r--r--protocols/oscar/ssi.h78
-rw-r--r--protocols/oscar/stats.c38
-rw-r--r--protocols/oscar/tlv.c600
-rw-r--r--protocols/oscar/txqueue.c440
-rw-r--r--protocols/proxy.c672
-rw-r--r--protocols/proxy.h60
-rw-r--r--protocols/sha.c173
-rw-r--r--protocols/sha.h21
-rw-r--r--protocols/ssl_bogus.c50
-rw-r--r--protocols/ssl_client.h35
-rw-r--r--protocols/ssl_gnutls.c136
-rw-r--r--protocols/ssl_nss.c180
-rw-r--r--protocols/ssl_openssl.c158
-rw-r--r--protocols/util.c415
-rw-r--r--protocols/yahoo/Makefile37
-rw-r--r--protocols/yahoo/crypt.c207
-rw-r--r--protocols/yahoo/libyahoo2.c4591
-rw-r--r--protocols/yahoo/yahoo.c958
-rw-r--r--protocols/yahoo/yahoo2.h222
-rw-r--r--protocols/yahoo/yahoo2_callbacks.h697
-rw-r--r--protocols/yahoo/yahoo2_types.h241
-rw-r--r--protocols/yahoo/yahoo_debug.h38
-rw-r--r--protocols/yahoo/yahoo_fn.c4622
-rw-r--r--protocols/yahoo/yahoo_fn.h33
-rw-r--r--protocols/yahoo/yahoo_httplib.c437
-rw-r--r--protocols/yahoo/yahoo_httplib.h48
-rw-r--r--protocols/yahoo/yahoo_list.c236
-rw-r--r--protocols/yahoo/yahoo_list.h74
-rw-r--r--protocols/yahoo/yahoo_util.c159
-rw-r--r--protocols/yahoo/yahoo_util.h102
120 files changed, 51723 insertions, 0 deletions
diff --git a/protocols/Makefile b/protocols/Makefile
new file mode 100644
index 00000000..c5f938fd
--- /dev/null
+++ b/protocols/Makefile
@@ -0,0 +1,49 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../Makefile.settings
+
+# [SH] Program variables
+objects = md5.o nogaim.o proxy.o sha.o util.o $(SSL_CLIENT)
+
+# [SH] The next two lines should contain the directory name (in $(subdirs))
+# and the name of the object file, which should be linked into
+# protocols.o (in $(subdirobjs)). These need to be in order, i.e. the
+# first object file should be in the first directory.
+subdirs = $(PROTOCOLS)
+subdirobjs = $(PROTOOBJS)
+
+# Expansion of variables
+subdirobjs := $(join $(subdirs),$(addprefix /,$(subdirobjs)))
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: protocols.o
+
+.PHONY: all clean distclean $(subdirs)
+
+clean: $(subdirs)
+ rm -f *.o $(OUTFILE) core
+
+distclean: clean $(subdirs)
+
+$(subdirs):
+ @$(MAKE) -C $@ $(MAKECMDGOALS)
+
+### MAIN PROGRAM
+
+protocols.o: $(objects) $(subdirs)
+ @echo '*' Linking protocols.o
+ @$(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o
+
+$(objects): ../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile
new file mode 100644
index 00000000..df326fe6
--- /dev/null
+++ b/protocols/jabber/Makefile
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = expat.o hashtable.o jid.o jpacket.o jutil.o log.o pool.o str.o xmlnode.o xmlparse.o xmlrole.o xmltok.o jabber.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: jabberr.o
+
+.PHONY: all clean distclean
+
+clean:
+ rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+jabberr.o: $(objects)
+ @echo '*' Linking jabberr.o
+ @$(LD) $(LFLAGS) $(objects) -o jabberr.o
diff --git a/protocols/jabber/asciitab.h b/protocols/jabber/asciitab.h
new file mode 100644
index 00000000..8a8a2dd3
--- /dev/null
+++ b/protocols/jabber/asciitab.h
@@ -0,0 +1,62 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/protocols/jabber/expat.c b/protocols/jabber/expat.c
new file mode 100644
index 00000000..0eedb321
--- /dev/null
+++ b/protocols/jabber/expat.c
@@ -0,0 +1,54 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "lib.h"
+#include <glib.h>
+
+void xmlnode_put_expat_attribs(xmlnode owner, const char** atts)
+{
+ int i = 0;
+ if (atts == NULL) return;
+ while (atts[i] != '\0')
+ {
+ xmlnode_put_attrib(owner, atts[i], atts[i+1]);
+ i += 2;
+ }
+}
diff --git a/protocols/jabber/genhash.c b/protocols/jabber/genhash.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/genhash.c
diff --git a/protocols/jabber/hashtable.c b/protocols/jabber/hashtable.c
new file mode 100644
index 00000000..82c33bc3
--- /dev/null
+++ b/protocols/jabber/hashtable.c
@@ -0,0 +1,142 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+csompliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+
+#ifdef XML_UNICODE_WCHAR_T
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+#endif
+
+#include "hashtable.h"
+
+#define INIT_SIZE 64
+
+static
+int keyeq(KEY s1, KEY s2)
+{
+ for (; *s1 == *s2; s1++, s2++)
+ if (*s1 == 0)
+ return 1;
+ return 0;
+}
+
+static
+unsigned long hash(KEY s)
+{
+ unsigned long h = 0;
+ while (*s)
+ h = (h << 5) + h + (unsigned char)*s++;
+ return h;
+}
+
+NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize)
+{
+ size_t i;
+ if (table->size == 0) {
+ if (!createSize)
+ return 0;
+ table->v = calloc(INIT_SIZE, sizeof(NAMED *));
+ if (!table->v)
+ return 0;
+ table->size = INIT_SIZE;
+ table->usedLim = INIT_SIZE / 2;
+ i = hash(name) & (table->size - 1);
+ }
+ else {
+ unsigned long h = hash(name);
+ for (i = h & (table->size - 1);
+ table->v[i];
+ i == 0 ? i = table->size - 1 : --i) {
+ if (keyeq(name, table->v[i]->name))
+ return table->v[i];
+ }
+ if (!createSize)
+ return 0;
+ if (table->used == table->usedLim) {
+ /* check for overflow */
+ size_t newSize = table->size * 2;
+ NAMED **newV = calloc(newSize, sizeof(NAMED *));
+ if (!newV)
+ return 0;
+ for (i = 0; i < table->size; i++)
+ if (table->v[i]) {
+ size_t j;
+ for (j = hash(table->v[i]->name) & (newSize - 1);
+ newV[j];
+ j == 0 ? j = newSize - 1 : --j)
+ ;
+ newV[j] = table->v[i];
+ }
+ g_free(table->v);
+ table->v = newV;
+ table->size = newSize;
+ table->usedLim = newSize/2;
+ for (i = h & (table->size - 1);
+ table->v[i];
+ i == 0 ? i = table->size - 1 : --i)
+ ;
+ }
+ }
+ table->v[i] = calloc(1, createSize);
+ if (!table->v[i])
+ return 0;
+ table->v[i]->name = name;
+ (table->used)++;
+ return table->v[i];
+}
+
+void hashTableDestroy(HASH_TABLE *table)
+{
+ size_t i;
+ for (i = 0; i < table->size; i++) {
+ NAMED *p = table->v[i];
+ if (p)
+ g_free(p);
+ }
+ g_free(table->v);
+}
+
+void hashTableInit(HASH_TABLE *p)
+{
+ p->size = 0;
+ p->usedLim = 0;
+ p->used = 0;
+ p->v = 0;
+}
+
+void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table)
+{
+ iter->p = table->v;
+ iter->end = iter->p + table->size;
+}
+
+NAMED *hashTableIterNext(HASH_TABLE_ITER *iter)
+{
+ while (iter->p != iter->end) {
+ NAMED *tem = *(iter->p)++;
+ if (tem)
+ return tem;
+ }
+ return 0;
+}
+
diff --git a/protocols/jabber/hashtable.h b/protocols/jabber/hashtable.h
new file mode 100644
index 00000000..df8ab8a4
--- /dev/null
+++ b/protocols/jabber/hashtable.h
@@ -0,0 +1,69 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+
+#include <stddef.h>
+
+#ifdef XML_UNICODE
+
+#ifdef XML_UNICODE_WCHAR_T
+typedef const wchar_t *KEY;
+#else /* not XML_UNICODE_WCHAR_T */
+typedef const unsigned short *KEY;
+#endif /* not XML_UNICODE_WCHAR_T */
+
+#else /* not XML_UNICODE */
+
+typedef const char *KEY;
+
+#endif /* not XML_UNICODE */
+
+typedef struct {
+ KEY name;
+} NAMED;
+
+typedef struct {
+ NAMED **v;
+ size_t size;
+ size_t used;
+ size_t usedLim;
+} HASH_TABLE;
+
+NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize);
+void hashTableInit(HASH_TABLE *);
+void hashTableDestroy(HASH_TABLE *);
+
+typedef struct {
+ NAMED **p;
+ NAMED **end;
+} HASH_TABLE_ITER;
+
+void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+NAMED *hashTableIterNext(HASH_TABLE_ITER *);
diff --git a/protocols/jabber/iasciitab.h b/protocols/jabber/iasciitab.h
new file mode 100644
index 00000000..333d6bb7
--- /dev/null
+++ b/protocols/jabber/iasciitab.h
@@ -0,0 +1,63 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
new file mode 100644
index 00000000..931a2182
--- /dev/null
+++ b/protocols/jabber/jabber.c
@@ -0,0 +1,2445 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef _WIN32
+#include <sys/utsname.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include "jabber.h"
+#include "nogaim.h"
+#include "bitlbee.h"
+#include "proxy.h"
+#include "ssl_client.h"
+
+/* The priv member of gjconn's is a gaim_connection for now. */
+#define GJ_GC(x) ((struct gaim_connection *)(x)->priv)
+
+#define IQID_AUTH "__AUTH__"
+
+#define IQ_NONE -1
+#define IQ_AUTH 0
+#define IQ_ROSTER 1
+
+#define UC_AWAY (0x02 | UC_UNAVAILABLE)
+#define UC_CHAT 0x04
+#define UC_XA (0x08 | UC_UNAVAILABLE)
+#define UC_DND (0x10 | UC_UNAVAILABLE)
+
+#define DEFAULT_SERVER "jabber.org"
+#define DEFAULT_GROUPCHAT "conference.jabber.org"
+#define DEFAULT_PORT 5222
+#define DEFAULT_PORT_SSL 5223
+
+#define JABBER_GROUP "Friends"
+
+/* i18n disabled - Bitlbee */
+#define N_(String) String
+
+/*
+ * Note: "was_connected" may seem redundant, but it was needed and I
+ * didn't want to touch the Jabber state stuff not specific to Gaim.
+ */
+typedef struct gjconn_struct {
+ /* Core structure */
+ pool p; /* Memory allocation pool */
+ int state; /* Connection state flag */
+ int was_connected; /* We were once connected */
+ int fd; /* Connection file descriptor */
+ void *ssl; /* SSL connection */
+ jid user; /* User info */
+ char *pass; /* User passwd */
+
+ /* Stream stuff */
+ int id; /* id counter for jab_getid() function */
+ char idbuf[9]; /* temporary storage for jab_getid() */
+ char *sid; /* stream id from server, for digest auth */
+ XML_Parser parser; /* Parser instance */
+ xmlnode current; /* Current node in parsing instance.. */
+
+ /* Event callback ptrs */
+ void (*on_state)(struct gjconn_struct *gjc, int state);
+ void (*on_packet)(struct gjconn_struct *gjc, jpacket p);
+
+ GHashTable *queries; /* query tracker */
+
+ void *priv;
+} *gjconn, gjconn_struct;
+
+typedef void (*gjconn_state_h)(gjconn gjc, int state);
+typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p);
+
+static gjconn gjab_new(char *user, char *pass, void *priv);
+static void gjab_delete(gjconn gjc);
+static void gjab_state_handler(gjconn gjc, gjconn_state_h h);
+static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h);
+static void gjab_start(gjconn gjc);
+static void gjab_stop(gjconn gjc);
+/*
+static int gjab_getfd(gjconn gjc);
+static jid gjab_getjid(gjconn gjc);
+static char *gjab_getsid(gjconn gjc);
+*/
+static char *gjab_getid(gjconn gjc);
+static void gjab_send(gjconn gjc, xmlnode x);
+static void gjab_send_raw(gjconn gjc, const char *str);
+static void gjab_recv(gjconn gjc);
+static void gjab_auth(gjconn gjc);
+
+/*
+ * It is *this* to which we point the gaim_connection proto_data
+ */
+struct jabber_data {
+ gjconn gjc;
+ gboolean did_import;
+ GSList *chats;
+ GHashTable *hash;
+ time_t idle;
+ gboolean die;
+};
+
+/*
+ * Jabber "chat group" info. Pointers to these go in jabber_data
+ * pending and existing chats lists.
+ */
+struct jabber_chat {
+ jid Jid;
+ struct gaim_connection *gc;
+ struct conversation *b;
+ int id;
+ int state;
+};
+
+/*
+ * Jabber chat states...
+ *
+ * Note: due to a bug in one version of the Jabber server, subscriptions
+ * to chat groups aren't (always?) properly removed at the server. The
+ * result is clients receive Jabber "presence" notifications for JIDs
+ * they no longer care about. The problem with such vestigial notifies is
+ * that we really have no way of telling if it's vestigial or if it's a
+ * valid "buddy" presence notification. So we keep jabber_chat structs
+ * around after leaving a chat group and simply mark them "closed." That
+ * way we can test for such errant presence notifications. I.e.: if we
+ * get a presence notfication from a JID that matches a chat group JID,
+ * we disregard it.
+ */
+#define JCS_PENDING 1 /* pending */
+#define JCS_ACTIVE 2 /* active */
+#define JCS_CLOSED 3 /* closed */
+
+
+static char *jabber_name()
+{
+ return "Jabber";
+}
+
+#define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); }
+
+static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group);
+static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from);
+
+static char *create_valid_jid(const char *given, char *server, char *resource)
+{
+ char *valid;
+
+ if (!strchr(given, '@'))
+ valid = g_strdup_printf("%s@%s/%s", given, server, resource);
+ else if (!strchr(strchr(given, '@'), '/'))
+ valid = g_strdup_printf("%s/%s", given, resource);
+ else
+ valid = g_strdup(given);
+
+ return valid;
+}
+
+static gjconn gjab_new(char *user, char *pass, void *priv)
+{
+ pool p;
+ gjconn gjc;
+
+ if (!user)
+ return (NULL);
+
+ p = pool_new();
+ if (!p)
+ return (NULL);
+ gjc = pmalloc_x(p, sizeof(gjconn_struct), 0);
+ if (!gjc) {
+ pool_free(p); /* no need for this anymore! */
+ return (NULL);
+ }
+ gjc->p = p;
+
+ if((gjc->user = jid_new(p, user)) == NULL) {
+ pool_free(p); /* no need for this anymore! */
+ return (NULL);
+ }
+ gjc->pass = pstrdup(p, pass);
+
+ gjc->state = JCONN_STATE_OFF;
+ gjc->was_connected = 0;
+ gjc->id = 1;
+ gjc->fd = -1;
+
+ gjc->priv = priv;
+
+ return gjc;
+}
+
+static void gjab_delete(gjconn gjc)
+{
+ if (!gjc)
+ return;
+
+ gjab_stop(gjc);
+ pool_free(gjc->p);
+}
+
+static void gjab_state_handler(gjconn gjc, gjconn_state_h h)
+{
+ if (!gjc)
+ return;
+
+ gjc->on_state = h;
+}
+
+static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h)
+{
+ if (!gjc)
+ return;
+
+ gjc->on_packet = h;
+}
+
+static void gjab_stop(gjconn gjc)
+{
+ if (!gjc || gjc->state == JCONN_STATE_OFF)
+ return;
+
+ gjab_send_raw(gjc, "</stream:stream>");
+ gjc->state = JCONN_STATE_OFF;
+ gjc->was_connected = 0;
+ if (gjc->ssl) {
+ ssl_disconnect(gjc->ssl);
+ gjc->ssl = NULL;
+ } else {
+ closesocket(gjc->fd);
+ }
+ gjc->fd = -1;
+ XML_ParserFree(gjc->parser);
+ gjc->parser = NULL;
+}
+
+/*
+static int gjab_getfd(gjconn gjc)
+{
+ if (gjc)
+ return gjc->fd;
+ else
+ return -1;
+}
+
+static jid gjab_getjid(gjconn gjc)
+{
+ if (gjc)
+ return (gjc->user);
+ else
+ return NULL;
+}
+
+static char *gjab_getsid(gjconn gjc)
+{
+ if (gjc)
+ return (gjc->sid);
+ else
+ return NULL;
+}
+*/
+
+static char *gjab_getid(gjconn gjc)
+{
+ g_snprintf(gjc->idbuf, 8, "%d", gjc->id++);
+ return &gjc->idbuf[0];
+}
+
+static void gjab_send(gjconn gjc, xmlnode x)
+{
+ if (gjc && gjc->state != JCONN_STATE_OFF) {
+ char *buf = xmlnode2str(x);
+ if (!buf)
+ return;
+ else if (gjc->ssl)
+ ssl_write(gjc->ssl, buf, strlen(buf));
+ else
+ write(gjc->fd, buf, strlen(buf));
+ }
+}
+
+static void gjab_send_raw(gjconn gjc, const char *str)
+{
+ if (gjc && gjc->state != JCONN_STATE_OFF) {
+ int len;
+
+ /*
+ * JFIXME: No error detection?!?!
+ */
+ if (gjc->ssl)
+ len = ssl_write(gjc->ssl, str, strlen(str));
+ else
+ len = write(gjc->fd, str, strlen(str));
+
+ if(len < 0) {
+ /* Do NOT write to stdout/stderr directly, IRC clients
+ might get confused, and we don't want that...
+ fprintf(stderr, "DBG: Problem sending. Error: %d\n", errno);
+ fflush(stderr); */
+ }
+ }
+}
+
+static void gjab_reqroster(gjconn gjc)
+{
+ xmlnode x;
+
+ x = jutil_iqnew(JPACKET__GET, NS_ROSTER);
+ xmlnode_put_attrib(x, "id", gjab_getid(gjc));
+
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+}
+
+static void gjab_reqauth(gjconn gjc)
+{
+ xmlnode x, y, z;
+ char *user;
+
+ if (!gjc)
+ return;
+
+ x = jutil_iqnew(JPACKET__GET, NS_AUTH);
+ xmlnode_put_attrib(x, "id", IQID_AUTH);
+ y = xmlnode_get_tag(x, "query");
+
+ user = gjc->user->user;
+
+ if (user) {
+ z = xmlnode_insert_tag(y, "username");
+ xmlnode_insert_cdata(z, user, -1);
+ }
+
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+}
+
+static void gjab_auth(gjconn gjc)
+{
+ xmlnode x, y, z;
+ char *hash, *user;
+
+ if (!gjc)
+ return;
+
+ x = jutil_iqnew(JPACKET__SET, NS_AUTH);
+ xmlnode_put_attrib(x, "id", IQID_AUTH);
+ y = xmlnode_get_tag(x, "query");
+
+ user = gjc->user->user;
+
+ if (user) {
+ z = xmlnode_insert_tag(y, "username");
+ xmlnode_insert_cdata(z, user, -1);
+ }
+
+ z = xmlnode_insert_tag(y, "resource");
+ xmlnode_insert_cdata(z, gjc->user->resource, -1);
+
+ if (gjc->sid) {
+ z = xmlnode_insert_tag(y, "digest");
+ hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1);
+ strcpy(hash, gjc->sid);
+ strcat(hash, gjc->pass);
+ hash = shahash(hash);
+ xmlnode_insert_cdata(z, hash, 40);
+ } else {
+ z = xmlnode_insert_tag(y, "password");
+ xmlnode_insert_cdata(z, gjc->pass, -1);
+ }
+
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+
+ return;
+}
+
+static void gjab_recv(gjconn gjc)
+{
+ static char buf[4096];
+ int len;
+
+ if (!gjc || gjc->state == JCONN_STATE_OFF)
+ return;
+
+ if (gjc->ssl)
+ len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1);
+ else
+ len = read(gjc->fd, buf, sizeof(buf) - 1);
+
+ if (len > 0) {
+ struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+ buf[len] = '\0';
+ XML_Parse(gjc->parser, buf, len, 0);
+ if (jd->die)
+ signoff(GJ_GC(gjc));
+ } else if (len < 0 || errno != EAGAIN) {
+ STATE_EVT(JCONN_STATE_OFF)
+ }
+}
+
+static void startElement(void *userdata, const char *name, const char **attribs)
+{
+ xmlnode x;
+ gjconn gjc = (gjconn) userdata;
+
+ if (gjc->current) {
+ /* Append the node to the current one */
+ x = xmlnode_insert_tag(gjc->current, name);
+ xmlnode_put_expat_attribs(x, attribs);
+
+ gjc->current = x;
+ } else {
+ x = xmlnode_new_tag(name);
+ xmlnode_put_expat_attribs(x, attribs);
+ if (strcmp(name, "stream:stream") == 0) {
+ /* special case: name == stream:stream */
+ /* id attrib of stream is stored for digest auth */
+ gjc->sid = g_strdup(xmlnode_get_attrib(x, "id"));
+ /* STATE_EVT(JCONN_STATE_AUTH) */
+ xmlnode_free(x);
+ } else {
+ gjc->current = x;
+ }
+ }
+}
+
+static void endElement(void *userdata, const char *name)
+{
+ gjconn gjc = (gjconn) userdata;
+ xmlnode x;
+ jpacket p;
+
+ if (gjc->current == NULL) {
+ /* we got </stream:stream> */
+ STATE_EVT(JCONN_STATE_OFF)
+ return;
+ }
+
+ x = xmlnode_get_parent(gjc->current);
+
+ if (!x) {
+ /* it is time to fire the event */
+ p = jpacket_new(gjc->current);
+
+ if (gjc->on_packet)
+ (gjc->on_packet) (gjc, p);
+ else
+ xmlnode_free(gjc->current);
+ }
+
+ gjc->current = x;
+}
+
+static void jabber_callback(gpointer data, gint source, GaimInputCondition condition)
+{
+ struct gaim_connection *gc = (struct gaim_connection *)data;
+ struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+
+ gjab_recv(jd->gjc);
+}
+
+static void charData(void *userdata, const char *s, int slen)
+{
+ gjconn gjc = (gjconn) userdata;
+
+ if (gjc->current)
+ xmlnode_insert_cdata(gjc->current, s, slen);
+}
+
+static void gjab_connected(gpointer data, gint source, GaimInputCondition cond)
+{
+ xmlnode x;
+ char *t, *t2;
+ struct gaim_connection *gc = data;
+ struct jabber_data *jd;
+ gjconn gjc;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ return;
+ }
+
+ jd = gc->proto_data;
+ gjc = jd->gjc;
+
+ if (gjc->fd != source)
+ gjc->fd = source;
+
+ if (source == -1) {
+ STATE_EVT(JCONN_STATE_OFF)
+ return;
+ }
+
+ gjc->state = JCONN_STATE_CONNECTED;
+ STATE_EVT(JCONN_STATE_CONNECTED)
+
+ /* start stream */
+ x = jutil_header(NS_CLIENT, gjc->user->server);
+ t = xmlnode2str(x);
+ /* this is ugly, we can create the string here instead of jutil_header */
+ /* what do you think about it? -madcat */
+ t2 = strstr(t, "/>");
+ *t2++ = '>';
+ *t2 = '\0';
+ gjab_send_raw(gjc, "<?xml version='1.0'?>");
+ gjab_send_raw(gjc, t);
+ xmlnode_free(x);
+
+ gjc->state = JCONN_STATE_ON;
+ STATE_EVT(JCONN_STATE_ON);
+
+ gc = GJ_GC(gjc);
+ gc->inpa = gaim_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc);
+}
+
+static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition cond)
+{
+ struct gaim_connection *gc = data;
+ struct jabber_data *jd;
+ gjconn gjc;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ ssl_disconnect(source);
+ return;
+ }
+
+ jd = gc->proto_data;
+ gjc = jd->gjc;
+
+ if (source == NULL) {
+ STATE_EVT(JCONN_STATE_OFF)
+ return;
+ }
+
+ gjab_connected(data, gjc->fd, cond);
+}
+
+static void gjab_start(gjconn gjc)
+{
+ struct aim_user *user;
+ int port = -1, ssl = 0;
+ char *server = NULL, *s;
+
+ if (!gjc || gjc->state != JCONN_STATE_OFF)
+ return;
+
+ user = GJ_GC(gjc)->user;
+ if (*user->proto_opt[0]) {
+ /* If there's a dot, assume there's a hostname in the beginning */
+ if (strchr(user->proto_opt[0], '.')) {
+ server = g_strdup(user->proto_opt[0]);
+ if ((s = strchr(server, ':')))
+ *s = 0;
+ }
+
+ /* After the hostname, there can be a port number */
+ s = strchr(user->proto_opt[0], ':');
+ if (s && isdigit(s[1]))
+ sscanf(s + 1, "%d", &port);
+
+ /* And if there's the string ssl, the user wants an SSL-connection */
+ if (strstr(user->proto_opt[0], ":ssl") || g_strcasecmp(user->proto_opt[0], "ssl") == 0)
+ ssl = 1;
+ }
+
+ if (port == -1 && !ssl)
+ port = DEFAULT_PORT;
+ else if (port == -1 && ssl)
+ port = DEFAULT_PORT_SSL;
+
+ if (server == NULL)
+ server = g_strdup(gjc->user->server);
+
+ gjc->parser = XML_ParserCreate(NULL);
+ XML_SetUserData(gjc->parser, (void *)gjc);
+ XML_SetElementHandler(gjc->parser, startElement, endElement);
+ XML_SetCharacterDataHandler(gjc->parser, charData);
+
+ if (ssl) {
+ if ((gjc->ssl = ssl_connect(server, port, gjab_connected_ssl, GJ_GC(gjc))))
+ gjc->fd = ssl_getfd(gjc->ssl);
+ else
+ gjc->fd = -1;
+ } else {
+ gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc));
+ }
+
+ g_free(server);
+
+ if (!user->gc || (gjc->fd < 0)) {
+ STATE_EVT(JCONN_STATE_OFF)
+ return;
+ }
+}
+
+/*
+ * Find existing/active Jabber chat
+ */
+static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat)
+{
+ GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats;
+ struct jabber_chat *jc = NULL;
+
+ while (jcs) {
+ jc = jcs->data;
+ if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER))
+ break;
+ jc = NULL;
+ jcs = jcs->next;
+ }
+
+ return jc;
+}
+
+/*
+ * Find pending chat
+ */
+static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat)
+{
+ GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats;
+ struct jabber_chat *jc = NULL;
+
+ while (jcs) {
+ jc = jcs->data;
+ if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER))
+ break;
+ jc = NULL;
+ jcs = jcs->next;
+ }
+
+ return jc;
+}
+
+static gboolean find_chat_buddy(struct conversation *b, char *name)
+{
+ GList *m = b->in_room;
+
+ while (m) {
+ if (!strcmp(m->data, name))
+ return TRUE;
+ m = m->next;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Remove a buddy from the (gaim) buddylist (if he's on it)
+ */
+static void jabber_remove_gaim_buddy(struct gaim_connection *gc, char *buddyname)
+{
+ struct buddy *b;
+
+ if ((b = find_buddy(gc, buddyname)) != NULL) {
+ /* struct group *group;
+
+ group = find_group_by_buddy(gc, buddyname);
+ remove_buddy(gc, group, b); */
+ jabber_remove_buddy(gc, b->name, JABBER_GROUP);
+ }
+}
+
+/*
+ * keep track of away msg same as yahoo plugin
+ */
+static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type)
+{
+ struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+ gpointer val = g_hash_table_lookup(jd->hash, name);
+ char *show;
+ char *vshow = NULL;
+ char *status = NULL;
+ char *msg = NULL;
+
+ if (type && (g_strcasecmp(type, "unavailable") == 0)) {
+ vshow = _("Unavailable");
+ } else {
+ if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) {
+ if (!g_strcasecmp(show, "away")) {
+ vshow = _("Away");
+ } else if (!g_strcasecmp(show, "chat")) {
+ vshow = _("Online");
+ } else if (!g_strcasecmp(show, "xa")) {
+ vshow = _("Extended Away");
+ } else if (!g_strcasecmp(show, "dnd")) {
+ vshow = _("Do Not Disturb");
+ }
+ }
+ }
+
+ status = xmlnode_get_tag_data(p->x, "status");
+
+ if(vshow != NULL || status != NULL ) {
+ /* kinda hokey, but it works :-) */
+ msg = g_strdup_printf("%s%s%s",
+ (vshow == NULL? "" : vshow),
+ (vshow == NULL || status == NULL? "" : ": "),
+ (status == NULL? "" : status));
+ } else {
+ msg = g_strdup(_("Online"));
+ }
+
+ if (val) {
+ g_free(val);
+ g_hash_table_insert(jd->hash, name, msg);
+ } else {
+ g_hash_table_insert(jd->hash, g_strdup(name), msg);
+ }
+}
+
+static time_t iso8601_to_time(char *timestamp)
+{
+ struct tm t;
+ time_t retval = 0;
+
+ if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d",
+ &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec))
+ {
+ t.tm_year -= 1900;
+ t.tm_mon -= 1;
+ t.tm_isdst = 0;
+ retval = mktime(&t);
+# ifdef HAVE_TM_GMTOFF
+ retval += t.tm_gmtoff;
+# else
+# ifdef HAVE_TIMEZONE
+ tzset(); /* making sure */
+ retval -= timezone;
+# endif
+# endif
+ }
+
+ return retval;
+}
+
+static void jabber_handlemessage(gjconn gjc, jpacket p)
+{
+ xmlnode y, xmlns, subj, z;
+ time_t time_sent = time(NULL);
+
+ char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL;
+ char m[BUF_LONG * 2];
+
+ type = xmlnode_get_attrib(p->x, "type");
+
+ z = xmlnode_get_firstchild(p->x);
+
+ while(z)
+ {
+ if(NSCHECK(z,NS_DELAY))
+ {
+ char *timestamp = xmlnode_get_attrib(z,"stamp");
+ time_sent = iso8601_to_time(timestamp);
+ }
+ z = xmlnode_get_nextsibling(z);
+ }
+
+ if (!type || !g_strcasecmp(type, "normal") || !g_strcasecmp(type, "chat")) {
+
+ /* XXX namespaces could be handled better. (mid) */
+ if ((xmlns = xmlnode_get_tag(p->x, "x")))
+ type = xmlnode_get_attrib(xmlns, "xmlns");
+
+ from = jid_full(p->from);
+ /*
+ if ((y = xmlnode_get_tag(p->x, "html"))) {
+ msg = xmlnode_get_data(y);
+ } else
+ */
+ if ((y = xmlnode_get_tag(p->x, "body"))) {
+ msg = xmlnode_get_data(y);
+ }
+
+
+ if (!from)
+ return;
+
+ if (type && !g_strcasecmp(type, "jabber:x:conference")) {
+ char *room;
+ GList *m = NULL;
+ char **data;
+
+ room = xmlnode_get_attrib(xmlns, "jid");
+ data = g_strsplit(room, "@", 2);
+ m = g_list_append(m, g_strdup(data[0]));
+ m = g_list_append(m, g_strdup(data[1]));
+ m = g_list_append(m, g_strdup(gjc->user->user));
+ g_strfreev(data);
+
+ /* ** Bitlbee ** serv_got_chat_invite(GJ_GC(gjc), room, from, msg, m); */
+ } else if (msg) { /* whisper */
+ struct jabber_chat *jc;
+ g_snprintf(m, sizeof(m), "%s", msg);
+ if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b)
+ serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent);
+ else {
+ int flags = 0;
+ /* ** Bitlbee **
+ if (xmlnode_get_tag(p->x, "gaim"))
+ flags = IM_FLAG_GAIMUSER;
+ if (find_conversation(jid_full(p->from)))
+ serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, time_sent, -1);
+ else {
+ ** End - Bitlbee ** */
+ if(p->from->user) {
+ from = g_strdup_printf("%s@%s", p->from->user, p->from->server);
+ } else {
+ /* server message? */
+ from = g_strdup(p->from->server);
+ }
+ serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1);
+ g_free(from);
+ /* ** Bitlbee ** } ** End - Bitlbee ** */
+ }
+ }
+
+ } else if (!g_strcasecmp(type, "error")) {
+ if ((y = xmlnode_get_tag(p->x, "error"))) {
+ type = xmlnode_get_attrib(y, "code");
+ msg = xmlnode_get_data(y);
+ }
+
+ if (msg) {
+ from = g_strdup_printf("Error %s", type ? type : "");
+ do_error_dialog(GJ_GC(gjc), msg, from);
+ g_free(from);
+ }
+ } else if (!g_strcasecmp(type, "groupchat")) {
+ struct jabber_chat *jc;
+ static int i = 0;
+
+ /*
+ if ((y = xmlnode_get_tag(p->x, "html"))) {
+ msg = xmlnode_get_data(y);
+ } else
+ */
+ if ((y = xmlnode_get_tag(p->x, "body"))) {
+ msg = xmlnode_get_data(y);
+ }
+
+ msg = utf8_to_str(msg);
+
+ if ((subj = xmlnode_get_tag(p->x, "subject"))) {
+ topic = xmlnode_get_data(subj);
+ }
+ topic = utf8_to_str(topic);
+
+ jc = find_existing_chat(GJ_GC(gjc), p->from);
+ if (!jc) {
+ /* we're not in this chat. are we supposed to be? */
+ if ((jc = find_pending_chat(GJ_GC(gjc), p->from)) != NULL) {
+ /* yes, we're supposed to be. so now we are. */
+ jc->b = serv_got_joined_chat(GJ_GC(gjc), i++, p->from->user);
+ jc->id = jc->b->id;
+ jc->state = JCS_ACTIVE;
+ } else {
+ /* no, we're not supposed to be. */
+ g_free(msg);
+ return;
+ }
+ }
+ if (p->from->resource) {
+ if (!y) {
+ if (!find_chat_buddy(jc->b, p->from->resource)) {
+ add_chat_buddy(jc->b, p->from->resource);
+ } else if ((y = xmlnode_get_tag(p->x, "status"))) {
+ char *buf;
+
+ buf = g_strdup_printf("%s@%s/%s",
+ p->from->user, p->from->server, p->from->resource);
+ jabber_track_away(gjc, p, buf, NULL);
+ g_free(buf);
+
+ }
+ } else if (jc->b && msg) {
+ char buf[8192];
+
+ if (topic) {
+ char tbuf[8192];
+ g_snprintf(tbuf, sizeof(tbuf), "%s", topic);
+ }
+
+
+ g_snprintf(buf, sizeof(buf), "%s", msg);
+ serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, time_sent);
+ }
+ } else { /* message from the server */
+ if(jc->b && topic) {
+ char tbuf[8192];
+ g_snprintf(tbuf, sizeof(tbuf), "%s", topic);
+ }
+ }
+
+ g_free(msg);
+ g_free(topic);
+
+ }
+}
+
+static void jabber_handlepresence(gjconn gjc, jpacket p)
+{
+ char *to, *from, *type;
+ struct buddy *b = NULL;
+ jid who;
+ char *buddy;
+ xmlnode y;
+ char *show;
+ int state = 0;
+ GSList *resources;
+ char *res;
+ struct conversation *cnv = NULL;
+ struct jabber_chat *jc = NULL;
+
+ to = xmlnode_get_attrib(p->x, "to");
+ from = xmlnode_get_attrib(p->x, "from");
+ type = xmlnode_get_attrib(p->x, "type");
+
+ if (type && g_strcasecmp(type, "error") == 0) {
+ return;
+ }
+ else if ((y = xmlnode_get_tag(p->x, "show"))) {
+ show = xmlnode_get_data(y);
+ if (!show) {
+ state = 0;
+ } else if (!g_strcasecmp(show, "away")) {
+ state = UC_AWAY;
+ } else if (!g_strcasecmp(show, "chat")) {
+ state = UC_CHAT;
+ } else if (!g_strcasecmp(show, "xa")) {
+ state = UC_XA;
+ } else if (!g_strcasecmp(show, "dnd")) {
+ state = UC_DND;
+ }
+ } else {
+ state = 0;
+ }
+
+ who = jid_new(gjc->p, from);
+ if (who->user == NULL) {
+ /* FIXME: transport */
+ return;
+ }
+
+ buddy = g_strdup_printf("%s@%s", who->user, who->server);
+
+ /* um. we're going to check if it's a chat. if it isn't, and there are pending
+ * chats, create the chat. if there aren't pending chats and we don't have the
+ * buddy on our list, simply bail out. */
+ if ((cnv = NULL) == NULL) {
+ static int i = 0x70;
+ if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) {
+ jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user);
+ jc->id = jc->b->id;
+ jc->state = JCS_ACTIVE;
+ } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) {
+ g_free(buddy);
+ return;
+ }
+ }
+
+ if (!cnv) {
+ resources = b->proto_data;
+ res = who->resource;
+ if (res)
+ while (resources) {
+ if (!strcmp(res, resources->data))
+ break;
+ resources = resources->next;
+ }
+
+ /* keep track of away msg same as yahoo plugin */
+ jabber_track_away(gjc, p, normalize(b->name), type);
+
+ if (type && (g_strcasecmp(type, "unavailable") == 0)) {
+ if (resources) {
+ g_free(resources->data);
+ b->proto_data = g_slist_remove(b->proto_data, resources->data);
+ }
+ if (!b->proto_data) {
+ serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0);
+ }
+ } else {
+ if (!resources) {
+ b->proto_data = g_slist_append(b->proto_data, g_strdup(res));
+ }
+
+ serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0);
+
+ }
+ } else {
+ if (who->resource) {
+ char *buf;
+
+ buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);
+ jabber_track_away(gjc, p, buf, type);
+ g_free(buf);
+
+ if (type && !g_strcasecmp(type, "unavailable")) {
+ struct jabber_data *jd;
+ if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) {
+ g_free(buddy);
+ return;
+ }
+ jd = jc->gc->proto_data;
+ /* if it's not ourselves...*/
+ if (strcmp(who->resource, jc->Jid->resource) && jc->b) {
+ remove_chat_buddy(jc->b, who->resource, NULL);
+ g_free(buddy);
+ return;
+ }
+
+ jc->state = JCS_CLOSED;
+ serv_got_chat_left(GJ_GC(gjc), jc->id);
+ /*
+ * TBD: put back some day?
+ jd->chats = g_slist_remove(jd->chats, jc);
+ g_free(jc);
+ */
+ } else {
+ if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) {
+ g_free(buddy);
+ return;
+ }
+ if (!find_chat_buddy(jc->b, who->resource)) {
+ add_chat_buddy(jc->b, who->resource);
+ }
+ }
+ }
+ }
+
+ g_free(buddy);
+
+ return;
+}
+
+/*
+ * Used only by Jabber accept/deny add stuff just below
+ */
+struct jabber_add_permit {
+ gjconn gjc;
+ gchar *user;
+};
+
+/*
+ * Common part for Jabber accept/deny adds
+ *
+ * "type" says whether we'll permit/deny the subscribe request
+ */
+static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type)
+{
+ xmlnode g = xmlnode_new_tag("presence");
+
+ xmlnode_put_attrib(g, "to", jap->user);
+ xmlnode_put_attrib(g, "type", type);
+ gjab_send(jap->gjc, g);
+
+ xmlnode_free(g);
+}
+
+/*
+ * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n()
+ */
+static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap)
+{
+ jabber_accept_deny_add(jap, "subscribed");
+ /*
+ * If we don't already have the buddy on *our* buddylist,
+ * ask if we want him or her added.
+ */
+ if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) {
+ show_got_added(GJ_GC(jap->gjc), NULL, jap->user, NULL, NULL);
+ }
+ g_free(jap->user);
+ g_free(jap);
+}
+
+/*
+ * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n()
+ */
+static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap)
+{
+ jabber_accept_deny_add(jap, "unsubscribed");
+ g_free(jap->user);
+ g_free(jap);
+}
+
+/*
+ * Handle subscription requests
+ */
+static void jabber_handles10n(gjconn gjc, jpacket p)
+{
+ xmlnode g;
+ char *Jid = xmlnode_get_attrib(p->x, "from");
+ char *type = xmlnode_get_attrib(p->x, "type");
+
+ g = xmlnode_new_tag("presence");
+ xmlnode_put_attrib(g, "to", Jid);
+
+ if (!strcmp(type, "subscribe")) {
+ /*
+ * A "subscribe to us" request was received - put up the approval dialog
+ */
+ struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1);
+ gchar *msg = g_strdup_printf(_("The user %s wants to add you to their buddy list."),
+ Jid);
+
+ jap->gjc = gjc;
+ jap->user = g_strdup(Jid);
+ do_ask_dialog(GJ_GC(gjc), msg, jap, jabber_accept_add, jabber_deny_add);
+
+ g_free(msg);
+ xmlnode_free(g); /* Never needed it here anyway */
+ return;
+
+ } else if (!strcmp(type, "unsubscribe")) {
+ /*
+ * An "unsubscribe to us" was received - simply "approve" it
+ */
+ xmlnode_put_attrib(g, "type", "unsubscribed");
+ } else {
+ /*
+ * Did we attempt to subscribe to somebody and they do not exist?
+ */
+ if (!strcmp(type, "unsubscribed")) {
+ xmlnode y;
+ char *status;
+ if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) &&
+ !strcmp(status, "Not Found")) {
+ char *msg = g_strdup_printf("%s: \"%s\"", _("No such user"),
+ xmlnode_get_attrib(p->x, "from"));
+ do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error"));
+ g_free(msg);
+ }
+ }
+
+ xmlnode_free(g);
+ return;
+ }
+
+ gjab_send(gjc, g);
+ xmlnode_free(g);
+}
+
+/*
+ * Pending subscription to a buddy?
+ */
+#define BUD_SUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \
+ (ask) != NULL && !g_strcasecmp((ask), "subscribe"))
+
+/*
+ * Subscribed to a buddy?
+ */
+#define BUD_SUBD_TO(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \
+ ((ask) == NULL || !g_strcasecmp((ask), "subscribe")))
+
+/*
+ * Pending unsubscription to a buddy?
+ */
+#define BUD_USUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \
+ (ask) != NULL && !g_strcasecmp((ask), "unsubscribe"))
+
+/*
+ * Unsubscribed to a buddy?
+ */
+#define BUD_USUBD_TO(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \
+ ((ask) == NULL || !g_strcasecmp((ask), "unsubscribe")))
+
+/*
+ * If a buddy is added or removed from the roster on another resource
+ * jabber_handlebuddy is called
+ *
+ * Called with roster item node.
+ */
+static void jabber_handlebuddy(gjconn gjc, xmlnode x)
+{
+ xmlnode g;
+ char *Jid, *name, *sub, *ask;
+ jid who;
+ struct buddy *b = NULL;
+ char *buddyname, *groupname = NULL;
+
+ Jid = xmlnode_get_attrib(x, "jid");
+ name = xmlnode_get_attrib(x, "name");
+ sub = xmlnode_get_attrib(x, "subscription");
+ ask = xmlnode_get_attrib(x, "ask");
+ who = jid_new(gjc->p, Jid);
+
+ /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this
+ * equivilent point. So...
+ *
+ * We haven't allocated any memory or done anything interesting to
+ * this point, so we'll violate Good Coding Structure here by
+ * simply bailing out.
+ */
+ if (!who || !who->user) {
+ return;
+ }
+
+ buddyname = g_strdup_printf("%s@%s", who->user, who->server);
+
+ if((g = xmlnode_get_tag(x, "group")) != NULL) {
+ groupname = xmlnode_get_data(g);
+ }
+
+ /*
+ * Add or remove a buddy? Change buddy's alias or group?
+ */
+ if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) {
+ if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) {
+ add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname,
+ name ? name : buddyname);
+ } else {
+ /* struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); */
+
+ /*
+ * If the buddy's in a new group or his/her alias is changed...
+ */
+ if(groupname) {
+ int present = b->present; /* save presence state */
+ int uc = b->uc; /* and away state (?) */
+ int idle = b->idle;
+ int signon = b->signon;
+
+ /*
+ * seems rude, but it seems to be the only way...
+ */
+ /* remove_buddy(GJ_GC(gjc), c_grp, b); */
+ jabber_remove_buddy(GJ_GC(gjc), b->name, JABBER_GROUP);
+
+ add_buddy(GJ_GC(gjc), groupname, buddyname,
+ name ? name : buddyname);
+ if(present) {
+ serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0);
+ }
+ } else if(name != NULL && strcmp(b->show, name)) {
+ strncpy(b->show, name, BUDDY_ALIAS_MAXLEN);
+ b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */
+ serv_buddy_rename(GJ_GC(gjc), buddyname, b->show);
+ }
+ }
+ } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !g_strcasecmp(sub, "remove")) {
+ jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname);
+ }
+ g_free(buddyname);
+
+}
+
+static void jabber_handleroster(gjconn gjc, xmlnode querynode)
+{
+ xmlnode x;
+
+ x = xmlnode_get_firstchild(querynode);
+ while (x) {
+ jabber_handlebuddy(gjc, x);
+ x = xmlnode_get_nextsibling(x);
+ }
+
+ x = jutil_presnew(0, NULL, "Online");
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+}
+
+static void jabber_handleauthresp(gjconn gjc, jpacket p)
+{
+ if (jpacket_subtype(p) == JPACKET__RESULT) {
+ if (xmlnode_has_children(p->x)) {
+ xmlnode query = xmlnode_get_tag(p->x, "query");
+ set_login_progress(GJ_GC(gjc), 4, _("Authenticating"));
+ if (!xmlnode_get_tag(query, "digest")) {
+ g_free(gjc->sid);
+ gjc->sid = NULL;
+ }
+ gjab_auth(gjc);
+ } else {
+ account_online(GJ_GC(gjc));
+
+ if (bud_list_cache_exists(GJ_GC(gjc)))
+ do_import(GJ_GC(gjc), NULL);
+
+ ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE;
+
+ gjab_reqroster(gjc);
+ }
+ } else {
+ xmlnode xerr;
+ char *errmsg = NULL;
+ int errcode = 0;
+ struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+
+ xerr = xmlnode_get_tag(p->x, "error");
+ if (xerr) {
+ char msg[BUF_LONG];
+ errmsg = xmlnode_get_data(xerr);
+ if (xmlnode_get_attrib(xerr, "code")) {
+ errcode = atoi(xmlnode_get_attrib(xerr, "code"));
+ g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg ? errmsg : "Unknown error");
+ } else
+ g_snprintf(msg, sizeof(msg), "%s", errmsg);
+ hide_login_progress(GJ_GC(gjc), msg);
+ } else {
+ hide_login_progress(GJ_GC(gjc), _("Unknown login error"));
+ }
+
+ jd->die = TRUE;
+ }
+}
+
+static void jabber_handleversion(gjconn gjc, xmlnode iqnode) {
+ xmlnode querynode, x;
+ char *id, *from;
+ char os[1024];
+#ifndef _WIN32
+ struct utsname osinfo;
+
+ uname(&osinfo);
+ g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine);
+#else
+ g_snprintf(os, sizeof os, "Windows %d %d", _winmajor, _winminor);
+#endif
+
+
+ id = xmlnode_get_attrib(iqnode, "id");
+ from = xmlnode_get_attrib(iqnode, "from");
+
+ x = jutil_iqnew(JPACKET__RESULT, NS_VERSION);
+
+ xmlnode_put_attrib(x, "to", from);
+ xmlnode_put_attrib(x, "id", id);
+ querynode = xmlnode_get_tag(x, "query");
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1);
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), BITLBEE_VERSION, -1);
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1);
+
+ gjab_send(gjc, x);
+
+ xmlnode_free(x);
+}
+
+static void jabber_handletime(gjconn gjc, xmlnode iqnode) {
+ xmlnode querynode, x;
+ char *id, *from;
+ time_t now_t;
+ struct tm *now;
+ char buf[1024];
+
+ time(&now_t);
+ now = localtime(&now_t);
+
+ id = xmlnode_get_attrib(iqnode, "id");
+ from = xmlnode_get_attrib(iqnode, "from");
+
+ x = jutil_iqnew(JPACKET__RESULT, NS_TIME);
+
+ xmlnode_put_attrib(x, "to", from);
+ xmlnode_put_attrib(x, "id", id);
+ querynode = xmlnode_get_tag(x, "query");
+
+ strftime(buf, 1024, "%Y%m%dT%T", now);
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1);
+ strftime(buf, 1024, "%Z", now);
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1);
+ strftime(buf, 1024, "%d %b %Y %T", now);
+ xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1);
+
+ gjab_send(gjc, x);
+
+ xmlnode_free(x);
+}
+
+static void jabber_handlelast(gjconn gjc, xmlnode iqnode) {
+ xmlnode x, querytag;
+ char *id, *from;
+ struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+ char idle_time[32];
+
+ id = xmlnode_get_attrib(iqnode, "id");
+ from = xmlnode_get_attrib(iqnode, "from");
+
+ x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last");
+
+ xmlnode_put_attrib(x, "to", from);
+ xmlnode_put_attrib(x, "id", id);
+ querytag = xmlnode_get_tag(x, "query");
+ g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0);
+ xmlnode_put_attrib(querytag, "seconds", idle_time);
+
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+}
+
+/*
+ * delete == TRUE: delete found entry
+ *
+ * returns pointer to (local) copy of value if found, NULL otherwise
+ *
+ * Note: non-reentrant! Local static storage re-used on subsequent calls.
+ * If you're going to need to keep the returned value, make a copy!
+ */
+static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete)
+{
+ gpointer my_key, my_val;
+ static gchar *ret_val = NULL;
+
+ if(ret_val != NULL) {
+ g_free(ret_val);
+ ret_val = NULL;
+ }
+
+ /* self-protection */
+ if(queries != NULL && key != NULL) {
+ if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) {
+ ret_val = g_strdup((gchar *) my_val);
+ if(delete) {
+ g_hash_table_remove(queries, key);
+ g_free(my_key);
+ g_free(my_val);
+ }
+ }
+ }
+
+ return(ret_val);
+}
+
+static void jabber_handlepacket(gjconn gjc, jpacket p)
+{
+ char *id;
+ switch (p->type) {
+ case JPACKET_MESSAGE:
+ jabber_handlemessage(gjc, p);
+ break;
+ case JPACKET_PRESENCE:
+ jabber_handlepresence(gjc, p);
+ break;
+ case JPACKET_IQ:
+ id = xmlnode_get_attrib(p->x, "id");
+ if (id != NULL && !strcmp(id, IQID_AUTH)) {
+ jabber_handleauthresp(gjc, p);
+ break;
+ }
+
+ if (jpacket_subtype(p) == JPACKET__SET) {
+ xmlnode querynode;
+ querynode = xmlnode_get_tag(p->x, "query");
+ if (NSCHECK(querynode, "jabber:iq:roster")) {
+ jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode));
+ }
+ } else if (jpacket_subtype(p) == JPACKET__GET) {
+ xmlnode querynode;
+ querynode = xmlnode_get_tag(p->x, "query");
+ if (NSCHECK(querynode, NS_VERSION)) {
+ jabber_handleversion(gjc, p->x);
+ } else if (NSCHECK(querynode, NS_TIME)) {
+ jabber_handletime(gjc, p->x);
+ } else if (NSCHECK(querynode, "jabber:iq:last")) {
+ jabber_handlelast(gjc, p->x);
+ }
+ } else if (jpacket_subtype(p) == JPACKET__RESULT) {
+ xmlnode querynode, vcard;
+ /* char *xmlns; */
+ char *from;
+
+ /*
+ * TBD: ISTM maybe this part could use a serious re-work?
+ */
+ from = xmlnode_get_attrib(p->x, "from");
+ querynode = xmlnode_get_tag(p->x, "query");
+ vcard = xmlnode_get_tag(p->x, "vCard");
+ if (!vcard)
+ vcard = xmlnode_get_tag(p->x, "VCARD");
+
+ if (NSCHECK(querynode, NS_ROSTER)) {
+ jabber_handleroster(gjc, querynode);
+ } else if (NSCHECK(querynode, NS_VCARD)) {
+ jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */
+ jabber_handlevcard(gjc, querynode, from);
+ } else if (vcard) {
+ jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */
+ jabber_handlevcard(gjc, vcard, from);
+ } else {
+ char *val;
+
+ /* handle "null" query results */
+ if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) {
+ if (!g_strncasecmp(val, "vcard", 5)) {
+ jabber_handlevcard(gjc, NULL, from);
+ }
+
+ /* No-op */
+ }
+ }
+
+ } else if (jpacket_subtype(p) == JPACKET__ERROR) {
+ xmlnode xerr;
+ char *from, *errmsg = NULL;
+ int errcode = 0;
+
+ from = xmlnode_get_attrib(p->x, "from");
+ xerr = xmlnode_get_tag(p->x, "error");
+ if (xerr) {
+ errmsg = xmlnode_get_data(xerr);
+ if (xmlnode_get_attrib(xerr, "code"))
+ errcode = atoi(xmlnode_get_attrib(xerr, "code"));
+ }
+
+ from = g_strdup_printf("Error %d (%s)", errcode, from);
+ do_error_dialog(GJ_GC(gjc), errmsg, from);
+ g_free(from);
+
+ }
+
+ break;
+ case JPACKET_S10N:
+ jabber_handles10n(gjc, p);
+ break;
+ }
+
+ xmlnode_free(p->x);
+
+ return;
+}
+
+static void jabber_handlestate(gjconn gjc, int state)
+{
+ switch (state) {
+ case JCONN_STATE_OFF:
+ if(gjc->was_connected) {
+ hide_login_progress_error(GJ_GC(gjc), _("Connection lost"));
+ } else {
+ hide_login_progress(GJ_GC(gjc), _("Unable to connect"));
+ }
+ signoff(GJ_GC(gjc));
+ break;
+ case JCONN_STATE_CONNECTED:
+ gjc->was_connected = 1;
+ set_login_progress(GJ_GC(gjc), 2, _("Connected"));
+ break;
+ case JCONN_STATE_ON:
+ set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method"));
+ gjab_reqauth(gjc);
+ break;
+ }
+ return;
+}
+
+static void jabber_login(struct aim_user *user)
+{
+ struct gaim_connection *gc = new_gaim_conn(user);
+ struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1);
+ char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "BitlBee");
+
+ jd->hash = g_hash_table_new(g_str_hash, g_str_equal);
+ jd->chats = NULL; /* we have no chats yet */
+
+ set_login_progress(gc, 1, _("Connecting"));
+
+ if (!(jd->gjc = gjab_new(loginname, user->password, gc))) {
+ g_free(loginname);
+ hide_login_progress(gc, _("Unable to connect"));
+ signoff(gc);
+ return;
+ }
+
+ g_free(loginname);
+ gjab_state_handler(jd->gjc, jabber_handlestate);
+ gjab_packet_handler(jd->gjc, jabber_handlepacket);
+ jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal);
+ gjab_start(jd->gjc);
+}
+
+static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) {
+ g_free(key);
+ g_free(val);
+ return TRUE;
+}
+
+static gboolean jabber_free(gpointer data)
+{
+ struct jabber_data *jd = data;
+
+ if(jd->gjc != NULL) {
+ gjab_delete(jd->gjc);
+ g_free(jd->gjc->sid);
+ jd->gjc = NULL;
+ }
+ g_free(jd);
+
+ return FALSE;
+}
+
+static void jabber_close(struct gaim_connection *gc)
+{
+ struct jabber_data *jd = gc->proto_data;
+
+ if(jd) {
+ GSList *jcs = jd->chats;
+
+ /* Free-up the jabber_chat struct allocs and the list */
+ while (jcs) {
+ g_free(jcs->data);
+ jcs = jcs->next;
+ }
+ g_slist_free(jd->chats);
+
+ /* Free-up the away status memories and the list */
+ if(jd->hash != NULL) {
+ g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL);
+ g_hash_table_destroy(jd->hash);
+ jd->hash = NULL;
+ }
+
+ /* Free-up the pending queries memories and the list */
+ if(jd->gjc != NULL && jd->gjc->queries != NULL) {
+ g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL);
+ g_hash_table_destroy(jd->gjc->queries);
+ jd->gjc->queries = NULL;
+ }
+ }
+ if (gc->inpa)
+ gaim_input_remove(gc->inpa);
+
+ if(jd) {
+ g_timeout_add(50, jabber_free, jd);
+ if(jd->gjc != NULL)
+ xmlnode_free(jd->gjc->current);
+ }
+ gc->proto_data = NULL;
+}
+
+static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags)
+{
+ xmlnode x, y;
+ char *realwho;
+ gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+ if (!who || !message)
+ return 0;
+
+ x = xmlnode_new_tag("message");
+ /* Bare username and "username" not the server itself? */
+ if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0)
+ realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
+ else
+ realwho = g_strdup(who);
+ xmlnode_put_attrib(x, "to", realwho);
+ g_free(realwho);
+
+ xmlnode_insert_tag(x, "bitlbee");
+ xmlnode_put_attrib(x, "type", "chat");
+
+ if (message && strlen(message)) {
+ y = xmlnode_insert_tag(x, "body");
+ xmlnode_insert_cdata(y, message, -1);
+ }
+
+ gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+ xmlnode_free(x);
+ return 1;
+}
+
+/*
+ * Add/update buddy's roster entry on server
+ */
+static void jabber_roster_update(struct gaim_connection *gc, char *name)
+{
+ xmlnode x, y;
+ char *realwho;
+ gjconn gjc;
+ struct buddy *buddy = NULL;
+ /* struct group *buddy_group = NULL; */
+
+ if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) {
+ gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+ if (!strchr(name, '@'))
+ realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
+ else {
+ jid who = jid_new(gjc->p, name);
+ if (who->user == NULL) {
+ /* FIXME: transport */
+ return;
+ }
+ realwho = g_strdup_printf("%s@%s", who->user, who->server);
+ }
+
+
+ x = jutil_iqnew(JPACKET__SET, NS_ROSTER);
+ y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item");
+ xmlnode_put_attrib(y, "jid", realwho);
+
+
+ /* If we can find the buddy, there's an alias for him, it's not 0-length
+ * and it doesn't match his JID, add the "name" attribute.
+ */
+ if((buddy = find_buddy(gc, realwho)) != NULL &&
+ buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) {
+
+ xmlnode_put_attrib(y, "name", buddy->show);
+ }
+
+ /*
+ * Find out what group the buddy's in and send that along
+ * with the roster item.
+ */
+ /* ** Bitlbee disabled **
+ if((buddy_group = NULL) != NULL) {
+ xmlnode z;
+ z = xmlnode_insert_tag(y, "group");
+ xmlnode_insert_cdata(z, buddy_group->name, -1);
+ }
+ ** End - Bitlbee ** */
+
+ gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+
+ xmlnode_free(x);
+ g_free(realwho);
+ }
+}
+
+/*
+ * Change buddy's group on server roster
+ */
+static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group)
+{
+ if(strcmp(old_group, new_group)) {
+ jabber_roster_update(gc, name);
+ }
+}
+
+static void jabber_add_buddy(struct gaim_connection *gc, char *name)
+{
+ xmlnode x;
+ char *realwho;
+ gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+ if (!((struct jabber_data *)gc->proto_data)->did_import)
+ return;
+
+ if (!name)
+ return;
+
+ if (!strcmp(gc->username, name))
+ return;
+
+ if (!strchr(name, '@'))
+ realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
+ else {
+ jid who;
+
+ if((who = jid_new(gjc->p, name)) == NULL) {
+ char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name);
+ do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error"));
+ g_free(msg);
+ jabber_remove_gaim_buddy(gc, name);
+ return;
+ }
+ if (who->user == NULL) {
+ /* FIXME: transport */
+ return;
+ }
+ realwho = g_strdup_printf("%s@%s", who->user, who->server);
+ }
+
+ x = xmlnode_new_tag("presence");
+ xmlnode_put_attrib(x, "to", realwho);
+ xmlnode_put_attrib(x, "type", "subscribe");
+ gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+ xmlnode_free(x);
+
+ jabber_roster_update(gc, realwho);
+
+ g_free(realwho);
+}
+
+static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group)
+{
+ xmlnode x;
+ char *realwho;
+ gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+ if (!name)
+ return;
+
+ if (!strchr(name, '@'))
+ realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
+ else
+ realwho = g_strdup(name);
+
+ x = xmlnode_new_tag("presence");
+ xmlnode_put_attrib(x, "to", realwho);
+ xmlnode_put_attrib(x, "type", "unsubscribe");
+ gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+ g_free(realwho);
+ xmlnode_free(x);
+}
+
+static void jabber_get_info(struct gaim_connection *gc, char *who) {
+ xmlnode x;
+ char *id;
+ char *realwho;
+ struct jabber_data *jd = gc->proto_data;
+ gjconn gjc = jd->gjc;
+
+ x = jutil_iqnew(JPACKET__GET, NS_VCARD);
+ /* Bare username? */
+ if (!strchr(who, '@')) {
+ realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
+ } else {
+ realwho = g_strdup(who);
+ }
+ xmlnode_put_attrib(x, "to", realwho);
+ g_free(realwho);
+
+ id = gjab_getid(gjc);
+ xmlnode_put_attrib(x, "id", id);
+
+ g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard"));
+
+ gjab_send(gjc, x);
+
+ xmlnode_free(x);
+
+}
+
+static void jabber_get_away_msg(struct gaim_connection *gc, char *who) {
+ struct jabber_data *jd = gc->proto_data;
+ gjconn gjc = jd->gjc;
+ char *status;
+
+ /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */
+ gchar **str_arr = (gchar **) g_new(gpointer, 3);
+ gchar **ap = str_arr;
+ gchar *realwho, *final;
+
+ /* Bare username? */
+ if (!strchr(who, '@')) {
+ realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
+ } else {
+ realwho = g_strdup(who);
+ }
+ *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho);
+
+ if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) {
+ status = _("Unknown");
+ }
+ *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status);
+
+ *ap = NULL;
+
+ final= g_strjoinv(NULL, str_arr);
+ g_strfreev(str_arr);
+
+ g_free(realwho);
+ g_free(final);
+
+}
+
+static GList *jabber_away_states(struct gaim_connection *gc) {
+ GList *m = NULL;
+
+ m = g_list_append(m, "Online");
+ m = g_list_append(m, "Chatty");
+ m = g_list_append(m, "Away");
+ m = g_list_append(m, "Extended Away");
+ m = g_list_append(m, "Do Not Disturb");
+
+ return m;
+}
+
+static void jabber_set_away(struct gaim_connection *gc, char *state, char *message)
+{
+ xmlnode x, y;
+ struct jabber_data *jd = gc->proto_data;
+ gjconn gjc = jd->gjc;
+
+ gc->away = NULL; /* never send an auto-response */
+
+ x = xmlnode_new_tag("presence");
+
+ if (!strcmp(state, GAIM_AWAY_CUSTOM)) {
+ /* oh goody. Gaim is telling us what to do. */
+ if (message) {
+ /* Gaim wants us to be away */
+ y = xmlnode_insert_tag(x, "show");
+ xmlnode_insert_cdata(y, "away", -1);
+ y = xmlnode_insert_tag(x, "status");
+ {
+ char *utf8 = str_to_utf8(message);
+ xmlnode_insert_cdata(y, utf8, -1);
+ g_free(utf8);
+ }
+ gc->away = "";
+ } else {
+ /* Gaim wants us to not be away */
+ /* but for Jabber, we can just send presence with no other information. */
+ }
+ } else {
+ /* state is one of our own strings. it won't be NULL. */
+ if (!g_strcasecmp(state, "Online")) {
+ /* once again, we don't have to put anything here */
+ } else if (!g_strcasecmp(state, "Chatty")) {
+ y = xmlnode_insert_tag(x, "show");
+ xmlnode_insert_cdata(y, "chat", -1);
+ } else if (!g_strcasecmp(state, "Away")) {
+ y = xmlnode_insert_tag(x, "show");
+ xmlnode_insert_cdata(y, "away", -1);
+ gc->away = "";
+ } else if (!g_strcasecmp(state, "Extended Away")) {
+ y = xmlnode_insert_tag(x, "show");
+ xmlnode_insert_cdata(y, "xa", -1);
+ gc->away = "";
+ } else if (!g_strcasecmp(state, "Do Not Disturb")) {
+ y = xmlnode_insert_tag(x, "show");
+ xmlnode_insert_cdata(y, "dnd", -1);
+ gc->away = "";
+ }
+ }
+
+ gjab_send(gjc, x);
+ xmlnode_free(x);
+}
+
+static void jabber_set_idle(struct gaim_connection *gc, int idle) {
+ struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+ jd->idle = idle ? time(NULL) - idle : idle;
+}
+
+static void jabber_keepalive(struct gaim_connection *gc) {
+ struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+ gjab_send_raw(jd->gjc, " \t ");
+}
+
+static void jabber_buddy_free(struct buddy *b)
+{
+ while (b->proto_data) {
+ g_free(((GSList *)b->proto_data)->data);
+ b->proto_data = g_slist_remove(b->proto_data, ((GSList *)b->proto_data)->data);
+ }
+}
+
+/*---------------------------------------*/
+/* Jabber "set info" (vCard) support */
+/*---------------------------------------*/
+
+/*
+ * V-Card format:
+ *
+ * <vCard prodid='' version='' xmlns=''>
+ * <FN></FN>
+ * <N>
+ * <FAMILY/>
+ * <GIVEN/>
+ * </N>
+ * <NICKNAME/>
+ * <URL/>
+ * <ADR>
+ * <STREET/>
+ * <EXTADD/>
+ * <LOCALITY/>
+ * <REGION/>
+ * <PCODE/>
+ * <COUNTRY/>
+ * </ADR>
+ * <TEL/>
+ * <EMAIL/>
+ * <ORG>
+ * <ORGNAME/>
+ * <ORGUNIT/>
+ * </ORG>
+ * <TITLE/>
+ * <ROLE/>
+ * <DESC/>
+ * <BDAY/>
+ * </vCard>
+ *
+ * See also:
+ *
+ * http://docs.jabber.org/proto/html/vcard-temp.html
+ * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd
+ */
+
+/*
+ * Cross-reference user-friendly V-Card entry labels to vCard XML tags
+ * and attributes.
+ *
+ * Order is (or should be) unimportant. For example: we have no way of
+ * knowing in what order real data will arrive.
+ *
+ * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag
+ * name, XML tag's parent tag "path" (relative to vCard node).
+ *
+ * List is terminated by a NULL label pointer.
+ *
+ * Entries with no label text, but with XML tag and parent tag
+ * entries, are used by V-Card XML construction routines to
+ * "automagically" construct the appropriate XML node tree.
+ *
+ * Thoughts on future direction/expansion
+ *
+ * This is a "simple" vCard.
+ *
+ * It is possible for nodes other than the "vCard" node to have
+ * attributes. Should that prove necessary/desirable, add an
+ * "attributes" pointer to the vcard_template struct, create the
+ * necessary tag_attr structs, and add 'em to the vcard_dflt_data
+ * array.
+ *
+ * The above changes will (obviously) require changes to the vCard
+ * construction routines.
+ */
+
+static struct vcard_template {
+ char *label; /* label text pointer */
+ char *text; /* entry text pointer */
+ int visible; /* should entry field be "visible?" */
+ int editable; /* should entry field be editable? */
+ char *tag; /* tag text */
+ char *ptag; /* parent tag "path" text */
+ char *url; /* vCard display format if URL */
+} vcard_template_data[] = {
+ {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL},
+ {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL},
+ {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL},
+ {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL},
+ {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"},
+ {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL},
+ {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL},
+ {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL},
+ {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL},
+ {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL},
+ {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL},
+ {N_("Telephone"), NULL, TRUE, TRUE, "TELEPHONE", NULL, NULL},
+ {N_("Email"), NULL, TRUE, TRUE, "EMAIL", NULL, "<A HREF=\"mailto:%s\">%s</A>"},
+ {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL},
+ {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL},
+ {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL},
+ {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL},
+ {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL},
+ {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL},
+ {"", NULL, TRUE, TRUE, "N", NULL, NULL},
+ {"", NULL, TRUE, TRUE, "ADR", NULL, NULL},
+ {"", NULL, TRUE, TRUE, "ORG", NULL, NULL},
+ {NULL, NULL, 0, 0, NULL, NULL, NULL}
+};
+
+/*
+ * Used by routines to parse an XML-encoded string into an xmlnode tree
+ */
+typedef struct {
+ XML_Parser parser;
+ xmlnode current;
+} *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct;
+
+
+/*
+ * Used by XML_Parse on parsing CDATA
+ */
+static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen)
+{
+ xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+
+ if (xmlp->current)
+ xmlnode_insert_cdata(xmlp->current, s, slen);
+}
+
+/*
+ * Used by XML_Parse to start or append to an xmlnode
+ */
+static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs)
+{
+ xmlnode x;
+ xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+
+ if (xmlp->current) {
+ /* Append the node to the current one */
+ x = xmlnode_insert_tag(xmlp->current, name);
+ xmlnode_put_expat_attribs(x, attribs);
+
+ xmlp->current = x;
+ } else {
+ x = xmlnode_new_tag(name);
+ xmlnode_put_expat_attribs(x, attribs);
+ xmlp->current = x;
+ }
+}
+
+/*
+ * Used by XML_Parse to end an xmlnode
+ */
+static void xmlstr2xmlnode_endElement(void *userdata, const char *name)
+{
+ xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+ xmlnode x;
+
+ if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) {
+ xmlp->current = x;
+ }
+}
+
+/*
+ * Parse an XML-encoded string into an xmlnode tree
+ *
+ * Caller is responsible for freeing the returned xmlnode
+ */
+static xmlnode xmlstr2xmlnode(char *xmlstring)
+{
+ xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1);
+ xmlnode x = NULL;
+
+ my_parser->parser = XML_ParserCreate(NULL);
+ my_parser->current = NULL;
+
+ XML_SetUserData(my_parser->parser, (void *)my_parser);
+ XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement);
+ XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData);
+ XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0);
+
+ x = my_parser->current;
+
+ XML_ParserFree(my_parser->parser);
+ g_free(my_parser);
+
+ return(x);
+}
+
+/*
+ * Insert a tag node into an xmlnode tree, recursively inserting parent tag
+ * nodes as necessary
+ *
+ * Returns pointer to inserted node
+ *
+ * Note to hackers: this code is designed to be re-entrant (it's recursive--it
+ * calls itself), so don't put any "static"s in here!
+ */
+static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag)
+{
+ xmlnode x = NULL;
+
+ /*
+ * If the parent tag wasn't specified, see if we can get it
+ * from the vCard template struct.
+ */
+ if(parent_tag == NULL) {
+ struct vcard_template *vc_tp = vcard_template_data;
+
+ while(vc_tp->label != NULL) {
+ if(strcmp(vc_tp->tag, new_tag) == 0) {
+ parent_tag = vc_tp->ptag;
+ break;
+ }
+ ++vc_tp;
+ }
+ }
+
+ /*
+ * If we have a parent tag...
+ */
+ if(parent_tag != NULL ) {
+ /*
+ * Try to get the parent node for a tag
+ */
+ if((x = xmlnode_get_tag(start, parent_tag)) == NULL) {
+ /*
+ * Descend?
+ */
+ char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag);
+ char *parent;
+
+ if((parent = strrchr(grand_parent, '/')) != NULL) {
+ *(parent++) = '\0';
+ x = insert_tag_to_parent_tag(start, grand_parent, parent);
+ } else {
+ x = xmlnode_insert_tag(start, grand_parent);
+ }
+ g_free(grand_parent);
+ } else {
+ /*
+ * We found *something* to be the parent node.
+ * Note: may be the "root" node!
+ */
+ xmlnode y;
+ if((y = xmlnode_get_tag(x, new_tag)) != NULL) {
+ return(y);
+ }
+ }
+ }
+
+ /*
+ * insert the new tag into its parent node
+ */
+ return(xmlnode_insert_tag((x == NULL? start : x), new_tag));
+}
+
+/*
+ * Send vCard info to Jabber server
+ */
+static void jabber_set_info(struct gaim_connection *gc, char *info)
+{
+ xmlnode x, vc_node;
+ char *id;
+ struct jabber_data *jd = gc->proto_data;
+ gjconn gjc = jd->gjc;
+
+ x = xmlnode_new_tag("iq");
+ xmlnode_put_attrib(x,"type","set");
+
+ id = gjab_getid(gjc);
+
+ xmlnode_put_attrib(x, "id", id);
+
+ /*
+ * Send only if there's actually any *information* to send
+ */
+ if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL &&
+ g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) {
+ xmlnode_insert_tag_node(x, vc_node);
+ gjab_send(gjc, x);
+ }
+
+ xmlnode_free(x);
+}
+
+/*
+ * displays a Jabber vCard
+ */
+static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from)
+{
+ struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+ jid who = jid_new(gjc->p, from);
+ char *status = NULL, *text = NULL;
+ GString *str = g_string_sized_new(100);
+ xmlnode child;
+
+ gchar *buddy = NULL;
+
+ if(querynode == NULL) {
+ serv_got_crap(GJ_GC(gjc), "%s - Received empty info reply from %s", _("User Info"), from);
+ return;
+ }
+
+ if(who->resource != NULL && (who->resource)[0] != '\0') {
+ buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);
+ } else {
+ buddy = g_strdup_printf("%s@%s", who->user, who->server);
+ }
+
+ if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) {
+ status = _("Unknown");
+ }
+
+ g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"),
+ status);
+
+ for(child = querynode->firstchild; child; child = child->next)
+ {
+ xmlnode child2;
+
+ if(child->type != NTYPE_TAG)
+ continue;
+
+ text = xmlnode_get_data(child);
+ if(text && !strcmp(child->name, "FN")) {
+ info_string_append(str, "\n", _("Full Name"), text);
+ } else if (!strcmp(child->name, "N")) {
+ for (child2 = child->firstchild; child2; child2 = child2->next) {
+ char *text2 = NULL;
+
+ if (child2->type != NTYPE_TAG)
+ continue;
+
+ text2 = xmlnode_get_data(child2);
+ if (text2 && !strcmp(child2->name, "FAMILY")) {
+ info_string_append(str, "\n", _("Family Name"), text2);
+ } else if (text2 && !strcmp(child2->name, "GIVEN")) {
+ info_string_append(str, "\n", _("Given Name"), text2);
+ } else if (text2 && !strcmp(child2->name, "MIDDLE")) {
+ info_string_append(str, "\n", _("Middle Name"), text2);
+ }
+ }
+ } else if (text && !strcmp(child->name, "NICKNAME")) {
+ info_string_append(str, "\n", _("Nickname"), text);
+ } else if (text && !strcmp(child->name, "BDAY")) {
+ info_string_append(str, "\n", _("Birthday"), text);
+ } else if (!strcmp(child->name, "ADR")) {
+ /* show wich address it is */
+ /* Just for the beauty of bitlbee
+ if (child->firstchild)
+ g_string_sprintfa(str, "%s:\n", _("Address"));
+ */
+ for(child2 = child->firstchild; child2; child2 = child2->next) {
+ char *text2 = NULL;
+
+ if(child2->type != NTYPE_TAG)
+ continue;
+
+ text2 = xmlnode_get_data(child2);
+ if(text2 && !strcmp(child2->name, "POBOX")) {
+ info_string_append(str, "\n",
+ _("P.O. Box"), text2);
+ } else if(text2 && !strcmp(child2->name, "EXTADR")) {
+ info_string_append(str, "\n",
+ _("Extended Address"), text2);
+ } else if(text2 && !strcmp(child2->name, "STREET")) {
+ info_string_append(str, "\n",
+ _("Street Address"), text2);
+ } else if(text2 && !strcmp(child2->name, "LOCALITY")) {
+ info_string_append(str, "\n",
+ _("Locality"), text2);
+ } else if(text2 && !strcmp(child2->name, "REGION")) {
+ info_string_append(str, "\n",
+ _("Region"), text2);
+ } else if(text2 && !strcmp(child2->name, "PCODE")) {
+ info_string_append(str, "\n",
+ _("Postal Code"), text2);
+ } else if(text2 && (!strcmp(child2->name, "CTRY")
+ || !strcmp(child2->name, "COUNTRY"))) {
+ info_string_append(str, "\n", _("Country"), text2);
+ }
+ }
+ } else if(!strcmp(child->name, "TEL")) {
+ char *number = NULL;
+ if ((child2 = xmlnode_get_tag(child, "NUMBER"))) {
+ /* show what kind of number it is */
+ number = xmlnode_get_data(child2);
+ if(number) {
+ info_string_append(str, "\n", _("Telephone"), number);
+ }
+ } else if((number = xmlnode_get_data(child))) {
+ /* lots of clients (including gaim) do this,
+ * but it's out of spec */
+ info_string_append(str, "\n", _("Telephone"), number);
+ }
+ } else if(!strcmp(child->name, "EMAIL")) {
+ char *userid = NULL;
+ if((child2 = xmlnode_get_tag(child, "USERID"))) {
+ /* show what kind of email it is */
+ userid = xmlnode_get_data(child2);
+ if(userid) {
+ info_string_append(str, "\n", _("Email"), userid);
+ }
+ } else if((userid = xmlnode_get_data(child))) {
+ /* lots of clients (including gaim) do this,
+ * but it's out of spec */
+ info_string_append(str, "\n", _("Email"), userid);
+ }
+ } else if(!strcmp(child->name, "ORG")) {
+ for(child2 = child->firstchild; child2; child2 = child2->next) {
+ char *text2 = NULL;
+
+ if(child2->type != NTYPE_TAG)
+ continue;
+
+ text2 = xmlnode_get_data(child2);
+ if(text2 && !strcmp(child2->name, "ORGNAME")) {
+ info_string_append(str, "\n", _("Organization Name"), text2);
+ } else if(text2 && !strcmp(child2->name, "ORGUNIT")) {
+ info_string_append(str, "\n", _("Organization Unit"), text2);
+ }
+ }
+ } else if(text && !strcmp(child->name, "TITLE")) {
+ info_string_append(str, "\n", _("Title"), text);
+ } else if(text && !strcmp(child->name, "ROLE")) {
+ info_string_append(str, "\n", _("Role"), text);
+ } else if(text && !strcmp(child->name, "DESC")) {
+ g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Description"),
+ text, _("End of Description"));
+ }
+ }
+
+ serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str);
+
+ g_free(buddy);
+ g_string_free(str, TRUE);
+}
+
+
+static GList *jabber_actions()
+{
+ GList *m = NULL;
+
+ m = g_list_append(m, _("Set User Info"));
+ /*
+ m = g_list_append(m, _("Set Dir Info"));
+ m = g_list_append(m, _("Change Password"));
+ */
+
+ return m;
+}
+
+static struct prpl *my_protocol = NULL;
+
+void jabber_init(struct prpl *ret)
+{
+ /* the NULL's aren't required but they're nice to have */
+ ret->protocol = PROTO_JABBER;
+ ret->name = jabber_name;
+ ret->away_states = jabber_away_states;
+ ret->actions = jabber_actions;
+ ret->login = jabber_login;
+ ret->close = jabber_close;
+ ret->send_im = jabber_send_im;
+ ret->set_info = jabber_set_info;
+ ret->get_info = jabber_get_info;
+ ret->set_away = jabber_set_away;
+ ret->get_away = jabber_get_away_msg;
+ ret->set_idle = jabber_set_idle;
+ ret->add_buddy = jabber_add_buddy;
+ ret->remove_buddy = jabber_remove_buddy;
+ ret->add_permit = NULL;
+ ret->add_deny = NULL;
+ ret->rem_permit = NULL;
+ ret->rem_deny = NULL;
+ ret->set_permit_deny = NULL;
+ ret->keepalive = jabber_keepalive;
+ ret->buddy_free = jabber_buddy_free;
+ ret->alias_buddy = jabber_roster_update;
+ ret->group_buddy = jabber_group_change;
+
+ my_protocol = ret;
+}
diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h
new file mode 100644
index 00000000..0b907500
--- /dev/null
+++ b/protocols/jabber/jabber.h
@@ -0,0 +1,315 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Jabber
+ * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+#include <ctype.h>
+#ifdef _WIN32
+#undef DATADIR
+#include "sock.h"
+#endif
+
+#include "lib.h"
+
+
+#ifndef INCL_JABBER_H
+#define INCL_JABBER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------- */
+/* */
+/* JID structures & constants */
+/* */
+/* --------------------------------------------------------- */
+#define JID_RESOURCE 1
+#define JID_USER 2
+#define JID_SERVER 4
+
+typedef struct jid_struct
+{
+ pool p;
+ char* resource;
+ char* user;
+ char* server;
+ char* full;
+ struct jid_struct *next; /* for lists of jids */
+} *jid;
+
+jid jid_new(pool p, char *idstr); /* Creates a jabber id from the idstr */
+void jid_set(jid id, char *str, int item); /* Individually sets jid components */
+char* jid_full(jid id); /* Builds a string type=user/resource@server from the jid data */
+int jid_cmp(jid a, jid b); /* Compares two jid's, returns 0 for perfect match */
+int jid_cmpx(jid a, jid b, int parts); /* Compares just the parts specified as JID_|JID_ */
+jid jid_append(jid a, jid b); /* Appending b to a (list), no dups */
+xmlnode jid_xres(jid id); /* Returns xmlnode representation of the resource?query=string */
+xmlnode jid_nodescan(jid id, xmlnode x); /* Scans the children of the node for a matching jid attribute */
+jid jid_user(jid a); /* returns the same jid but just of the user@host part */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* JPacket structures & constants */
+/* */
+/* --------------------------------------------------------- */
+#define JPACKET_UNKNOWN 0x00
+#define JPACKET_MESSAGE 0x01
+#define JPACKET_PRESENCE 0x02
+#define JPACKET_IQ 0x04
+#define JPACKET_S10N 0x08
+
+#define JPACKET__UNKNOWN 0
+#define JPACKET__NONE 1
+#define JPACKET__ERROR 2
+#define JPACKET__CHAT 3
+#define JPACKET__GROUPCHAT 4
+#define JPACKET__GET 5
+#define JPACKET__SET 6
+#define JPACKET__RESULT 7
+#define JPACKET__SUBSCRIBE 8
+#define JPACKET__SUBSCRIBED 9
+#define JPACKET__UNSUBSCRIBE 10
+#define JPACKET__UNSUBSCRIBED 11
+#define JPACKET__AVAILABLE 12
+#define JPACKET__UNAVAILABLE 13
+#define JPACKET__PROBE 14
+#define JPACKET__HEADLINE 15
+#define JPACKET__INVISIBLE 16
+
+typedef struct jpacket_struct
+{
+ unsigned char type;
+ int subtype;
+ int flag;
+ void* aux1;
+ xmlnode x;
+ jid to;
+ jid from;
+ char* iqns;
+ xmlnode iq;
+ pool p;
+} *jpacket, _jpacket;
+
+jpacket jpacket_new(xmlnode x); /* Creates a jabber packet from the xmlnode */
+int jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Presence Proxy DB structures & constants */
+/* */
+/* --------------------------------------------------------- */
+typedef struct ppdb_struct
+{
+ jid id; /* entry data */
+ int pri;
+ xmlnode x;
+ struct ppdb_struct* user; /* linked list for user@server */
+ pool p; /* db-level data */
+ struct ppdb_struct* next;
+} _ppdb, *ppdb;
+
+ppdb ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */
+xmlnode ppdb_primary(ppdb db, jid id); /* Fetches the matching primary presence for the id */
+void ppdb_free(ppdb db); /* Frees the db and all entries */
+xmlnode ppdb_get(ppdb db, jid id); /* Called successively to return each presence xmlnode */
+ /* for the id and children, returns NULL at the end */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Simple Jabber Rate limit functions */
+/* */
+/* --------------------------------------------------------- */
+typedef struct jlimit_struct
+{
+ char *key;
+ int start;
+ int points;
+ int maxt, maxp;
+ pool p;
+} *jlimit, _jlimit;
+
+jlimit jlimit_new(int maxt, int maxp);
+void jlimit_free(jlimit r);
+int jlimit_check(jlimit r, char *key, int points);
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Error structures & constants */
+/* */
+/* --------------------------------------------------------- */
+typedef struct terror_struct
+{
+ int code;
+ char msg[64];
+} terror;
+
+#define TERROR_BAD (terror){400,"Bad Request"}
+#define TERROR_AUTH (terror){401,"Unauthorized"}
+#define TERROR_PAY (terror){402,"Payment Required"}
+#define TERROR_FORBIDDEN (terror){403,"Forbidden"}
+#define TERROR_NOTFOUND (terror){404,"Not Found"}
+#define TERROR_NOTALLOWED (terror){405,"Not Allowed"}
+#define TERROR_NOTACCEPTABLE (terror){406,"Not Acceptable"}
+#define TERROR_REGISTER (terror){407,"Registration Required"}
+#define TERROR_REQTIMEOUT (terror){408,"Request Timeout"}
+#define TERROR_CONFLICT (terror){409,"Conflict"}
+
+#define TERROR_INTERNAL (terror){500,"Internal Server Error"}
+#define TERROR_NOTIMPL (terror){501,"Not Implemented"}
+#define TERROR_EXTERNAL (terror){502,"Remote Server Error"}
+#define TERROR_UNAVAIL (terror){503,"Service Unavailable"}
+#define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"}
+#define TERROR_DISCONNECTED (terror){510,"Disconnected"}
+
+/* --------------------------------------------------------- */
+/* */
+/* Namespace constants */
+/* */
+/* --------------------------------------------------------- */
+#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)
+
+#define NS_CLIENT "jabber:client"
+#define NS_SERVER "jabber:server"
+#define NS_AUTH "jabber:iq:auth"
+#define NS_REGISTER "jabber:iq:register"
+#define NS_ROSTER "jabber:iq:roster"
+#define NS_OFFLINE "jabber:x:offline"
+#define NS_AGENT "jabber:iq:agent"
+#define NS_AGENTS "jabber:iq:agents"
+#define NS_DELAY "jabber:x:delay"
+#define NS_VERSION "jabber:iq:version"
+#define NS_TIME "jabber:iq:time"
+#define NS_VCARD "vcard-temp"
+#define NS_PRIVATE "jabber:iq:private"
+#define NS_SEARCH "jabber:iq:search"
+#define NS_OOB "jabber:iq:oob"
+#define NS_XOOB "jabber:x:oob"
+#define NS_ADMIN "jabber:iq:admin"
+#define NS_FILTER "jabber:iq:filter"
+#define NS_AUTH_0K "jabber:iq:auth:0k"
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Message Types */
+/* */
+/* --------------------------------------------------------- */
+#define TMSG_NORMAL "normal"
+#define TMSG_ERROR "error"
+#define TMSG_CHAT "chat"
+#define TMSG_GROUPCHAT "groupchat"
+#define TMSG_HEADLINE "headline"
+
+
+/* --------------------------------------------------------- */
+/* */
+/* JUtil functions */
+/* */
+/* --------------------------------------------------------- */
+xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */
+xmlnode jutil_iqnew(int type, char *ns); /* Create a skeleton iq packet */
+xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body);
+ /* Create a skeleton message packet */
+xmlnode jutil_header(char* xmlns, char* server); /* Create a skeleton stream packet */
+int jutil_priority(xmlnode x); /* Determine priority of this packet */
+void jutil_tofrom(xmlnode x); /* Swaps to/from fields on a packet */
+xmlnode jutil_iqresult(xmlnode x); /* Generate a skeleton iq/result, given a iq/query */
+char* jutil_timestamp(void); /* Get stringified timestamp */
+void jutil_error(xmlnode x, terror E); /* Append an <error> node to x */
+void jutil_delay(xmlnode msg, char *reason); /* Append a delay packet to msg */
+char* jutil_regkey(char *key, char *seed); /* pass a seed to generate a key, pass the key again to validate (returns it) */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* JConn structures & functions */
+/* */
+/* --------------------------------------------------------- */
+#define JCONN_STATE_OFF 0
+#define JCONN_STATE_CONNECTED 1
+#define JCONN_STATE_ON 2
+#define JCONN_STATE_AUTH 3
+
+typedef struct jconn_struct
+{
+ /* Core structure */
+ pool p; /* Memory allocation pool */
+ int state; /* Connection state flag */
+ int fd; /* Connection file descriptor */
+ jid user; /* User info */
+ char *pass; /* User passwd */
+
+ /* Stream stuff */
+ int id; /* id counter for jab_getid() function */
+ char idbuf[9]; /* temporary storage for jab_getid() */
+ char *sid; /* stream id from server, for digest auth */
+ XML_Parser parser; /* Parser instance */
+ xmlnode current; /* Current node in parsing instance.. */
+
+ /* Event callback ptrs */
+ void (*on_state)(struct jconn_struct *j, int state);
+ void (*on_packet)(struct jconn_struct *j, jpacket p);
+
+} *jconn, jconn_struct;
+
+typedef void (*jconn_state_h)(jconn j, int state);
+typedef void (*jconn_packet_h)(jconn j, jpacket p);
+
+
+jconn jab_new(char *user, char *pass);
+void jab_delete(jconn j);
+void jab_state_handler(jconn j, jconn_state_h h);
+void jab_packet_handler(jconn j, jconn_packet_h h);
+void jab_start(jconn j);
+void jab_stop(jconn j);
+
+int jab_getfd(jconn j);
+jid jab_getjid(jconn j);
+char *jab_getsid(jconn j);
+char *jab_getid(jconn j);
+
+void jab_send(jconn j, xmlnode x);
+void jab_send_raw(jconn j, const char *str);
+void jab_recv(jconn j);
+void jab_poll(jconn j, int timeout);
+
+char *jab_auth(jconn j);
+char *jab_reg(jconn j);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCL_JABBER_H */
diff --git a/protocols/jabber/jconn.c b/protocols/jabber/jconn.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/jconn.c
diff --git a/protocols/jabber/jid.c b/protocols/jabber/jid.c
new file mode 100644
index 00000000..ed2b9ba1
--- /dev/null
+++ b/protocols/jabber/jid.c
@@ -0,0 +1,170 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static jid jid_safe(jid id)
+{
+ char *str;
+
+ if(strlen(id->server) == 0 || strlen(id->server) > 255)
+ return NULL;
+
+ /* lowercase the hostname, make sure it's valid characters */
+ for(str = id->server; *str != '\0'; str++)
+ {
+ *str = tolower(*str);
+ if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return NULL;
+ }
+
+ /* cut off the user */
+ if(id->user != NULL && strlen(id->user) > 64)
+ id->user[64] = '\0';
+
+ /* check for low and invalid ascii characters in the username */
+ if(id->user != NULL)
+ for(str = id->user; *str != '\0'; str++)
+ if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return NULL;
+
+ return id;
+}
+
+jid jid_new(pool p, char *idstr)
+{
+ char *server, *resource, *type, *str;
+ jid id;
+
+ if(p == NULL || idstr == NULL || strlen(idstr) == 0)
+ return NULL;
+
+ /* user@server/resource */
+
+ str = pstrdup(p, idstr);
+
+ id = pmalloco(p,sizeof(struct jid_struct));
+ id->p = p;
+
+ resource = strstr(str,"/");
+ if(resource != NULL)
+ {
+ *resource = '\0';
+ ++resource;
+ if(strlen(resource) > 0)
+ id->resource = resource;
+ }else{
+ resource = str + strlen(str); /* point to end */
+ }
+
+ type = strstr(str,":");
+ if(type != NULL && type < resource)
+ {
+ *type = '\0';
+ ++type;
+ str = type; /* ignore the type: prefix */
+ }
+
+ server = strstr(str,"@");
+ if(server == NULL || server > resource)
+ { /* if there's no @, it's just the server address */
+ id->server = str;
+ }else{
+ *server = '\0';
+ ++server;
+ id->server = server;
+ if(strlen(str) > 0)
+ id->user = str;
+ }
+
+ return jid_safe(id);
+}
+
+char *jid_full(jid id)
+{
+ spool s;
+
+ if(id == NULL)
+ return NULL;
+
+ /* use cached copy */
+ if(id->full != NULL)
+ return id->full;
+
+ s = spool_new(id->p);
+
+ if(id->user != NULL)
+ spooler(s, id->user,"@",s);
+
+ spool_add(s, id->server);
+
+ if(id->resource != NULL)
+ spooler(s, "/",id->resource,s);
+
+ id->full = spool_print(s);
+ return id->full;
+}
+
+/* local utils */
+static int _jid_nullstrcmp(char *a, char *b)
+{
+ if(a == NULL && b == NULL) return 0;
+ if(a == NULL || b == NULL) return -1;
+ return strcmp(a,b);
+}
+static int _jid_nullstrcasecmp(char *a, char *b)
+{
+ if(a == NULL && b == NULL) return 0;
+ if(a == NULL || b == NULL) return -1;
+ return g_strcasecmp(a,b);
+}
+
+/* suggested by Anders Qvist <quest@valdez.netg.se> */
+int jid_cmpx(jid a, jid b, int parts)
+{
+ if(a == NULL || b == NULL)
+ return -1;
+
+ if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
+ if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
+ if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1;
+
+ return 0;
+}
diff --git a/protocols/jabber/jpacket.c b/protocols/jabber/jpacket.c
new file mode 100644
index 00000000..9c7ce00d
--- /dev/null
+++ b/protocols/jabber/jpacket.c
@@ -0,0 +1,159 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+static jpacket jpacket_reset(jpacket p);
+
+jpacket jpacket_new(xmlnode x)
+{
+ jpacket p;
+
+ if(x == NULL)
+ return NULL;
+
+ p = pmalloc(xmlnode_pool(x),sizeof(_jpacket));
+ p->x = x;
+
+ return jpacket_reset(p);
+}
+
+static jpacket jpacket_reset(jpacket p)
+{
+ char *val;
+ xmlnode x;
+
+ x = p->x;
+ memset(p,0,sizeof(_jpacket));
+ p->x = x;
+ p->p = xmlnode_pool(x);
+
+ if(strncmp(xmlnode_get_name(x),"message",7) == 0)
+ {
+ p->type = JPACKET_MESSAGE;
+ }else if(strncmp(xmlnode_get_name(x),"presence",8) == 0)
+ {
+ p->type = JPACKET_PRESENCE;
+ val = xmlnode_get_attrib(x, "type");
+ if(val == NULL)
+ p->subtype = JPACKET__AVAILABLE;
+ else if(strcmp(val,"unavailable") == 0)
+ p->subtype = JPACKET__UNAVAILABLE;
+ else if(strcmp(val,"probe") == 0)
+ p->subtype = JPACKET__PROBE;
+ else if(strcmp(val,"error") == 0)
+ p->subtype = JPACKET__ERROR;
+ else if(strcmp(val,"invisible") == 0)
+ p->subtype = JPACKET__INVISIBLE;
+ else if(*val == 's' || *val == 'u')
+ p->type = JPACKET_S10N;
+ else if(strcmp(val,"available") == 0)
+ { /* someone is using type='available' which is frowned upon */
+ xmlnode_hide_attrib(x,"type");
+ p->subtype = JPACKET__AVAILABLE;
+ }else
+ p->type = JPACKET_UNKNOWN;
+ }else if(strncmp(xmlnode_get_name(x),"iq",2) == 0)
+ {
+ p->type = JPACKET_IQ;
+ p->iq = xmlnode_get_tag(x,"?xmlns");
+ p->iqns = xmlnode_get_attrib(p->iq,"xmlns");
+ }
+
+ /* set up the jids if any, flag packet as unknown if they are unparseable */
+ val = xmlnode_get_attrib(x,"to");
+ if(val != NULL)
+ if((p->to = jid_new(p->p, val)) == NULL)
+ p->type = JPACKET_UNKNOWN;
+ val = xmlnode_get_attrib(x,"from");
+ if(val != NULL)
+ if((p->from = jid_new(p->p, val)) == NULL)
+ p->type = JPACKET_UNKNOWN;
+
+ return p;
+}
+
+
+int jpacket_subtype(jpacket p)
+{
+ char *type;
+ int ret = p->subtype;
+
+ if(ret != JPACKET__UNKNOWN)
+ return ret;
+
+ ret = JPACKET__NONE; /* default, when no type attrib is specified */
+ type = xmlnode_get_attrib(p->x, "type");
+ if(j_strcmp(type,"error") == 0)
+ ret = JPACKET__ERROR;
+ else
+ switch(p->type)
+ {
+ case JPACKET_MESSAGE:
+ if(j_strcmp(type,"chat") == 0)
+ ret = JPACKET__CHAT;
+ else if(j_strcmp(type,"groupchat") == 0)
+ ret = JPACKET__GROUPCHAT;
+ else if(j_strcmp(type,"headline") == 0)
+ ret = JPACKET__HEADLINE;
+ break;
+ case JPACKET_S10N:
+ if(j_strcmp(type,"subscribe") == 0)
+ ret = JPACKET__SUBSCRIBE;
+ else if(j_strcmp(type,"subscribed") == 0)
+ ret = JPACKET__SUBSCRIBED;
+ else if(j_strcmp(type,"unsubscribe") == 0)
+ ret = JPACKET__UNSUBSCRIBE;
+ else if(j_strcmp(type,"unsubscribed") == 0)
+ ret = JPACKET__UNSUBSCRIBED;
+ break;
+ case JPACKET_IQ:
+ if(j_strcmp(type,"get") == 0)
+ ret = JPACKET__GET;
+ else if(j_strcmp(type,"set") == 0)
+ ret = JPACKET__SET;
+ else if(j_strcmp(type,"result") == 0)
+ ret = JPACKET__RESULT;
+ break;
+ }
+
+ p->subtype = ret;
+ return ret;
+}
diff --git a/protocols/jabber/jutil.c b/protocols/jabber/jutil.c
new file mode 100644
index 00000000..dd367ac9
--- /dev/null
+++ b/protocols/jabber/jutil.c
@@ -0,0 +1,122 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+#include "nogaim.h"
+
+/* util for making presence packets */
+xmlnode jutil_presnew(int type, char *to, char *status)
+{
+ xmlnode pres;
+
+ pres = xmlnode_new_tag("presence");
+ switch(type)
+ {
+ case JPACKET__SUBSCRIBE:
+ xmlnode_put_attrib(pres,"type","subscribe");
+ break;
+ case JPACKET__UNSUBSCRIBE:
+ xmlnode_put_attrib(pres,"type","unsubscribe");
+ break;
+ case JPACKET__SUBSCRIBED:
+ xmlnode_put_attrib(pres,"type","subscribed");
+ break;
+ case JPACKET__UNSUBSCRIBED:
+ xmlnode_put_attrib(pres,"type","unsubscribed");
+ break;
+ case JPACKET__PROBE:
+ xmlnode_put_attrib(pres,"type","probe");
+ break;
+ case JPACKET__UNAVAILABLE:
+ xmlnode_put_attrib(pres,"type","unavailable");
+ break;
+ case JPACKET__INVISIBLE:
+ xmlnode_put_attrib(pres,"type","invisible");
+ break;
+ }
+ if(to != NULL)
+ xmlnode_put_attrib(pres,"to",to);
+ if(status != NULL)
+ xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
+
+ return pres;
+}
+
+/* util for making IQ packets */
+xmlnode jutil_iqnew(int type, char *ns)
+{
+ xmlnode iq;
+
+ iq = xmlnode_new_tag("iq");
+ switch(type)
+ {
+ case JPACKET__GET:
+ xmlnode_put_attrib(iq,"type","get");
+ break;
+ case JPACKET__SET:
+ xmlnode_put_attrib(iq,"type","set");
+ break;
+ case JPACKET__RESULT:
+ xmlnode_put_attrib(iq,"type","result");
+ break;
+ case JPACKET__ERROR:
+ xmlnode_put_attrib(iq,"type","error");
+ break;
+ }
+ xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns);
+
+ return iq;
+}
+
+/* util for making stream packets */
+xmlnode jutil_header(char* xmlns, char* server)
+{
+ xmlnode result;
+ if ((xmlns == NULL)||(server == NULL))
+ return NULL;
+ result = xmlnode_new_tag("stream:stream");
+ xmlnode_put_attrib(result, "xmlns:stream", "http://etherx.jabber.org/streams");
+ xmlnode_put_attrib(result, "xmlns", xmlns);
+ xmlnode_put_attrib(result, "to", server);
+
+ return result;
+}
diff --git a/protocols/jabber/karma.c b/protocols/jabber/karma.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/karma.c
diff --git a/protocols/jabber/latin1tab.h b/protocols/jabber/latin1tab.h
new file mode 100644
index 00000000..48609aa8
--- /dev/null
+++ b/protocols/jabber/latin1tab.h
@@ -0,0 +1,62 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
diff --git a/protocols/jabber/lib.h b/protocols/jabber/lib.h
new file mode 100644
index 00000000..ce0669e5
--- /dev/null
+++ b/protocols/jabber/lib.h
@@ -0,0 +1,343 @@
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "xmlparse.h"
+
+int j_strcmp(const char *a, const char *b);
+
+/*
+** Arrange to use either varargs or stdargs
+*/
+
+#define MAXSHORTSTR 203 /* max short string length */
+#define QUAD_T unsigned long long
+
+#if defined(__STDC__) || defined(_WIN32)
+
+#include <stdarg.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap, f)
+# define VA_END va_end(ap)
+
+#else /* __STDC__ */
+
+# include <varargs.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap)
+# define VA_END va_end(ap)
+
+#endif /* __STDC__ */
+
+
+#ifndef INCL_LIB_H
+#define INCL_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------- */
+/* */
+/* Pool-based memory management routines */
+/* */
+/* --------------------------------------------------------- */
+
+#undef POOL_DEBUG
+/*
+ flip these, this should be a prime number for top # of pools debugging
+#define POOL_DEBUG 40009
+*/
+
+/* pheap - singular allocation of memory */
+struct pheap
+{
+ void *block;
+ int size, used;
+};
+
+/* pool_cleaner - callback type which is associated
+ with a pool entry; invoked when the pool entry is
+ free'd */
+typedef void (*pool_cleaner)(void *arg);
+
+/* pfree - a linked list node which stores an
+ allocation chunk, plus a callback */
+struct pfree
+{
+ pool_cleaner f;
+ void *arg;
+ struct pheap *heap;
+ struct pfree *next;
+};
+
+/* pool - base node for a pool. Maintains a linked list
+ of pool entries (pfree) */
+typedef struct pool_struct
+{
+ int size;
+ struct pfree *cleanup;
+ struct pheap *heap;
+#ifdef POOL_DEBUG
+ char name[8], zone[32];
+ int lsize;
+} _pool, *pool;
+#define pool_new() _pool_new(ZONE)
+#define pool_heap(i) _pool_new_heap(i,ZONE)
+#else
+} _pool, *pool;
+#define pool_heap(i) _pool_new_heap(i,NULL)
+#define pool_new() _pool_new(NULL)
+#endif
+
+pool _pool_new(char *zone); /* new pool :) */
+pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */
+void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */
+void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */
+void *pmalloco(pool p, int size); /* YAPW for zeroing the block */
+char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */
+void pool_stat(int full); /* print to stderr the changed pools and reset */
+void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */
+void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */
+
+
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Socket helper stuff */
+/* */
+/* --------------------------------------------------------- */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define NETSOCKET_SERVER 0
+#define NETSOCKET_CLIENT 1
+#define NETSOCKET_UDP 2
+
+#ifndef WIN32
+int make_netsocket(u_short port, char *host, int type);
+struct in_addr *make_addr(char *host);
+int set_fd_close_on_exec(int fd, int flag);
+#endif
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Hashtable functions */
+/* */
+/* --------------------------------------------------------- */
+typedef struct xhn_struct
+{
+ struct xhn_struct *next;
+ const char *key;
+ void *val;
+} *xhn, _xhn;
+
+char *strescape(pool p, char *);
+
+
+/* --------------------------------------------------------- */
+/* */
+/* String pools (spool) functions */
+/* */
+/* --------------------------------------------------------- */
+struct spool_node
+{
+ char *c;
+ struct spool_node *next;
+};
+
+typedef struct spool_struct
+{
+ pool p;
+ int len;
+ struct spool_node *last;
+ struct spool_node *first;
+} *spool;
+
+spool spool_new(pool p); /* create a string pool */
+void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */
+char *spool_print(spool s); /* return a big string */
+void spool_add(spool s, char *str); /* add a single char to the pool */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* xmlnodes - Document Object Model */
+/* */
+/* --------------------------------------------------------- */
+#define NTYPE_TAG 0
+#define NTYPE_ATTRIB 1
+#define NTYPE_CDATA 2
+
+#define NTYPE_LAST 2
+#define NTYPE_UNDEF -1
+
+/* --------------------------------------------------------------------------
+ Node structure. Do not use directly! Always use accessor macros
+ and methods!
+ -------------------------------------------------------------------------- */
+typedef struct xmlnode_t
+{
+ char* name;
+ unsigned short type;
+ char* data;
+ int data_sz;
+ int complete;
+ pool p;
+ struct xmlnode_t* parent;
+ struct xmlnode_t* firstchild;
+ struct xmlnode_t* lastchild;
+ struct xmlnode_t* prev;
+ struct xmlnode_t* next;
+ struct xmlnode_t* firstattrib;
+ struct xmlnode_t* lastattrib;
+} _xmlnode, *xmlnode;
+
+/* Node creation routines */
+xmlnode xmlnode_wrap(xmlnode x,const char* wrapper);
+xmlnode xmlnode_new_tag(const char* name);
+xmlnode xmlnode_new_tag_pool(pool p, const char* name);
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name);
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size);
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node);
+xmlnode xmlnode_str(char *str, int len);
+xmlnode xmlnode_dup(xmlnode x); /* duplicate x */
+xmlnode xmlnode_dup_pool(pool p, xmlnode x);
+
+/* Node Memory Pool */
+pool xmlnode_pool(xmlnode node);
+
+/* Node editing */
+void xmlnode_hide(xmlnode child);
+void xmlnode_hide_attrib(xmlnode parent, const char *name);
+
+/* Node deletion routine, also frees the node pool! */
+void xmlnode_free(xmlnode node);
+
+/* Locates a child tag by name and returns it */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name);
+char* xmlnode_get_tag_data(xmlnode parent, const char* name);
+
+/* Attribute accessors */
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value);
+char* xmlnode_get_attrib(xmlnode owner, const char* name);
+void xmlnode_put_expat_attribs(xmlnode owner, const char** atts);
+
+/* Bastard am I, but these are fun for internal use ;-) */
+void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value);
+void* xmlnode_get_vattrib(xmlnode owner, const char* name);
+
+/* Node traversal routines */
+xmlnode xmlnode_get_firstchild(xmlnode parent);
+xmlnode xmlnode_get_lastchild(xmlnode parent);
+xmlnode xmlnode_get_nextsibling(xmlnode sibling);
+xmlnode xmlnode_get_prevsibling(xmlnode sibling);
+xmlnode xmlnode_get_parent(xmlnode node);
+
+/* Node information routines */
+char* xmlnode_get_name(xmlnode node);
+char* xmlnode_get_data(xmlnode node);
+
+int xmlnode_has_children(xmlnode node);
+
+/* Node-to-string translation */
+char* xmlnode2str(xmlnode node);
+
+/********** END OLD libxode.h BEGIN OLD jabber.h *************/
+
+
+// #define KARMA_DEBUG
+// default to disable karma
+#define KARMA_READ_MAX(k) (abs(k)*100) /* how much you are allowed to read off the sock */
+#define KARMA_INIT 5 /* internal "init" value */
+#define KARMA_HEARTBEAT 2 /* seconds to register for heartbeat */
+#define KARMA_MAX 10 /* total max karma you can have */
+#define KARMA_INC 1 /* how much to increment every KARMA_HEARTBEAT seconds */
+#define KARMA_DEC 0 /* how much to penalize for reading KARMA_READ_MAX in
+ KARMA_HEARTBEAT seconds */
+#define KARMA_PENALTY -5 /* where you go when you hit 0 karma */
+#define KARMA_RESTORE 5 /* where you go when you payed your penelty or INIT */
+#define KARMA_RESETMETER 0 /* Reset byte meter on restore default is falst */
+
+struct karma
+{
+ int init; /* struct initialized */
+ int reset_meter; /* reset the byte meter on restore */
+ int val; /* current karma value */
+ long bytes; /* total bytes read (in that time period) */
+ int max; /* max karma you can have */
+ int inc,dec; /* how much to increment/decrement */
+ int penalty,restore; /* what penalty (<0) or restore (>0) */
+ time_t last_update; /* time this was last incremented */
+};
+
+struct karma *karma_new(pool p); /* creates a new karma object, with default values */
+void karma_copy(struct karma *new, struct karma *old); /* makes a copy of old in new */
+void karma_increment(struct karma *k); /* inteligently increments karma */
+void karma_decrement(struct karma *k, long bytes_read); /* inteligently decrements karma */
+int karma_check(struct karma *k,long bytes_read); /* checks to see if we have good karma */
+
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Namespace constants */
+/* */
+/* --------------------------------------------------------- */
+#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)
+
+#define NS_CLIENT "jabber:client"
+#define NS_SERVER "jabber:server"
+#define NS_AUTH "jabber:iq:auth"
+#define NS_REGISTER "jabber:iq:register"
+#define NS_ROSTER "jabber:iq:roster"
+#define NS_OFFLINE "jabber:x:offline"
+#define NS_AGENT "jabber:iq:agent"
+#define NS_AGENTS "jabber:iq:agents"
+#define NS_DELAY "jabber:x:delay"
+#define NS_VERSION "jabber:iq:version"
+#define NS_TIME "jabber:iq:time"
+#define NS_VCARD "vcard-temp"
+#define NS_PRIVATE "jabber:iq:private"
+#define NS_SEARCH "jabber:iq:search"
+#define NS_OOB "jabber:iq:oob"
+#define NS_XOOB "jabber:x:oob"
+#define NS_ADMIN "jabber:iq:admin"
+#define NS_FILTER "jabber:iq:filter"
+#define NS_AUTH_0K "jabber:iq:auth:0k"
+#define NS_BROWSE "jabber:iq:browse"
+#define NS_EVENT "jabber:x:event"
+#define NS_CONFERENCE "jabber:iq:conference"
+#define NS_SIGNED "jabber:x:signed"
+#define NS_ENCRYPTED "jabber:x:encrypted"
+#define NS_GATEWAY "jabber:iq:gateway"
+#define NS_LAST "jabber:iq:last"
+#define NS_ENVELOPE "jabber:x:envelope"
+#define NS_EXPIRE "jabber:x:expire"
+#define NS_XHTML "http://www.w3.org/1999/xhtml"
+
+#define NS_XDBGINSERT "jabber:xdb:ginsert"
+#define NS_XDBNSLIST "jabber:xdb:nslist"
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCL_LIB_H */
diff --git a/protocols/jabber/libxode.h b/protocols/jabber/libxode.h
new file mode 100644
index 00000000..2ed3fd83
--- /dev/null
+++ b/protocols/jabber/libxode.h
@@ -0,0 +1,398 @@
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <syslog.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#ifndef __CYGWIN__
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#include <sys/time.h>
+#include <time.h>
+
+#include "xmlparse.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+/*
+** Arrange to use either varargs or stdargs
+*/
+
+#define MAXSHORTSTR 203 /* max short string length */
+#define QUAD_T unsigned long long
+
+#ifdef __STDC__
+
+#include <stdarg.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap, f)
+# define VA_END va_end(ap)
+
+#else /* __STDC__ */
+
+# include <varargs.h>
+
+# define VA_LOCAL_DECL va_list ap;
+# define VA_START(f) va_start(ap)
+# define VA_END va_end(ap)
+
+#endif /* __STDC__ */
+
+
+#ifndef INCL_LIBXODE_H
+#define INCL_LIBXODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef HAVE_SNPRINTF
+extern int ap_snprintf(char *, size_t, const char *, ...);
+#define snprintf ap_snprintf
+#endif
+
+#ifndef HAVE_VSNPRINTF
+extern int ap_vsnprintf(char *, size_t, const char *, va_list ap);
+#define vsnprintf ap_vsnprintf
+#endif
+
+#define ZONE zonestr(__FILE__,__LINE__)
+char *zonestr(char *file, int line);
+
+/* --------------------------------------------------------- */
+/* */
+/* Pool-based memory management routines */
+/* */
+/* --------------------------------------------------------- */
+
+#undef POOL_DEBUG
+/*
+ flip these, this should be a prime number for top # of pools debugging
+#define POOL_DEBUG 40009
+*/
+
+/* pheap - singular allocation of memory */
+struct pheap
+{
+ void *block;
+ int size, used;
+};
+
+/* pool_cleaner - callback type which is associated
+ with a pool entry; invoked when the pool entry is
+ free'd */
+typedef void (*pool_cleaner)(void *arg);
+
+/* pfree - a linked list node which stores an
+ allocation chunk, plus a callback */
+struct pfree
+{
+ pool_cleaner f;
+ void *arg;
+ struct pheap *heap;
+ struct pfree *next;
+};
+
+/* pool - base node for a pool. Maintains a linked list
+ of pool entries (pfree) */
+typedef struct pool_struct
+{
+ int size;
+ struct pfree *cleanup;
+ struct pheap *heap;
+#ifdef POOL_DEBUG
+ char name[8], zone[32];
+ int lsize;
+} _pool, *pool;
+#define pool_new() _pool_new(ZONE)
+#define pool_heap(i) _pool_new_heap(i,ZONE)
+#else
+} _pool, *pool;
+#define pool_heap(i) _pool_new_heap(i,NULL)
+#define pool_new() _pool_new(NULL)
+#endif
+
+pool _pool_new(char *zone); /* new pool :) */
+pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */
+void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */
+void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */
+void *pmalloco(pool p, int size); /* YAPW for zeroing the block */
+char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */
+void pool_stat(int full); /* print to stderr the changed pools and reset */
+void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */
+void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */
+
+
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Socket helper stuff */
+/* */
+/* --------------------------------------------------------- */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define NETSOCKET_SERVER 0
+#define NETSOCKET_CLIENT 1
+#define NETSOCKET_UDP 2
+
+#ifndef WIN32
+int make_netsocket(u_short port, char *host, int type);
+struct in_addr *make_addr(char *host);
+int set_fd_close_on_exec(int fd, int flag);
+#endif
+
+
+/* --------------------------------------------------------- */
+/* */
+/* SHA calculations */
+/* */
+/* --------------------------------------------------------- */
+#if (SIZEOF_INT == 4)
+typedef unsigned int uint32;
+#elif (SIZEOF_SHORT == 4)
+typedef unsigned short uint32;
+#else
+typedef unsigned int uint32;
+#endif /* HAVEUINT32 */
+
+int sha_hash(int *data, int *hash);
+int sha_init(int *hash);
+char *shahash(char *str); /* NOT THREAD SAFE */
+void shahash_r(const char* str, char hashbuf[40]); /* USE ME */
+
+int strprintsha(char *dest, int *hashval);
+
+
+/* --------------------------------------------------------- */
+/* */
+/* Hashtable functions */
+/* */
+/* --------------------------------------------------------- */
+typedef int (*KEYHASHFUNC)(const void *key);
+typedef int (*KEYCOMPAREFUNC)(const void *key1, const void *key2);
+typedef int (*TABLEWALKFUNC)(void *user_data, const void *key, void *data);
+
+typedef void *HASHTABLE;
+
+HASHTABLE ghash_create(int buckets, KEYHASHFUNC hash, KEYCOMPAREFUNC cmp);
+void ghash_destroy(HASHTABLE tbl);
+void *ghash_get(HASHTABLE tbl, const void *key);
+int ghash_put(HASHTABLE tbl, const void *key, void *value);
+int ghash_remove(HASHTABLE tbl, const void *key);
+int ghash_walk(HASHTABLE tbl, TABLEWALKFUNC func, void *user_data);
+int str_hash_code(const char *s);
+
+
+/* --------------------------------------------------------- */
+/* */
+/* XML escaping utils */
+/* */
+/* --------------------------------------------------------- */
+char *strescape(pool p, char *buf); /* Escape <>&'" chars */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* String pools (spool) functions */
+/* */
+/* --------------------------------------------------------- */
+struct spool_node
+{
+ char *c;
+ struct spool_node *next;
+};
+
+typedef struct spool_struct
+{
+ pool p;
+ int len;
+ struct spool_node *last;
+ struct spool_node *first;
+} *spool;
+
+spool spool_new(pool p); /* create a string pool */
+void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */
+char *spool_print(spool s); /* return a big string */
+void spool_add(spool s, char *str); /* add a single char to the pool */
+char *spools(pool p, ...); /* wrap all the spooler stuff in one function, the happy fun ball! */
+
+
+/* --------------------------------------------------------- */
+/* */
+/* xmlnodes - Document Object Model */
+/* */
+/* --------------------------------------------------------- */
+#define NTYPE_TAG 0
+#define NTYPE_ATTRIB 1
+#define NTYPE_CDATA 2
+
+#define NTYPE_LAST 2
+#define NTYPE_UNDEF -1
+
+/* --------------------------------------------------------------------------
+ Node structure. Do not use directly! Always use accessor macros
+ and methods!
+ -------------------------------------------------------------------------- */
+typedef struct xmlnode_t
+{
+ char* name;
+ unsigned short type;
+ char* data;
+ int data_sz;
+ int complete;
+ pool p;
+ struct xmlnode_t* parent;
+ struct xmlnode_t* firstchild;
+ struct xmlnode_t* lastchild;
+ struct xmlnode_t* prev;
+ struct xmlnode_t* next;
+ struct xmlnode_t* firstattrib;
+ struct xmlnode_t* lastattrib;
+} _xmlnode, *xmlnode;
+
+/* Node creation routines */
+xmlnode xmlnode_wrap(xmlnode x,const char* wrapper);
+xmlnode xmlnode_new_tag(const char* name);
+xmlnode xmlnode_new_tag_pool(pool p, const char* name);
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name);
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size);
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node);
+void xmlnode_insert_node(xmlnode parent, xmlnode node);
+xmlnode xmlnode_str(char *str, int len);
+xmlnode xmlnode_file(char *file);
+xmlnode xmlnode_dup(xmlnode x); /* duplicate x */
+xmlnode xmlnode_dup_pool(pool p, xmlnode x);
+
+/* Node Memory Pool */
+pool xmlnode_pool(xmlnode node);
+xmlnode _xmlnode_new(pool p, const char *name, unsigned int type);
+
+/* Node editing */
+void xmlnode_hide(xmlnode child);
+void xmlnode_hide_attrib(xmlnode parent, const char *name);
+
+/* Node deletion routine, also frees the node pool! */
+void xmlnode_free(xmlnode node);
+
+/* Locates a child tag by name and returns it */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name);
+char* xmlnode_get_tag_data(xmlnode parent, const char* name);
+
+/* Attribute accessors */
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value);
+char* xmlnode_get_attrib(xmlnode owner, const char* name);
+void xmlnode_put_expat_attribs(xmlnode owner, const char** atts);
+
+/* Bastard am I, but these are fun for internal use ;-) */
+void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value);
+void* xmlnode_get_vattrib(xmlnode owner, const char* name);
+
+/* Node traversal routines */
+xmlnode xmlnode_get_firstattrib(xmlnode parent);
+xmlnode xmlnode_get_firstchild(xmlnode parent);
+xmlnode xmlnode_get_lastchild(xmlnode parent);
+xmlnode xmlnode_get_nextsibling(xmlnode sibling);
+xmlnode xmlnode_get_prevsibling(xmlnode sibling);
+xmlnode xmlnode_get_parent(xmlnode node);
+
+/* Node information routines */
+char* xmlnode_get_name(xmlnode node);
+char* xmlnode_get_data(xmlnode node);
+int xmlnode_get_datasz(xmlnode node);
+int xmlnode_get_type(xmlnode node);
+
+int xmlnode_has_children(xmlnode node);
+int xmlnode_has_attribs(xmlnode node);
+
+/* Node-to-string translation */
+char* xmlnode2str(xmlnode node);
+
+/* Node-to-terminated-string translation
+ -- useful for interfacing w/ scripting langs */
+char* xmlnode2tstr(xmlnode node);
+
+int xmlnode_cmp(xmlnode a, xmlnode b); /* compares a and b for equality */
+
+int xmlnode2file(char *file, xmlnode node); /* writes node to file */
+
+/* Expat callbacks */
+void expat_startElement(void* userdata, const char* name, const char** atts);
+void expat_endElement(void* userdata, const char* name);
+void expat_charData(void* userdata, const char* s, int len);
+
+/* SHA.H */
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is SHA 180-1 Header File
+ *
+ * The Initial Developer of the Original Code is Paul Kocher of
+ * Cryptography Research. Portions created by Paul Kocher are
+ * Copyright (C) 1995-9 by Cryptography Research, Inc. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Paul Kocher
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+typedef struct {
+ unsigned long H[5];
+ unsigned long W[80];
+ int lenW;
+ unsigned long sizeHi,sizeLo;
+} SHA_CTX;
+
+
+void shaInit(SHA_CTX *ctx);
+void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len);
+void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]);
+void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]);
+
+
+/* END SHA.H */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCL_LIBXODE_H */
diff --git a/protocols/jabber/log.c b/protocols/jabber/log.c
new file mode 100644
index 00000000..86d19e1d
--- /dev/null
+++ b/protocols/jabber/log.c
@@ -0,0 +1,44 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Jabber
+ * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#include "jabber.h"
+#include "log.h"
+
+#ifdef DEBUG
+
+void jdebug(char *zone, const char *msgfmt, ...)
+{
+ va_list ap;
+ static char loghdr[LOGSIZE_HDR];
+ static char logmsg[LOGSIZE_TAIL];
+ static int size;
+
+ /* XXX: We may want to check the sizes eventually */
+ size = g_snprintf(loghdr, LOGSIZE_HDR, "debug/%s %s\n", zone, msgfmt);
+
+ va_start(ap, msgfmt);
+ size = vsnprintf(logmsg, LOGSIZE_TAIL, loghdr, ap);
+
+ fprintf(stderr,"%s",logmsg);
+
+ return;
+}
+
+
+#endif /* DEBUG */
diff --git a/protocols/jabber/log.h b/protocols/jabber/log.h
new file mode 100644
index 00000000..9bce9e12
--- /dev/null
+++ b/protocols/jabber/log.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Jabber
+ * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#ifndef INCL_LOG_H
+#define INCL_LOG_H
+
+#define LOGSIZE_HDR 1024
+#define LOGSIZE_TAIL 2048
+
+
+#ifdef DEBUG
+ void jdebug(char *zone, const char *msgfmt, ...);
+#else
+ #define jdebug if(0) warn
+#endif
+
+
+
+#endif /* INCL_LOG_H */
+
diff --git a/protocols/jabber/nametab.h b/protocols/jabber/nametab.h
new file mode 100644
index 00000000..b05e62c7
--- /dev/null
+++ b/protocols/jabber/nametab.h
@@ -0,0 +1,150 @@
+static const unsigned namingBitmap[] = {
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
+0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
+0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
+0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
+0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
+0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
+0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
+0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
+0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
+0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+0x00000000, 0x00004C40, 0x00000000, 0x00000000,
+0x00000007, 0x00000000, 0x00000000, 0x00000000,
+0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
+0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
+0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
+0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
+0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
+0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
+0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
+0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
+0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
+0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
+0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/protocols/jabber/pool.c b/protocols/jabber/pool.c
new file mode 100644
index 00000000..8b88d747
--- /dev/null
+++ b/protocols/jabber/pool.c
@@ -0,0 +1,247 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include "bitlbee.h"
+#include <glib.h>
+
+
+#ifdef POOL_DEBUG
+int pool__total = 0;
+int pool__ltotal = 0;
+HASHTABLE pool__disturbed = NULL;
+void *_pool__malloc(size_t size)
+{
+ pool__total++;
+ return g_malloc(size);
+}
+void _pool__free(void *block)
+{
+ pool__total--;
+ g_free(block);
+}
+#else
+#define _pool__malloc g_malloc
+#define _pool__free g_free
+#endif
+
+
+/* make an empty pool */
+pool _pool_new(char *zone)
+{
+ pool p;
+ while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1);
+ p->cleanup = NULL;
+ p->heap = NULL;
+ p->size = 0;
+
+#ifdef POOL_DEBUG
+ p->lsize = -1;
+ p->zone[0] = '\0';
+ strcat(p->zone,zone);
+ sprintf(p->name,"%X",p);
+
+ if(pool__disturbed == NULL)
+ {
+ pool__disturbed = 1; /* reentrancy flag! */
+ pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
+ }
+ if(pool__disturbed != 1)
+ ghash_put(pool__disturbed,p->name,p);
+#endif
+
+ return p;
+}
+
+/* free a heap */
+static void _pool_heap_free(void *arg)
+{
+ struct pheap *h = (struct pheap *)arg;
+
+ _pool__free(h->block);
+ _pool__free(h);
+}
+
+/* mem should always be freed last */
+static void _pool_cleanup_append(pool p, struct pfree *pf)
+{
+ struct pfree *cur;
+
+ if(p->cleanup == NULL)
+ {
+ p->cleanup = pf;
+ return;
+ }
+
+ /* fast forward to end of list */
+ for(cur = p->cleanup; cur->next != NULL; cur = cur->next);
+
+ cur->next = pf;
+}
+
+/* create a cleanup tracker */
+static struct pfree *_pool_free(pool p, pool_cleaner f, void *arg)
+{
+ struct pfree *ret;
+
+ /* make the storage for the tracker */
+ while((ret = _pool__malloc(sizeof(struct pfree))) == NULL) sleep(1);
+ ret->f = f;
+ ret->arg = arg;
+ ret->next = NULL;
+
+ return ret;
+}
+
+/* create a heap and make sure it get's cleaned up */
+static struct pheap *_pool_heap(pool p, int size)
+{
+ struct pheap *ret;
+ struct pfree *clean;
+
+ /* make the return heap */
+ while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1);
+ while((ret->block = _pool__malloc(size)) == NULL) sleep(1);
+ ret->size = size;
+ p->size += size;
+ ret->used = 0;
+
+ /* append to the cleanup list */
+ clean = _pool_free(p, _pool_heap_free, (void *)ret);
+ clean->heap = ret; /* for future use in finding used mem for pstrdup */
+ _pool_cleanup_append(p, clean);
+
+ return ret;
+}
+
+pool _pool_new_heap(int size, char *zone)
+{
+ pool p;
+ p = _pool_new(zone);
+ p->heap = _pool_heap(p,size);
+ return p;
+}
+
+void *pmalloc(pool p, int size)
+{
+ void *block;
+
+ if(p == NULL)
+ {
+ fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n");
+ abort();
+ }
+
+ /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */
+ if(p->heap == NULL || size > (p->heap->size / 2))
+ {
+ while((block = _pool__malloc(size)) == NULL) sleep(1);
+ p->size += size;
+ _pool_cleanup_append(p, _pool_free(p, _pool__free, block));
+ return block;
+ }
+
+ /* we have to preserve boundaries, long story :) */
+ if(size >= 4)
+ while(p->heap->used&7) p->heap->used++;
+
+ /* if we don't fit in the old heap, replace it */
+ if(size > (p->heap->size - p->heap->used))
+ p->heap = _pool_heap(p, p->heap->size);
+
+ /* the current heap has room */
+ block = (char *)p->heap->block + p->heap->used;
+ p->heap->used += size;
+ return block;
+}
+
+void *pmalloc_x(pool p, int size, char c)
+{
+ void* result = pmalloc(p, size);
+ if (result != NULL)
+ memset(result, c, size);
+ return result;
+}
+
+/* easy safety utility (for creating blank mem for structs, etc) */
+void *pmalloco(pool p, int size)
+{
+ void *block = pmalloc(p, size);
+ memset(block, 0, size);
+ return block;
+}
+
+/* XXX efficient: move this to const char * and then loop throug the existing heaps to see if src is within a block in this pool */
+char *pstrdup(pool p, const char *src)
+{
+ char *ret;
+
+ if(src == NULL)
+ return NULL;
+
+ ret = pmalloc(p,strlen(src) + 1);
+ strcpy(ret,src);
+
+ return ret;
+}
+
+void pool_free(pool p)
+{
+ struct pfree *cur, *stub;
+
+ if(p == NULL) return;
+
+ cur = p->cleanup;
+ while(cur != NULL)
+ {
+ (*cur->f)(cur->arg);
+ stub = cur->next;
+ _pool__free(cur);
+ cur = stub;
+ }
+
+#ifdef POOL_DEBUG
+ ghash_remove(pool__disturbed,p->name);
+#endif
+
+ _pool__free(p);
+
+}
diff --git a/protocols/jabber/pproxy.c b/protocols/jabber/pproxy.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/pproxy.c
diff --git a/protocols/jabber/rate.c b/protocols/jabber/rate.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/rate.c
diff --git a/protocols/jabber/str.c b/protocols/jabber/str.c
new file mode 100644
index 00000000..a8454b44
--- /dev/null
+++ b/protocols/jabber/str.c
@@ -0,0 +1,215 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static char *j_strcat(char *dest, char *txt)
+{
+ if(!txt) return(dest);
+
+ while(*txt)
+ *dest++ = *txt++;
+ *dest = '\0';
+
+ return(dest);
+}
+
+int j_strcmp(const char *a, const char *b)
+{
+ if(a == NULL || b == NULL)
+ return -1;
+
+ while(*a == *b && *a != '\0' && *b != '\0'){ a++; b++; }
+
+ if(*a == *b) return 0;
+
+ return -1;
+}
+
+spool spool_new(pool p)
+{
+ spool s;
+
+ s = pmalloc(p, sizeof(struct spool_struct));
+ s->p = p;
+ s->len = 0;
+ s->last = NULL;
+ s->first = NULL;
+ return s;
+}
+
+void spool_add(spool s, char *str)
+{
+ struct spool_node *sn;
+ int len;
+
+ if(str == NULL)
+ return;
+
+ len = strlen(str);
+ if(len == 0)
+ return;
+
+ sn = pmalloc(s->p, sizeof(struct spool_node));
+ sn->c = pstrdup(s->p, str);
+ sn->next = NULL;
+
+ s->len += len;
+ if(s->last != NULL)
+ s->last->next = sn;
+ s->last = sn;
+ if(s->first == NULL)
+ s->first = sn;
+}
+
+void spooler(spool s, ...)
+{
+ va_list ap;
+ char *arg = NULL;
+
+ if(s == NULL)
+ return;
+
+ VA_START(s);
+
+ /* loop till we hfit our end flag, the first arg */
+ while(1)
+ {
+ arg = va_arg(ap,char *);
+ if((spool)arg == s)
+ break;
+ else
+ spool_add(s, arg);
+ }
+
+ va_end(ap);
+}
+
+char *spool_print(spool s)
+{
+ char *ret,*tmp;
+ struct spool_node *next;
+
+ if(s == NULL || s->len == 0 || s->first == NULL)
+ return NULL;
+
+ ret = pmalloc(s->p, s->len + 1);
+ *ret = '\0';
+
+ next = s->first;
+ tmp = ret;
+ while(next != NULL)
+ {
+ tmp = j_strcat(tmp,next->c);
+ next = next->next;
+ }
+
+ return ret;
+}
+
+char *strescape(pool p, char *buf)
+{
+ int i,j,oldlen,newlen;
+ char *temp;
+
+ if (p == NULL || buf == NULL) return(NULL);
+
+ oldlen = newlen = strlen(buf);
+ for(i=0;i<oldlen;i++)
+ {
+ switch(buf[i])
+ {
+ case '&':
+ newlen+=5;
+ break;
+ case '\'':
+ newlen+=6;
+ break;
+ case '\"':
+ newlen+=6;
+ break;
+ case '<':
+ newlen+=4;
+ break;
+ case '>':
+ newlen+=4;
+ break;
+ }
+ }
+
+ if(oldlen == newlen) return buf;
+
+ temp = pmalloc(p,newlen+1);
+
+ if (temp==NULL) return(NULL);
+
+ for(i=j=0;i<oldlen;i++)
+ {
+ switch(buf[i])
+ {
+ case '&':
+ memcpy(&temp[j],"&amp;",5);
+ j += 5;
+ break;
+ case '\'':
+ memcpy(&temp[j],"&apos;",6);
+ j += 6;
+ break;
+ case '\"':
+ memcpy(&temp[j],"&quot;",6);
+ j += 6;
+ break;
+ case '<':
+ memcpy(&temp[j],"&lt;",4);
+ j += 4;
+ break;
+ case '>':
+ memcpy(&temp[j],"&gt;",4);
+ j += 4;
+ break;
+ default:
+ temp[j++] = buf[i];
+ }
+ }
+ temp[j] = '\0';
+ return temp;
+}
diff --git a/protocols/jabber/utf8tab.h b/protocols/jabber/utf8tab.h
new file mode 100644
index 00000000..a38fe624
--- /dev/null
+++ b/protocols/jabber/utf8tab.h
@@ -0,0 +1,63 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
diff --git a/protocols/jabber/xhash.c b/protocols/jabber/xhash.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/xhash.c
diff --git a/protocols/jabber/xmldef.h b/protocols/jabber/xmldef.h
new file mode 100644
index 00000000..8b2b2308
--- /dev/null
+++ b/protocols/jabber/xmldef.h
@@ -0,0 +1,34 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
diff --git a/protocols/jabber/xmlnode.c b/protocols/jabber/xmlnode.c
new file mode 100644
index 00000000..88dd4eef
--- /dev/null
+++ b/protocols/jabber/xmlnode.c
@@ -0,0 +1,705 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above. If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL. If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL.
+ *
+ *
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent);
+static int xmlnode_get_type(xmlnode node);
+static void xmlnode_insert_node(xmlnode parent, xmlnode node);
+
+/* Internal routines */
+static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
+{
+ xmlnode result = NULL;
+ if (type > NTYPE_LAST)
+ return NULL;
+
+ if (type != NTYPE_CDATA && name == NULL)
+ return NULL;
+
+ if (p == NULL)
+ {
+ p = pool_heap(1*1024);
+ }
+
+ /* Allocate & zero memory */
+ result = (xmlnode)pmalloco(p, sizeof(_xmlnode));
+
+ /* Initialize fields */
+ if (type != NTYPE_CDATA)
+ result->name = pstrdup(p,name);
+ result->type = type;
+ result->p = p;
+ return result;
+}
+
+static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
+{
+ xmlnode result;
+
+ result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
+ if (result != NULL)
+ {
+ /* Setup sibling pointers */
+ result->prev = lastsibling;
+ lastsibling->next = result;
+ }
+ return result;
+}
+
+static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
+{
+ xmlnode result;
+
+ if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL;
+
+ /* If parent->firstchild is NULL, simply create a new node for the first child */
+ if (parent->firstchild == NULL)
+ {
+ result = _xmlnode_new(parent->p, name, type);
+ parent->firstchild = result;
+ }
+ /* Otherwise, append this to the lastchild */
+ else
+ {
+ result= _xmlnode_append_sibling(parent->lastchild, name, type);
+ }
+ result->parent = parent;
+ parent->lastchild = result;
+ return result;
+
+}
+
+static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
+{
+ xmlnode current;
+
+ /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
+ the specified name */
+ current = firstsibling;
+ while (current != NULL)
+ {
+ if ((current->type == type) && (j_strcmp(current->name, name) == 0))
+ return current;
+ else
+ current = current->next;
+ }
+ return NULL;
+}
+
+static void _xmlnode_merge(xmlnode data)
+{
+ xmlnode cur;
+ char *merge, *scur;
+ int imerge;
+
+ /* get total size of all merged cdata */
+ imerge = 0;
+ for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+ imerge += cur->data_sz;
+
+ /* copy in current data and then spin through all of them and merge */
+ scur = merge = pmalloc(data->p,imerge + 1);
+ for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+ {
+ memcpy(scur,cur->data,cur->data_sz);
+ scur += cur->data_sz;
+ }
+ *scur = '\0';
+
+ /* this effectively hides all of the merged-in chunks */
+ data->next = cur;
+ if(cur == NULL)
+ data->parent->lastchild = data;
+ else
+ cur->prev = data;
+
+ /* reset data */
+ data->data = merge;
+ data->data_sz = imerge;
+
+}
+
+static void _xmlnode_hide_sibling(xmlnode child)
+{
+ if(child == NULL)
+ return;
+
+ if(child->prev != NULL)
+ child->prev->next = child->next;
+ if(child->next != NULL)
+ child->next->prev = child->prev;
+}
+
+static void _xmlnode_tag2str(spool s, xmlnode node, int flag)
+{
+ xmlnode tmp;
+
+ if(flag==0 || flag==1)
+ {
+ spooler(s,"<",xmlnode_get_name(node),s);
+ tmp = xmlnode_get_firstattrib(node);
+ while(tmp) {
+ spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
+ tmp = xmlnode_get_nextsibling(tmp);
+ }
+ if(flag==0)
+ spool_add(s,"/>");
+ else
+ spool_add(s,">");
+ }
+ else
+ {
+ spooler(s,"</",xmlnode_get_name(node),">",s);
+ }
+}
+
+static spool _xmlnode2spool(xmlnode node)
+{
+ spool s;
+ int level=0,dir=0;
+ xmlnode tmp;
+
+ if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
+ return NULL;
+
+ s = spool_new(xmlnode_pool(node));
+ if(!s) return(NULL);
+
+ while(1)
+ {
+ if(dir==0)
+ {
+ if(xmlnode_get_type(node) == NTYPE_TAG)
+ {
+ if(xmlnode_has_children(node))
+ {
+ _xmlnode_tag2str(s,node,1);
+ node = xmlnode_get_firstchild(node);
+ level++;
+ continue;
+ }else{
+ _xmlnode_tag2str(s,node,0);
+ }
+ }else{
+ spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
+ }
+ }
+
+ tmp = xmlnode_get_nextsibling(node);
+ if(!tmp)
+ {
+ node = xmlnode_get_parent(node);
+ level--;
+ if(level>=0) _xmlnode_tag2str(s,node,2);
+ if(level<1) break;
+ dir = 1;
+ }else{
+ node = tmp;
+ dir = 0;
+ }
+ }
+
+ return s;
+}
+
+
+/* External routines */
+
+
+/*
+ * xmlnode_new_tag -- create a tag node
+ * Automatically creates a memory pool for the node.
+ *
+ * parameters
+ * name -- name of the tag
+ *
+ * returns
+ * a pointer to the tag node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_new_tag(const char* name)
+{
+ return _xmlnode_new(NULL, name, NTYPE_TAG);
+}
+
+
+/*
+ * xmlnode_insert_tag -- append a child tag to a tag
+ *
+ * parameters
+ * parent -- pointer to the parent tag
+ * name -- name of the child tag
+ *
+ * returns
+ * a pointer to the child tag node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
+{
+ return _xmlnode_insert(parent, name, NTYPE_TAG);
+}
+
+
+/*
+ * xmlnode_insert_cdata -- append character data to a tag
+ *
+ * parameters
+ * parent -- parent tag
+ * CDATA -- character data
+ * size -- size of CDATA
+ * or -1 for null-terminated CDATA strings
+ *
+ * returns
+ * a pointer to the child CDATA node
+ * or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
+{
+ xmlnode result;
+
+ if(CDATA == NULL || parent == NULL)
+ return NULL;
+
+ if(size == -1)
+ size = strlen(CDATA);
+
+ result = _xmlnode_insert(parent, NULL, NTYPE_CDATA);
+ if (result != NULL)
+ {
+ result->data = (char*)pmalloc(result->p, size + 1);
+ memcpy(result->data, CDATA, size);
+ result->data[size] = '\0';
+ result->data_sz = size;
+ }
+
+ return result;
+}
+
+
+/*
+ * xmlnode_get_tag -- find given tag in an xmlnode tree
+ *
+ * parameters
+ * parent -- pointer to the parent tag
+ * name -- "name" for the child tag of that name
+ * "name/name" for a sub child (recurses)
+ * "?attrib" to match the first tag with that attrib defined
+ * "?attrib=value" to match the first tag with that attrib and value
+ * "=cdata" to match the cdata contents of the child
+ * or any combination: "name/name/?attrib", "name=cdata", etc
+ *
+ * results
+ * a pointer to the tag matching search criteria
+ * or NULL if search was unsuccessfull
+ */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
+{
+ char *str, *slash, *qmark, *equals;
+ xmlnode step, ret;
+
+
+ if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
+
+ if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL)
+ return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
+
+ str = g_strdup(name);
+ slash = strstr(str, "/");
+ qmark = strstr(str, "?");
+ equals = strstr(str, "=");
+
+ if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark))
+ { /* of type =cdata */
+
+ *equals = '\0';
+ equals++;
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG)
+ continue;
+
+ if(*str != '\0')
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ if(j_strcmp(xmlnode_get_data(step),equals) != 0)
+ continue;
+
+ break;
+ }
+
+ g_free(str);
+ return step;
+ }
+
+
+ if(qmark != NULL && (slash == NULL || qmark < slash))
+ { /* of type ?attrib */
+
+ *qmark = '\0';
+ qmark++;
+ if(equals != NULL)
+ {
+ *equals = '\0';
+ equals++;
+ }
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG)
+ continue;
+
+ if(*str != '\0')
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ if(xmlnode_get_attrib(step,qmark) == NULL)
+ continue;
+
+ if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
+ continue;
+
+ break;
+ }
+
+ g_free(str);
+ return step;
+ }
+
+
+ *slash = '\0';
+ ++slash;
+
+ for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+ {
+ if(xmlnode_get_type(step) != NTYPE_TAG) continue;
+
+ if(j_strcmp(xmlnode_get_name(step),str) != 0)
+ continue;
+
+ ret = xmlnode_get_tag(step, slash);
+ if(ret != NULL)
+ {
+ g_free(str);
+ return ret;
+ }
+ }
+
+ g_free(str);
+ return NULL;
+}
+
+
+/* return the cdata from any tag */
+char *xmlnode_get_tag_data(xmlnode parent, const char *name)
+{
+ xmlnode tag;
+
+ tag = xmlnode_get_tag(parent, name);
+ if(tag == NULL) return NULL;
+
+ return xmlnode_get_data(tag);
+}
+
+
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
+{
+ xmlnode attrib;
+
+ if(owner == NULL || name == NULL || value == NULL) return;
+
+ /* If there are no existing attributs, allocate a new one to start
+ the list */
+ if (owner->firstattrib == NULL)
+ {
+ attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
+ owner->firstattrib = attrib;
+ owner->lastattrib = attrib;
+ }
+ else
+ {
+ attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+ if(attrib == NULL)
+ {
+ attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
+ owner->lastattrib = attrib;
+ }
+ }
+ /* Update the value of the attribute */
+ attrib->data_sz = strlen(value);
+ attrib->data = pstrdup(owner->p, value);
+
+}
+
+char* xmlnode_get_attrib(xmlnode owner, const char* name)
+{
+ xmlnode attrib;
+
+ if (owner != NULL && owner->firstattrib != NULL)
+ {
+ attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+ if (attrib != NULL)
+ return (char*)attrib->data;
+ }
+ return NULL;
+}
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent)
+{
+ if (parent != NULL)
+ return parent->firstattrib;
+ return NULL;
+}
+
+xmlnode xmlnode_get_firstchild(xmlnode parent)
+{
+ if (parent != NULL)
+ return parent->firstchild;
+ return NULL;
+}
+
+xmlnode xmlnode_get_nextsibling(xmlnode sibling)
+{
+ if (sibling != NULL)
+ return sibling->next;
+ return NULL;
+}
+
+xmlnode xmlnode_get_parent(xmlnode node)
+{
+ if (node != NULL)
+ return node->parent;
+ return NULL;
+}
+
+char* xmlnode_get_name(xmlnode node)
+{
+ if (node != NULL)
+ return node->name;
+ return NULL;
+}
+
+char* xmlnode_get_data(xmlnode node)
+{
+ if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */
+ for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node))
+ if(xmlnode_get_type(node) == NTYPE_CDATA) break;
+
+ if(node == NULL) return NULL;
+
+ /* check for a dirty node w/ unassembled cdata chunks */
+ if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+ _xmlnode_merge(node);
+
+ return node->data;
+}
+
+static int xmlnode_get_datasz(xmlnode node)
+{
+ if(xmlnode_get_type(node) != NTYPE_CDATA) return 0;
+
+ /* check for a dirty node w/ unassembled cdata chunks */
+ if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+ _xmlnode_merge(node);
+ return node->data_sz;
+}
+
+static int xmlnode_get_type(xmlnode node)
+{
+ if (node != NULL)
+ return node->type;
+ return NTYPE_UNDEF;
+}
+
+int xmlnode_has_children(xmlnode node)
+{
+ if ((node != NULL) && (node->firstchild != NULL))
+ return 1;
+ return 0;
+}
+
+static int xmlnode_has_attribs(xmlnode node)
+{
+ if ((node != NULL) && (node->firstattrib != NULL))
+ return 1;
+ return 0;
+}
+
+pool xmlnode_pool(xmlnode node)
+{
+ if (node != NULL)
+ return node->p;
+ return (pool)NULL;
+}
+
+void xmlnode_hide_attrib(xmlnode parent, const char *name)
+{
+ xmlnode attrib;
+
+ if(parent == NULL || parent->firstattrib == NULL || name == NULL)
+ return;
+
+ attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
+ if(attrib == NULL)
+ return;
+
+ /* first fix up at the child level */
+ _xmlnode_hide_sibling(attrib);
+
+ /* next fix up at the parent level */
+ if(parent->firstattrib == attrib)
+ parent->firstattrib = attrib->next;
+ if(parent->lastattrib == attrib)
+ parent->lastattrib = attrib->prev;
+}
+
+
+
+/*
+ * xmlnode2str -- convert given xmlnode tree into a string
+ *
+ * parameters
+ * node -- pointer to the xmlnode structure
+ *
+ * results
+ * a pointer to the created string
+ * or NULL if it was unsuccessfull
+ */
+char *xmlnode2str(xmlnode node)
+{
+ return spool_print(_xmlnode2spool(node));
+}
+
+/* loop through both a and b comparing everything, attribs, cdata, children, etc */
+static int xmlnode_cmp(xmlnode a, xmlnode b)
+{
+ int ret = 0;
+
+ while(1)
+ {
+ if(a == NULL && b == NULL)
+ return 0;
+
+ if(a == NULL || b == NULL)
+ return -1;
+
+ if(xmlnode_get_type(a) != xmlnode_get_type(b))
+ return -1;
+
+ switch(xmlnode_get_type(a))
+ {
+ case NTYPE_ATTRIB:
+ ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+ if(ret != 0)
+ return -1;
+ ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+ if(ret != 0)
+ return -1;
+ break;
+ case NTYPE_TAG:
+ ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+ if(ret != 0)
+ return -1;
+ ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
+ if(ret != 0)
+ return -1;
+ ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
+ if(ret != 0)
+ return -1;
+ break;
+ case NTYPE_CDATA:
+ ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+ if(ret != 0)
+ return -1;
+ }
+ a = xmlnode_get_nextsibling(a);
+ b = xmlnode_get_nextsibling(b);
+ }
+}
+
+
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
+{
+ xmlnode child;
+
+ child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
+ if (xmlnode_has_attribs(node))
+ xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
+ if (xmlnode_has_children(node))
+ xmlnode_insert_node(child, xmlnode_get_firstchild(node));
+
+ return child;
+}
+
+/* places copy of node and node's siblings in parent */
+static void xmlnode_insert_node(xmlnode parent, xmlnode node)
+{
+ if(node == NULL || parent == NULL)
+ return;
+
+ while(node != NULL)
+ {
+ switch(xmlnode_get_type(node))
+ {
+ case NTYPE_ATTRIB:
+ xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
+ break;
+ case NTYPE_TAG:
+ xmlnode_insert_tag_node(parent, node);
+ break;
+ case NTYPE_CDATA:
+ xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
+ }
+ node = xmlnode_get_nextsibling(node);
+ }
+}
+
+
+void xmlnode_free(xmlnode node)
+{
+ if(node == NULL)
+ return;
+
+ pool_free(node->p);
+}
diff --git a/protocols/jabber/xmlparse.c b/protocols/jabber/xmlparse.c
new file mode 100644
index 00000000..492da948
--- /dev/null
+++ b/protocols/jabber/xmlparse.c
@@ -0,0 +1,2629 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmlparse.h"
+
+#ifdef XML_UNICODE
+#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#define XmlConvert XmlUtf16Convert
+#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#define XmlEncode XmlUtf16Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1))
+typedef unsigned short ICHAR;
+#else
+#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#define XmlConvert XmlUtf8Convert
+#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#define XmlEncode XmlUtf8Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+
+#ifndef XML_NS
+
+#define XmlInitEncodingNS XmlInitEncoding
+#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#undef XmlGetInternalEncodingNS
+#define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_T(x) L ## x
+#else
+#define XML_T(x) x
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+
+#include "xmltok.h"
+#include "xmlrole.h"
+#include "hashtable.h"
+
+#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+ struct prefix *prefix;
+ struct binding *nextTagBinding;
+ struct binding *prevPrefixBinding;
+ const struct attribute_id *attId;
+ XML_Char *uri;
+ int uriLen;
+ int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+ const XML_Char *name;
+ BINDING *binding;
+} PREFIX;
+
+typedef struct {
+ const XML_Char *str;
+ const XML_Char *localPart;
+ int uriLen;
+} TAG_NAME;
+
+typedef struct tag {
+ struct tag *parent;
+ const char *rawName;
+ int rawNameLength;
+ TAG_NAME name;
+ char *buf;
+ char *bufEnd;
+ BINDING *bindings;
+} TAG;
+
+typedef struct {
+ const XML_Char *name;
+ const XML_Char *textPtr;
+ int textLen;
+ const XML_Char *systemId;
+ const XML_Char *base;
+ const XML_Char *publicId;
+ const XML_Char *notation;
+ char open;
+} ENTITY;
+
+typedef struct block {
+ struct block *next;
+ int size;
+ XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+ BLOCK *blocks;
+ BLOCK *freeBlocks;
+ const XML_Char *end;
+ XML_Char *ptr;
+ XML_Char *start;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+an attribute has been specified. */
+typedef struct attribute_id {
+ XML_Char *name;
+ PREFIX *prefix;
+ char maybeTokenized;
+ char xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+ const ATTRIBUTE_ID *id;
+ char isCdata;
+ const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+ const XML_Char *name;
+ PREFIX *prefix;
+ int nDefaultAtts;
+ int allocDefaultAtts;
+ DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+ HASH_TABLE generalEntities;
+ HASH_TABLE elementTypes;
+ HASH_TABLE attributeIds;
+ HASH_TABLE prefixes;
+ STRING_POOL pool;
+ int complete;
+ int standalone;
+ const XML_Char *base;
+ PREFIX defaultPrefix;
+} DTD;
+
+typedef struct open_internal_entity {
+ const char *internalEventPtr;
+ const char *internalEventEndPtr;
+ struct open_internal_entity *next;
+ ENTITY *entity;
+} OPEN_INTERNAL_ENTITY;
+
+typedef enum XML_Error Processor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+static Processor epilogProcessor;
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName);
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *, const char *);
+static enum XML_Error
+initializeEncoding(XML_Parser parser);
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+ const char *start, const char *end, const char **endPtr);
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr);
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s,
+ TAG_NAME *tagNamePtr, BINDING **bindingsPtr);
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr);
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, const XML_Char *dfltValue);
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+ STRING_POOL *);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+ STRING_POOL *);
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const char *start, const char *end);
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+
+static const XML_Char *getContext(XML_Parser parser);
+static void normalizePublicId(XML_Char *s);
+static int dtdInit(DTD *);
+static void dtdDestroy(DTD *);
+static void poolInit(STRING_POOL *);
+static void poolClear(STRING_POOL *);
+static void poolDestroy(STRING_POOL *);
+static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static int poolGrow(STRING_POOL *pool);
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s);
+static void *XML_GetBuffer(XML_Parser parser, int len);
+static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c) \
+ (((pool)->ptr == (pool)->end && !poolGrow(pool)) \
+ ? 0 \
+ : ((*((pool)->ptr)++ = c), 1))
+
+typedef struct {
+ /* The first member must be userData so that the XML_GetUserData macro works. */
+ void *m_userData;
+ void *m_handlerArg;
+ char *m_buffer;
+ /* first character to be parsed */
+ const char *m_bufferPtr;
+ /* past last character to be parsed */
+ char *m_bufferEnd;
+ /* allocated end of buffer */
+ const char *m_bufferLim;
+ long m_parseEndByteIndex;
+ const char *m_parseEndPtr;
+ XML_Char *m_dataBuf;
+ XML_Char *m_dataBufEnd;
+ XML_StartElementHandler m_startElementHandler;
+ XML_EndElementHandler m_endElementHandler;
+ XML_CharacterDataHandler m_characterDataHandler;
+ XML_ProcessingInstructionHandler m_processingInstructionHandler;
+ XML_CommentHandler m_commentHandler;
+ XML_StartCdataSectionHandler m_startCdataSectionHandler;
+ XML_EndCdataSectionHandler m_endCdataSectionHandler;
+ XML_DefaultHandler m_defaultHandler;
+ XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+ XML_NotationDeclHandler m_notationDeclHandler;
+ XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+ XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+ XML_NotStandaloneHandler m_notStandaloneHandler;
+ XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+ void *m_externalEntityRefHandlerArg;
+ XML_UnknownEncodingHandler m_unknownEncodingHandler;
+ const ENCODING *m_encoding;
+ INIT_ENCODING m_initEncoding;
+ const XML_Char *m_protocolEncodingName;
+ int m_ns;
+ void *m_unknownEncodingMem;
+ void *m_unknownEncodingData;
+ void *m_unknownEncodingHandlerData;
+ void (*m_unknownEncodingRelease)(void *);
+ PROLOG_STATE m_prologState;
+ Processor *m_processor;
+ enum XML_Error m_errorCode;
+ const char *m_eventPtr;
+ const char *m_eventEndPtr;
+ const char *m_positionPtr;
+ OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+ int m_defaultExpandInternalEntities;
+ int m_tagLevel;
+ ENTITY *m_declEntity;
+ const XML_Char *m_declNotationName;
+ const XML_Char *m_declNotationPublicId;
+ ELEMENT_TYPE *m_declElementType;
+ ATTRIBUTE_ID *m_declAttributeId;
+ char m_declAttributeIsCdata;
+ DTD m_dtd;
+ TAG *m_tagStack;
+ TAG *m_freeTagList;
+ BINDING *m_inheritedBindings;
+ BINDING *m_freeBindingList;
+ int m_attsSize;
+ int m_nSpecifiedAtts;
+ ATTRIBUTE *m_atts;
+ POSITION m_position;
+ STRING_POOL m_tempPool;
+ STRING_POOL m_temp2Pool;
+ char *m_groupConnector;
+ unsigned m_groupSize;
+ int m_hadExternalDoctype;
+ XML_Char m_namespaceSeparator;
+} Parser;
+
+#define userData (((Parser *)parser)->m_userData)
+#define handlerArg (((Parser *)parser)->m_handlerArg)
+#define startElementHandler (((Parser *)parser)->m_startElementHandler)
+#define endElementHandler (((Parser *)parser)->m_endElementHandler)
+#define characterDataHandler (((Parser *)parser)->m_characterDataHandler)
+#define processingInstructionHandler (((Parser *)parser)->m_processingInstructionHandler)
+#define commentHandler (((Parser *)parser)->m_commentHandler)
+#define startCdataSectionHandler (((Parser *)parser)->m_startCdataSectionHandler)
+#define endCdataSectionHandler (((Parser *)parser)->m_endCdataSectionHandler)
+#define defaultHandler (((Parser *)parser)->m_defaultHandler)
+#define unparsedEntityDeclHandler (((Parser *)parser)->m_unparsedEntityDeclHandler)
+#define notationDeclHandler (((Parser *)parser)->m_notationDeclHandler)
+#define startNamespaceDeclHandler (((Parser *)parser)->m_startNamespaceDeclHandler)
+#define endNamespaceDeclHandler (((Parser *)parser)->m_endNamespaceDeclHandler)
+#define notStandaloneHandler (((Parser *)parser)->m_notStandaloneHandler)
+#define externalEntityRefHandler (((Parser *)parser)->m_externalEntityRefHandler)
+#define externalEntityRefHandlerArg (((Parser *)parser)->m_externalEntityRefHandlerArg)
+#define unknownEncodingHandler (((Parser *)parser)->m_unknownEncodingHandler)
+#define encoding (((Parser *)parser)->m_encoding)
+#define initEncoding (((Parser *)parser)->m_initEncoding)
+#define unknownEncodingMem (((Parser *)parser)->m_unknownEncodingMem)
+#define unknownEncodingData (((Parser *)parser)->m_unknownEncodingData)
+#define unknownEncodingHandlerData \
+ (((Parser *)parser)->m_unknownEncodingHandlerData)
+#define unknownEncodingRelease (((Parser *)parser)->m_unknownEncodingRelease)
+#define protocolEncodingName (((Parser *)parser)->m_protocolEncodingName)
+#define ns (((Parser *)parser)->m_ns)
+#define prologState (((Parser *)parser)->m_prologState)
+#define processor (((Parser *)parser)->m_processor)
+#define errorCode (((Parser *)parser)->m_errorCode)
+#define eventPtr (((Parser *)parser)->m_eventPtr)
+#define eventEndPtr (((Parser *)parser)->m_eventEndPtr)
+#define positionPtr (((Parser *)parser)->m_positionPtr)
+#define position (((Parser *)parser)->m_position)
+#define openInternalEntities (((Parser *)parser)->m_openInternalEntities)
+#define defaultExpandInternalEntities (((Parser *)parser)->m_defaultExpandInternalEntities)
+#define tagLevel (((Parser *)parser)->m_tagLevel)
+#define buffer (((Parser *)parser)->m_buffer)
+#define bufferPtr (((Parser *)parser)->m_bufferPtr)
+#define bufferEnd (((Parser *)parser)->m_bufferEnd)
+#define parseEndByteIndex (((Parser *)parser)->m_parseEndByteIndex)
+#define parseEndPtr (((Parser *)parser)->m_parseEndPtr)
+#define bufferLim (((Parser *)parser)->m_bufferLim)
+#define dataBuf (((Parser *)parser)->m_dataBuf)
+#define dataBufEnd (((Parser *)parser)->m_dataBufEnd)
+#define dtd (((Parser *)parser)->m_dtd)
+#define declEntity (((Parser *)parser)->m_declEntity)
+#define declNotationName (((Parser *)parser)->m_declNotationName)
+#define declNotationPublicId (((Parser *)parser)->m_declNotationPublicId)
+#define declElementType (((Parser *)parser)->m_declElementType)
+#define declAttributeId (((Parser *)parser)->m_declAttributeId)
+#define declAttributeIsCdata (((Parser *)parser)->m_declAttributeIsCdata)
+#define freeTagList (((Parser *)parser)->m_freeTagList)
+#define freeBindingList (((Parser *)parser)->m_freeBindingList)
+#define inheritedBindings (((Parser *)parser)->m_inheritedBindings)
+#define tagStack (((Parser *)parser)->m_tagStack)
+#define atts (((Parser *)parser)->m_atts)
+#define attsSize (((Parser *)parser)->m_attsSize)
+#define nSpecifiedAtts (((Parser *)parser)->m_nSpecifiedAtts)
+#define tempPool (((Parser *)parser)->m_tempPool)
+#define temp2Pool (((Parser *)parser)->m_temp2Pool)
+#define groupConnector (((Parser *)parser)->m_groupConnector)
+#define groupSize (((Parser *)parser)->m_groupSize)
+#define hadExternalDoctype (((Parser *)parser)->m_hadExternalDoctype)
+#define namespaceSeparator (((Parser *)parser)->m_namespaceSeparator)
+
+#ifdef _MSC_VER
+#ifdef _DEBUG
+Parser *asParser(XML_Parser parser)
+{
+ return parser;
+}
+#endif
+#endif
+
+XML_Parser XML_ParserCreate(const XML_Char *encodingName)
+{
+ XML_Parser parser = malloc(sizeof(Parser));
+ if (!parser)
+ return parser;
+ processor = prologInitProcessor;
+ XmlPrologStateInit(&prologState);
+ userData = 0;
+ handlerArg = 0;
+ startElementHandler = 0;
+ endElementHandler = 0;
+ characterDataHandler = 0;
+ processingInstructionHandler = 0;
+ commentHandler = 0;
+ startCdataSectionHandler = 0;
+ endCdataSectionHandler = 0;
+ defaultHandler = 0;
+ unparsedEntityDeclHandler = 0;
+ notationDeclHandler = 0;
+ startNamespaceDeclHandler = 0;
+ endNamespaceDeclHandler = 0;
+ notStandaloneHandler = 0;
+ externalEntityRefHandler = 0;
+ externalEntityRefHandlerArg = parser;
+ unknownEncodingHandler = 0;
+ buffer = 0;
+ bufferPtr = 0;
+ bufferEnd = 0;
+ parseEndByteIndex = 0;
+ parseEndPtr = 0;
+ bufferLim = 0;
+ declElementType = 0;
+ declAttributeId = 0;
+ declEntity = 0;
+ declNotationName = 0;
+ declNotationPublicId = 0;
+ memset(&position, 0, sizeof(POSITION));
+ errorCode = XML_ERROR_NONE;
+ eventPtr = 0;
+ eventEndPtr = 0;
+ positionPtr = 0;
+ openInternalEntities = 0;
+ tagLevel = 0;
+ tagStack = 0;
+ freeTagList = 0;
+ freeBindingList = 0;
+ inheritedBindings = 0;
+ attsSize = INIT_ATTS_SIZE;
+ atts = malloc(attsSize * sizeof(ATTRIBUTE));
+ nSpecifiedAtts = 0;
+ dataBuf = malloc(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+ groupSize = 0;
+ groupConnector = 0;
+ hadExternalDoctype = 0;
+ unknownEncodingMem = 0;
+ unknownEncodingRelease = 0;
+ unknownEncodingData = 0;
+ unknownEncodingHandlerData = 0;
+ namespaceSeparator = '!';
+ ns = 0;
+ poolInit(&tempPool);
+ poolInit(&temp2Pool);
+ protocolEncodingName = encodingName ? poolCopyString(&tempPool, encodingName) : 0;
+ if (!dtdInit(&dtd) || !atts || !dataBuf
+ || (encodingName && !protocolEncodingName)) {
+ XML_ParserFree(parser);
+ return 0;
+ }
+ dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+ XmlInitEncoding(&initEncoding, &encoding, 0);
+ return parser;
+}
+
+static
+void destroyBindings(BINDING *bindings)
+{
+ for (;;) {
+ BINDING *b = bindings;
+ if (!b)
+ break;
+ bindings = b->nextTagBinding;
+ g_free(b->uri);
+ g_free(b);
+ }
+}
+
+void XML_ParserFree(XML_Parser parser)
+{
+ for (;;) {
+ TAG *p;
+ if (tagStack == 0) {
+ if (freeTagList == 0)
+ break;
+ tagStack = freeTagList;
+ freeTagList = 0;
+ }
+ p = tagStack;
+ tagStack = tagStack->parent;
+ g_free(p->buf);
+ destroyBindings(p->bindings);
+ g_free(p);
+ }
+ destroyBindings(freeBindingList);
+ destroyBindings(inheritedBindings);
+ poolDestroy(&tempPool);
+ poolDestroy(&temp2Pool);
+ dtdDestroy(&dtd);
+ g_free((void *)atts);
+ g_free(groupConnector);
+ g_free(buffer);
+ g_free(dataBuf);
+ g_free(unknownEncodingMem);
+ if (unknownEncodingRelease)
+ unknownEncodingRelease(unknownEncodingData);
+ g_free(parser);
+}
+
+void XML_SetUserData(XML_Parser parser, void *p)
+{
+ if (handlerArg == userData)
+ handlerArg = userData = p;
+ else
+ userData = p;
+}
+
+void XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end)
+{
+ startElementHandler = start;
+ endElementHandler = end;
+}
+
+void XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler)
+{
+ characterDataHandler = handler;
+}
+
+int XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
+{
+ if (len == 0) {
+ if (!isFinal)
+ return 1;
+ positionPtr = bufferPtr;
+ errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0);
+ if (errorCode == XML_ERROR_NONE)
+ return 1;
+ eventEndPtr = eventPtr;
+ return 0;
+ }
+ else if (bufferPtr == bufferEnd) {
+ const char *end;
+ int nLeftOver;
+ parseEndByteIndex += len;
+ positionPtr = s;
+ if (isFinal) {
+ errorCode = processor(parser, s, parseEndPtr = s + len, 0);
+ if (errorCode == XML_ERROR_NONE)
+ return 1;
+ eventEndPtr = eventPtr;
+ return 0;
+ }
+ errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+ if (errorCode != XML_ERROR_NONE) {
+ eventEndPtr = eventPtr;
+ return 0;
+ }
+ XmlUpdatePosition(encoding, positionPtr, end, &position);
+ nLeftOver = s + len - end;
+ if (nLeftOver) {
+ if (buffer == 0 || nLeftOver > bufferLim - buffer) {
+ /* FIXME avoid integer overflow */
+ buffer = buffer == 0 ? malloc(len * 2) : realloc(buffer, len * 2);
+ if (!buffer) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ eventPtr = eventEndPtr = 0;
+ return 0;
+ }
+ bufferLim = buffer + len * 2;
+ }
+ memcpy(buffer, end, nLeftOver);
+ bufferPtr = buffer;
+ bufferEnd = buffer + nLeftOver;
+ }
+ return 1;
+ }
+ else {
+ memcpy(XML_GetBuffer(parser, len), s, len);
+ return XML_ParseBuffer(parser, len, isFinal);
+ }
+}
+
+static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
+{
+ const char *start = bufferPtr;
+ positionPtr = start;
+ bufferEnd += len;
+ parseEndByteIndex += len;
+ errorCode = processor(parser, start, parseEndPtr = bufferEnd,
+ isFinal ? (const char **)0 : &bufferPtr);
+ if (errorCode == XML_ERROR_NONE) {
+ if (!isFinal)
+ XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+ return 1;
+ }
+ else {
+ eventEndPtr = eventPtr;
+ return 0;
+ }
+}
+
+static void *XML_GetBuffer(XML_Parser parser, int len)
+{
+ if (len > bufferLim - bufferEnd) {
+ /* FIXME avoid integer overflow */
+ int neededSize = len + (bufferEnd - bufferPtr);
+ if (neededSize <= bufferLim - buffer) {
+ memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
+ bufferEnd = buffer + (bufferEnd - bufferPtr);
+ bufferPtr = buffer;
+ }
+ else {
+ char *newBuf;
+ int bufferSize = bufferLim - bufferPtr;
+ if (bufferSize == 0)
+ bufferSize = INIT_BUFFER_SIZE;
+ do {
+ bufferSize *= 2;
+ } while (bufferSize < neededSize);
+ newBuf = malloc(bufferSize);
+ if (newBuf == 0) {
+ errorCode = XML_ERROR_NO_MEMORY;
+ return 0;
+ }
+ bufferLim = newBuf + bufferSize;
+ if (bufferPtr) {
+ memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
+ g_free(buffer);
+ }
+ bufferEnd = newBuf + (bufferEnd - bufferPtr);
+ bufferPtr = buffer = newBuf;
+ }
+ }
+ return bufferEnd;
+}
+
+static
+enum XML_Error contentProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ return doContent(parser, 0, encoding, start, end, endPtr);
+}
+
+static enum XML_Error
+doContent(XML_Parser parser,
+ int startTagLevel,
+ const ENCODING *enc,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ for (;;) {
+ const char *next = s; /* XmlContentTok doesn't always set the last arg */
+ int tok = XmlContentTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ *eventEndPP = end;
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ if (startTagLevel == 0)
+ return XML_ERROR_NO_ELEMENTS;
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ return XML_ERROR_NONE;
+ case XML_TOK_NONE:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (startTagLevel > 0) {
+ if (tagLevel != startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_NO_ELEMENTS;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ XML_Char ch = XmlPredefinedEntityName(enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (characterDataHandler)
+ characterDataHandler(handlerArg, &ch, 1);
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ name = poolStoreString(&dtd.pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+ poolDiscard(&dtd.pool);
+ if (!entity) {
+ if (dtd.complete || dtd.standalone)
+ return XML_ERROR_UNDEFINED_ENTITY;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ if (entity->open)
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ if (entity->notation)
+ return XML_ERROR_BINARY_ENTITY_REF;
+ if (entity) {
+ if (entity->textPtr) {
+ enum XML_Error result;
+ OPEN_INTERNAL_ENTITY openEntity;
+ if (defaultHandler && !defaultExpandInternalEntities) {
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ entity->open = 1;
+ openEntity.next = openInternalEntities;
+ openInternalEntities = &openEntity;
+ openEntity.entity = entity;
+ openEntity.internalEventPtr = 0;
+ openEntity.internalEventEndPtr = 0;
+ result = doContent(parser,
+ tagLevel,
+ internalEnc,
+ (char *)entity->textPtr,
+ (char *)(entity->textPtr + entity->textLen),
+ 0);
+ entity->open = 0;
+ openInternalEntities = openEntity.next;
+ if (result)
+ return result;
+ }
+ else if (externalEntityRefHandler) {
+ const XML_Char *context;
+ entity->open = 1;
+ context = getContext(parser);
+ entity->open = 0;
+ if (!context)
+ return XML_ERROR_NO_MEMORY;
+ if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ context,
+ dtd.base,
+ entity->systemId,
+ entity->publicId))
+ return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+ poolDiscard(&tempPool);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ }
+ case XML_TOK_START_TAG_WITH_ATTS:
+ if (!startElementHandler) {
+ enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+ if (result)
+ return result;
+ }
+ /* fall through */
+ case XML_TOK_START_TAG_NO_ATTS:
+ {
+ TAG *tag;
+ if (freeTagList) {
+ tag = freeTagList;
+ freeTagList = freeTagList->parent;
+ }
+ else {
+ tag = malloc(sizeof(TAG));
+ if (!tag)
+ return XML_ERROR_NO_MEMORY;
+ tag->buf = malloc(INIT_TAG_BUF_SIZE);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+ }
+ tag->bindings = 0;
+ tag->parent = tagStack;
+ tagStack = tag;
+ tag->name.localPart = 0;
+ tag->rawName = s + enc->minBytesPerChar;
+ tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+ if (nextPtr) {
+ /* Need to guarantee that:
+ tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)) <= tag->bufEnd - sizeof(XML_Char) */
+ if (tag->rawNameLength + (int)(sizeof(XML_Char) - 1) + (int)sizeof(XML_Char) > tag->bufEnd - tag->buf) {
+ int bufSize = tag->rawNameLength * 4;
+ bufSize = ROUND_UP(bufSize, sizeof(XML_Char));
+ tag->buf = realloc(tag->buf, bufSize);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + bufSize;
+ }
+ memcpy(tag->buf, tag->rawName, tag->rawNameLength);
+ tag->rawName = tag->buf;
+ }
+ ++tagLevel;
+ if (startElementHandler) {
+ enum XML_Error result;
+ XML_Char *toPtr;
+ for (;;) {
+ const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+ const char *fromPtr = tag->rawName;
+ int bufSize;
+ if (nextPtr)
+ toPtr = (XML_Char *)(tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)));
+ else
+ toPtr = (XML_Char *)tag->buf;
+ tag->name.str = toPtr;
+ XmlConvert(enc,
+ &fromPtr, rawNameEnd,
+ (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1);
+ if (fromPtr == rawNameEnd)
+ break;
+ bufSize = (tag->bufEnd - tag->buf) << 1;
+ tag->buf = realloc(tag->buf, bufSize);
+ if (!tag->buf)
+ return XML_ERROR_NO_MEMORY;
+ tag->bufEnd = tag->buf + bufSize;
+ if (nextPtr)
+ tag->rawName = tag->buf;
+ }
+ *toPtr = XML_T('\0');
+ result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+ if (result)
+ return result;
+ startElementHandler(handlerArg, tag->name.str, (const XML_Char **)atts);
+ poolClear(&tempPool);
+ }
+ else {
+ tag->name.str = 0;
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ }
+ case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+ if (!startElementHandler) {
+ enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+ if (result)
+ return result;
+ }
+ /* fall through */
+ case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+ if (startElementHandler || endElementHandler) {
+ const char *rawName = s + enc->minBytesPerChar;
+ enum XML_Error result;
+ BINDING *bindings = 0;
+ TAG_NAME name;
+ name.str = poolStoreString(&tempPool, enc, rawName,
+ rawName + XmlNameLength(enc, rawName));
+ if (!name.str)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ result = storeAtts(parser, enc, s, &name, &bindings);
+ if (result)
+ return result;
+ poolFinish(&tempPool);
+ if (startElementHandler)
+ startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+ if (endElementHandler) {
+ if (startElementHandler)
+ *eventPP = *eventEndPP;
+ endElementHandler(handlerArg, name.str);
+ }
+ poolClear(&tempPool);
+ while (bindings) {
+ BINDING *b = bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ bindings = bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ break;
+ case XML_TOK_END_TAG:
+ if (tagLevel == startTagLevel)
+ return XML_ERROR_ASYNC_ENTITY;
+ else {
+ int len;
+ const char *rawName;
+ TAG *tag = tagStack;
+ tagStack = tag->parent;
+ tag->parent = freeTagList;
+ freeTagList = tag;
+ rawName = s + enc->minBytesPerChar*2;
+ len = XmlNameLength(enc, rawName);
+ if (len != tag->rawNameLength
+ || memcmp(tag->rawName, rawName, len) != 0) {
+ *eventPP = rawName;
+ return XML_ERROR_TAG_MISMATCH;
+ }
+ --tagLevel;
+ if (endElementHandler && tag->name.str) {
+ if (tag->name.localPart) {
+ XML_Char *to = (XML_Char *)tag->name.str + tag->name.uriLen;
+ const XML_Char *from = tag->name.localPart;
+ while ((*to++ = *from++) != 0)
+ ;
+ }
+ endElementHandler(handlerArg, tag->name.str);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ while (tag->bindings) {
+ BINDING *b = tag->bindings;
+ if (endNamespaceDeclHandler)
+ endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ tag->bindings = tag->bindings->nextTagBinding;
+ b->nextTagBinding = freeBindingList;
+ freeBindingList = b;
+ b->prefix->binding = b->prevPrefixBinding;
+ }
+ if (tagLevel == 0)
+ return epilogProcessor(parser, next, end, nextPtr);
+ }
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ int n = XmlCharRefNumber(enc, s);
+ if (n < 0)
+ return XML_ERROR_BAD_CHAR_REF;
+ if (characterDataHandler) {
+ XML_Char buf[XML_ENCODE_MAX];
+ characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ }
+ break;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_CDATA_SECT_OPEN:
+ {
+ enum XML_Error result;
+ if (startCdataSectionHandler)
+ startCdataSectionHandler(handlerArg);
+#if 0
+ /* Suppose you doing a transformation on a document that involves
+ changing only the character data. You set up a defaultHandler
+ and a characterDataHandler. The defaultHandler simply copies
+ characters through. The characterDataHandler does the transformation
+ and writes the characters out escaping them as necessary. This case
+ will fail to work if we leave out the following two lines (because &
+ and < inside CDATA sections will be incorrectly escaped).
+
+ However, now we have a start/endCdataSectionHandler, so it seems
+ easier to let the user deal with this. */
+
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ result = doCdataSection(parser, enc, &next, end, nextPtr);
+ if (!next) {
+ processor = cdataSectionProcessor;
+ return result;
+ }
+ }
+ break;
+ case XML_TOK_TRAILING_RSQB:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)end - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, end);
+ if (startTagLevel == 0) {
+ *eventPP = end;
+ return XML_ERROR_NO_ELEMENTS;
+ }
+ if (tagLevel != startTagLevel) {
+ *eventPP = end;
+ return XML_ERROR_ASYNC_ENTITY;
+ }
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_CHARS:
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)next - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, enc, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ default:
+ if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ }
+ *eventPP = s = next;
+ }
+ /* not reached */
+}
+
+/* If tagNamePtr is non-null, build a real list of attributes,
+otherwise just check the attributes for well-formedness. */
+
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc,
+ const char *s, TAG_NAME *tagNamePtr,
+ BINDING **bindingsPtr)
+{
+ ELEMENT_TYPE *elementType = 0;
+ int nDefaultAtts = 0;
+ const XML_Char **appAtts;
+ int attIndex = 0;
+ int i;
+ int n;
+ int nPrefixes = 0;
+ BINDING *binding;
+ const XML_Char *localPart;
+
+ if (tagNamePtr) {
+ elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, 0);
+ if (!elementType) {
+ tagNamePtr->str = poolCopyString(&dtd.pool, tagNamePtr->str);
+ if (!tagNamePtr->str)
+ return XML_ERROR_NO_MEMORY;
+ elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, sizeof(ELEMENT_TYPE));
+ if (!elementType)
+ return XML_ERROR_NO_MEMORY;
+ if (ns && !setElementTypePrefix(parser, elementType))
+ return XML_ERROR_NO_MEMORY;
+ }
+ nDefaultAtts = elementType->nDefaultAtts;
+ }
+ n = XmlGetAttributes(enc, s, attsSize, atts);
+ if (n + nDefaultAtts > attsSize) {
+ int oldAttsSize = attsSize;
+ attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+ atts = realloc((void *)atts, attsSize * sizeof(ATTRIBUTE));
+ if (!atts)
+ return XML_ERROR_NO_MEMORY;
+ if (n > oldAttsSize)
+ XmlGetAttributes(enc, s, n, atts);
+ }
+ appAtts = (const XML_Char **)atts;
+ for (i = 0; i < n; i++) {
+ ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name,
+ atts[i].name
+ + XmlNameLength(enc, atts[i].name));
+ if (!attId)
+ return XML_ERROR_NO_MEMORY;
+ if ((attId->name)[-1]) {
+ if (enc == encoding)
+ eventPtr = atts[i].name;
+ return XML_ERROR_DUPLICATE_ATTRIBUTE;
+ }
+ (attId->name)[-1] = 1;
+ appAtts[attIndex++] = attId->name;
+ if (!atts[i].normalized) {
+ enum XML_Error result;
+ int isCdata = 1;
+
+ if (attId->maybeTokenized) {
+ int j;
+ for (j = 0; j < nDefaultAtts; j++) {
+ if (attId == elementType->defaultAtts[j].id) {
+ isCdata = elementType->defaultAtts[j].isCdata;
+ break;
+ }
+ }
+ }
+
+ result = storeAttributeValue(parser, enc, isCdata,
+ atts[i].valuePtr, atts[i].valueEnd,
+ &tempPool);
+ if (result)
+ return result;
+ if (tagNamePtr) {
+ appAtts[attIndex] = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ }
+ else
+ poolDiscard(&tempPool);
+ }
+ else if (tagNamePtr) {
+ appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd);
+ if (appAtts[attIndex] == 0)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ }
+ if (attId->prefix && tagNamePtr) {
+ if (attId->xmlns) {
+ if (!addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr))
+ return XML_ERROR_NO_MEMORY;
+ --attIndex;
+ }
+ else {
+ attIndex++;
+ nPrefixes++;
+ (attId->name)[-1] = 2;
+ }
+ }
+ else
+ attIndex++;
+ }
+ nSpecifiedAtts = attIndex;
+ if (tagNamePtr) {
+ int j;
+ for (j = 0; j < nDefaultAtts; j++) {
+ const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + j;
+ if (!(da->id->name)[-1] && da->value) {
+ if (da->id->prefix) {
+ if (da->id->xmlns) {
+ if (!addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr))
+ return XML_ERROR_NO_MEMORY;
+ }
+ else {
+ (da->id->name)[-1] = 2;
+ nPrefixes++;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ else {
+ (da->id->name)[-1] = 1;
+ appAtts[attIndex++] = da->id->name;
+ appAtts[attIndex++] = da->value;
+ }
+ }
+ }
+ appAtts[attIndex] = 0;
+ }
+ i = 0;
+ if (nPrefixes) {
+ for (; i < attIndex; i += 2) {
+ if (appAtts[i][-1] == 2) {
+ ATTRIBUTE_ID *id;
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, appAtts[i], 0);
+ if (id->prefix->binding) {
+ int j;
+ const BINDING *b = id->prefix->binding;
+ const XML_Char *s = appAtts[i];
+ for (j = 0; j < b->uriLen; j++) {
+ if (!poolAppendChar(&tempPool, b->uri[j]))
+ return XML_ERROR_NO_MEMORY;
+ }
+ while (*s++ != ':')
+ ;
+ do {
+ if (!poolAppendChar(&tempPool, *s))
+ return XML_ERROR_NO_MEMORY;
+ } while (*s++);
+ appAtts[i] = poolStart(&tempPool);
+ poolFinish(&tempPool);
+ }
+ if (!--nPrefixes)
+ break;
+ }
+ else
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ }
+ }
+ for (; i < attIndex; i += 2)
+ ((XML_Char *)(appAtts[i]))[-1] = 0;
+ if (!tagNamePtr)
+ return XML_ERROR_NONE;
+ for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+ binding->attId->name[-1] = 0;
+ if (elementType->prefix) {
+ binding = elementType->prefix->binding;
+ if (!binding)
+ return XML_ERROR_NONE;
+ localPart = tagNamePtr->str;
+ while (*localPart++ != XML_T(':'))
+ ;
+ }
+ else if (dtd.defaultPrefix.binding) {
+ binding = dtd.defaultPrefix.binding;
+ localPart = tagNamePtr->str;
+ }
+ else
+ return XML_ERROR_NONE;
+ tagNamePtr->localPart = localPart;
+ tagNamePtr->uriLen = binding->uriLen;
+ i = binding->uriLen;
+ do {
+ if (i == binding->uriAlloc) {
+ binding->uri = realloc(binding->uri, binding->uriAlloc *= 2);
+ if (!binding->uri)
+ return XML_ERROR_NO_MEMORY;
+ }
+ binding->uri[i++] = *localPart;
+ } while (*localPart++);
+ tagNamePtr->str = binding->uri;
+ return XML_ERROR_NONE;
+}
+
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr)
+{
+ BINDING *b;
+ int len;
+ for (len = 0; uri[len]; len++)
+ ;
+ if (namespaceSeparator)
+ len++;
+ if (freeBindingList) {
+ b = freeBindingList;
+ if (len > b->uriAlloc) {
+ b->uri = realloc(b->uri, len + EXPAND_SPARE);
+ if (!b->uri)
+ return 0;
+ b->uriAlloc = len + EXPAND_SPARE;
+ }
+ freeBindingList = b->nextTagBinding;
+ }
+ else {
+ b = malloc(sizeof(BINDING));
+ if (!b)
+ return 0;
+ b->uri = malloc(sizeof(XML_Char) * len + EXPAND_SPARE);
+ if (!b->uri) {
+ g_free(b);
+ return 0;
+ }
+ b->uriAlloc = len;
+ }
+ b->uriLen = len;
+ memcpy(b->uri, uri, len * sizeof(XML_Char));
+ if (namespaceSeparator)
+ b->uri[len - 1] = namespaceSeparator;
+ b->prefix = prefix;
+ b->attId = attId;
+ b->prevPrefixBinding = prefix->binding;
+ if (*uri == XML_T('\0') && prefix == &dtd.defaultPrefix)
+ prefix->binding = 0;
+ else
+ prefix->binding = b;
+ b->nextTagBinding = *bindingsPtr;
+ *bindingsPtr = b;
+ if (startNamespaceDeclHandler)
+ startNamespaceDeclHandler(handlerArg, prefix->name,
+ prefix->binding ? uri : 0);
+ return 1;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+the whole file is parsed with one call. */
+
+static
+enum XML_Error cdataSectionProcessor(XML_Parser parser,
+ const char *start,
+ const char *end,
+ const char **endPtr)
+{
+ enum XML_Error result = doCdataSection(parser, encoding, &start, end, endPtr);
+ if (start) {
+ processor = contentProcessor;
+ return contentProcessor(parser, start, end, endPtr);
+ }
+ return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null if
+the section is not yet closed. */
+
+static
+enum XML_Error doCdataSection(XML_Parser parser,
+ const ENCODING *enc,
+ const char **startPtr,
+ const char *end,
+ const char **nextPtr)
+{
+ const char *s = *startPtr;
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ *eventPP = s;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ *eventPP = s;
+ *startPtr = 0;
+ for (;;) {
+ const char *next;
+ int tok = XmlCdataSectionTok(enc, s, end, &next);
+ *eventEndPP = next;
+ switch (tok) {
+ case XML_TOK_CDATA_SECT_CLOSE:
+ if (endCdataSectionHandler)
+ endCdataSectionHandler(handlerArg);
+#if 0
+ /* see comment under XML_TOK_CDATA_SECT_OPEN */
+ else if (characterDataHandler)
+ characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ *startPtr = next;
+ return XML_ERROR_NONE;
+ case XML_TOK_DATA_NEWLINE:
+ if (characterDataHandler) {
+ XML_Char c = 0xA;
+ characterDataHandler(handlerArg, &c, 1);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_DATA_CHARS:
+ if (characterDataHandler) {
+ if (MUST_CONVERT(enc, s)) {
+ for (;;) {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = next;
+ characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ if (s == next)
+ break;
+ *eventPP = s;
+ }
+ }
+ else
+ characterDataHandler(handlerArg,
+ (XML_Char *)s,
+ (XML_Char *)next - (XML_Char *)s);
+ }
+ else if (defaultHandler)
+ reportDefault(parser, enc, s, next);
+ break;
+ case XML_TOK_INVALID:
+ *eventPP = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_PARTIAL:
+ case XML_TOK_NONE:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_CDATA_SECTION;
+ default:
+ abort();
+ }
+ *eventPP = s = next;
+ }
+ /* not reached */
+}
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser)
+{
+ const char *s;
+#ifdef XML_UNICODE
+ char encodingBuf[128];
+ if (!protocolEncodingName)
+ s = 0;
+ else {
+ int i;
+ for (i = 0; protocolEncodingName[i]; i++) {
+ if (i == sizeof(encodingBuf) - 1
+ || protocolEncodingName[i] >= 0x80
+ || protocolEncodingName[i] < 0) {
+ encodingBuf[0] = '\0';
+ break;
+ }
+ encodingBuf[i] = (char)protocolEncodingName[i];
+ }
+ encodingBuf[i] = '\0';
+ s = encodingBuf;
+ }
+#else
+s = protocolEncodingName;
+#endif
+ if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+ return XML_ERROR_NONE;
+ return handleUnknownEncoding(parser, protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+ const char *s, const char *next)
+{
+ const char *encodingName = 0;
+ const ENCODING *newEncoding = 0;
+ const char *version;
+ int standalone = -1;
+ if (!(ns
+ ? XmlParseXmlDeclNS
+ : XmlParseXmlDecl)(isGeneralTextEntity,
+ encoding,
+ s,
+ next,
+ &eventPtr,
+ &version,
+ &encodingName,
+ &newEncoding,
+ &standalone))
+ return XML_ERROR_SYNTAX;
+ if (!isGeneralTextEntity && standalone == 1)
+ dtd.standalone = 1;
+ if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ if (!protocolEncodingName) {
+ if (newEncoding) {
+ if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+ eventPtr = encodingName;
+ return XML_ERROR_INCORRECT_ENCODING;
+ }
+ encoding = newEncoding;
+ }
+ else if (encodingName) {
+ enum XML_Error result;
+ const XML_Char *s = poolStoreString(&tempPool,
+ encoding,
+ encodingName,
+ encodingName
+ + XmlNameLength(encoding, encodingName));
+ if (!s)
+ return XML_ERROR_NO_MEMORY;
+ result = handleUnknownEncoding(parser, s);
+ poolDiscard(&tempPool);
+ if (result == XML_ERROR_UNKNOWN_ENCODING)
+ eventPtr = encodingName;
+ return result;
+ }
+ }
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+ if (unknownEncodingHandler) {
+ XML_Encoding info;
+ int i;
+ for (i = 0; i < 256; i++)
+ info.map[i] = -1;
+ info.convert = 0;
+ info.data = 0;
+ info.release = 0;
+ if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) {
+ ENCODING *enc;
+ unknownEncodingMem = malloc(XmlSizeOfUnknownEncoding());
+ if (!unknownEncodingMem) {
+ if (info.release)
+ info.release(info.data);
+ return XML_ERROR_NO_MEMORY;
+ }
+ enc = (ns
+ ? XmlInitUnknownEncodingNS
+ : XmlInitUnknownEncoding)(unknownEncodingMem,
+ info.map,
+ info.convert,
+ info.data);
+ if (enc) {
+ unknownEncodingData = info.data;
+ unknownEncodingRelease = info.release;
+ encoding = enc;
+ return XML_ERROR_NONE;
+ }
+ }
+ if (info.release)
+ info.release(info.data);
+ }
+ return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error
+prologInitProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ enum XML_Error result = initializeEncoding(parser);
+ if (result != XML_ERROR_NONE)
+ return result;
+ processor = prologProcessor;
+ return prologProcessor(parser, s, end, nextPtr);
+}
+
+static enum XML_Error
+prologProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ for (;;) {
+ const char *next;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ if (tok <= 0) {
+ if (nextPtr != 0 && tok != XML_TOK_INVALID) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ switch (tok) {
+ case XML_TOK_INVALID:
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_NONE:
+ return XML_ERROR_NO_ELEMENTS;
+ case XML_TOK_PARTIAL:
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ return XML_ERROR_PARTIAL_CHAR;
+ case XML_TOK_TRAILING_CR:
+ eventPtr = s + encoding->minBytesPerChar;
+ return XML_ERROR_NO_ELEMENTS;
+ default:
+ abort();
+ }
+ }
+ switch (XmlTokenRole(&prologState, tok, s, next, encoding)) {
+ case XML_ROLE_XML_DECL:
+ {
+ enum XML_Error result = processXmlDecl(parser, 0, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ }
+ break;
+ case XML_ROLE_DOCTYPE_SYSTEM_ID:
+ if (!dtd.standalone
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ hadExternalDoctype = 1;
+ break;
+ case XML_ROLE_DOCTYPE_PUBLIC_ID:
+ case XML_ROLE_ENTITY_PUBLIC_ID:
+ if (!XmlIsPublicId(encoding, s, next, &eventPtr))
+ return XML_ERROR_SYNTAX;
+ if (declEntity) {
+ XML_Char *tem = poolStoreString(&dtd.pool,
+ encoding,
+ s + encoding->minBytesPerChar,
+ next - encoding->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declEntity->publicId = tem;
+ poolFinish(&dtd.pool);
+ }
+ break;
+ case XML_ROLE_INSTANCE_START:
+ processor = contentProcessor;
+ if (hadExternalDoctype)
+ dtd.complete = 0;
+ return contentProcessor(parser, s, end, nextPtr);
+ case XML_ROLE_ATTLIST_ELEMENT_NAME:
+ {
+ const XML_Char *name = poolStoreString(&dtd.pool, encoding, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ declElementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, name, sizeof(ELEMENT_TYPE));
+ if (!declElementType)
+ return XML_ERROR_NO_MEMORY;
+ if (declElementType->name != name)
+ poolDiscard(&dtd.pool);
+ else {
+ poolFinish(&dtd.pool);
+ if (!setElementTypePrefix(parser, declElementType))
+ return XML_ERROR_NO_MEMORY;
+ }
+ break;
+ }
+ case XML_ROLE_ATTRIBUTE_NAME:
+ declAttributeId = getAttributeId(parser, encoding, s, next);
+ if (!declAttributeId)
+ return XML_ERROR_NO_MEMORY;
+ declAttributeIsCdata = 0;
+ break;
+ case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+ declAttributeIsCdata = 1;
+ break;
+ case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+ case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+ if (dtd.complete
+ && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, 0))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+ case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+ {
+ const XML_Char *attVal;
+ enum XML_Error result
+ = storeAttributeValue(parser, encoding, declAttributeIsCdata,
+ s + encoding->minBytesPerChar,
+ next - encoding->minBytesPerChar,
+ &dtd.pool);
+ if (result)
+ return result;
+ attVal = poolStart(&dtd.pool);
+ poolFinish(&dtd.pool);
+ if (dtd.complete
+ && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, attVal))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ case XML_ROLE_ENTITY_VALUE:
+ {
+ enum XML_Error result = storeEntityValue(parser, s, next);
+ if (result != XML_ERROR_NONE)
+ return result;
+ }
+ break;
+ case XML_ROLE_ENTITY_SYSTEM_ID:
+ if (declEntity) {
+ declEntity->systemId = poolStoreString(&dtd.pool, encoding,
+ s + encoding->minBytesPerChar,
+ next - encoding->minBytesPerChar);
+ if (!declEntity->systemId)
+ return XML_ERROR_NO_MEMORY;
+ declEntity->base = dtd.base;
+ poolFinish(&dtd.pool);
+ }
+ break;
+ case XML_ROLE_ENTITY_NOTATION_NAME:
+ if (declEntity) {
+ declEntity->notation = poolStoreString(&dtd.pool, encoding, s, next);
+ if (!declEntity->notation)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&dtd.pool);
+ if (unparsedEntityDeclHandler) {
+ eventPtr = eventEndPtr = s;
+ unparsedEntityDeclHandler(handlerArg,
+ declEntity->name,
+ declEntity->base,
+ declEntity->systemId,
+ declEntity->publicId,
+ declEntity->notation);
+ }
+
+ }
+ break;
+ case XML_ROLE_GENERAL_ENTITY_NAME:
+ {
+ const XML_Char *name;
+ if (XmlPredefinedEntityName(encoding, s, next)) {
+ declEntity = 0;
+ break;
+ }
+ name = poolStoreString(&dtd.pool, encoding, s, next);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ if (dtd.complete) {
+ declEntity = (ENTITY *)lookup(&dtd.generalEntities, name, sizeof(ENTITY));
+ if (!declEntity)
+ return XML_ERROR_NO_MEMORY;
+ if (declEntity->name != name) {
+ poolDiscard(&dtd.pool);
+ declEntity = 0;
+ }
+ else
+ poolFinish(&dtd.pool);
+ }
+ else {
+ poolDiscard(&dtd.pool);
+ declEntity = 0;
+ }
+ }
+ break;
+ case XML_ROLE_PARAM_ENTITY_NAME:
+ declEntity = 0;
+ break;
+ case XML_ROLE_NOTATION_NAME:
+ declNotationPublicId = 0;
+ declNotationName = 0;
+ if (notationDeclHandler) {
+ declNotationName = poolStoreString(&tempPool, encoding, s, next);
+ if (!declNotationName)
+ return XML_ERROR_NO_MEMORY;
+ poolFinish(&tempPool);
+ }
+ break;
+ case XML_ROLE_NOTATION_PUBLIC_ID:
+ if (!XmlIsPublicId(encoding, s, next, &eventPtr))
+ return XML_ERROR_SYNTAX;
+ if (declNotationName) {
+ XML_Char *tem = poolStoreString(&tempPool,
+ encoding,
+ s + encoding->minBytesPerChar,
+ next - encoding->minBytesPerChar);
+ if (!tem)
+ return XML_ERROR_NO_MEMORY;
+ normalizePublicId(tem);
+ declNotationPublicId = tem;
+ poolFinish(&tempPool);
+ }
+ break;
+ case XML_ROLE_NOTATION_SYSTEM_ID:
+ if (declNotationName && notationDeclHandler) {
+ const XML_Char *systemId
+ = poolStoreString(&tempPool, encoding,
+ s + encoding->minBytesPerChar,
+ next - encoding->minBytesPerChar);
+ if (!systemId)
+ return XML_ERROR_NO_MEMORY;
+ eventPtr = eventEndPtr = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ dtd.base,
+ systemId,
+ declNotationPublicId);
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+ if (declNotationPublicId && notationDeclHandler) {
+ eventPtr = eventEndPtr = s;
+ notationDeclHandler(handlerArg,
+ declNotationName,
+ dtd.base,
+ 0,
+ declNotationPublicId);
+ }
+ poolClear(&tempPool);
+ break;
+ case XML_ROLE_ERROR:
+ eventPtr = s;
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ return XML_ERROR_PARAM_ENTITY_REF;
+ case XML_TOK_XML_DECL:
+ return XML_ERROR_MISPLACED_XML_PI;
+ default:
+ return XML_ERROR_SYNTAX;
+ }
+ case XML_ROLE_GROUP_OPEN:
+ if (prologState.level >= groupSize) {
+ if (groupSize)
+ groupConnector = realloc(groupConnector, groupSize *= 2);
+ else
+ groupConnector = malloc(groupSize = 32);
+ if (!groupConnector)
+ return XML_ERROR_NO_MEMORY;
+ }
+ groupConnector[prologState.level] = 0;
+ break;
+ case XML_ROLE_GROUP_SEQUENCE:
+ if (groupConnector[prologState.level] == '|') {
+ eventPtr = s;
+ return XML_ERROR_SYNTAX;
+ }
+ groupConnector[prologState.level] = ',';
+ break;
+ case XML_ROLE_GROUP_CHOICE:
+ if (groupConnector[prologState.level] == ',') {
+ eventPtr = s;
+ return XML_ERROR_SYNTAX;
+ }
+ groupConnector[prologState.level] = '|';
+ break;
+ case XML_ROLE_PARAM_ENTITY_REF:
+ if (!dtd.standalone
+ && notStandaloneHandler
+ && !notStandaloneHandler(handlerArg))
+ return XML_ERROR_NOT_STANDALONE;
+ dtd.complete = 0;
+ break;
+ case XML_ROLE_NONE:
+ switch (tok) {
+ case XML_TOK_PI:
+ eventPtr = s;
+ eventEndPtr = next;
+ if (!reportProcessingInstruction(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ eventPtr = s;
+ eventEndPtr = next;
+ if (!reportComment(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ break;
+ }
+ if (defaultHandler) {
+ switch (tok) {
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ case XML_TOK_BOM:
+ case XML_TOK_XML_DECL:
+ break;
+ default:
+ eventPtr = s;
+ eventEndPtr = next;
+ reportDefault(parser, encoding, s, next);
+ }
+ }
+ s = next;
+ }
+ /* not reached */
+}
+
+static
+enum XML_Error epilogProcessor(XML_Parser parser,
+ const char *s,
+ const char *end,
+ const char **nextPtr)
+{
+ processor = epilogProcessor;
+ eventPtr = s;
+ for (;;) {
+ const char *next;
+ int tok = XmlPrologTok(encoding, s, end, &next);
+ eventEndPtr = next;
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ if (defaultHandler) {
+ eventEndPtr = end;
+ reportDefault(parser, encoding, s, end);
+ }
+ /* fall through */
+ case XML_TOK_NONE:
+ if (nextPtr)
+ *nextPtr = end;
+ return XML_ERROR_NONE;
+ case XML_TOK_PROLOG_S:
+ if (defaultHandler)
+ reportDefault(parser, encoding, s, next);
+ break;
+ case XML_TOK_PI:
+ if (!reportProcessingInstruction(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_COMMENT:
+ if (!reportComment(parser, encoding, s, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_INVALID:
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_UNCLOSED_TOKEN;
+ case XML_TOK_PARTIAL_CHAR:
+ if (nextPtr) {
+ *nextPtr = s;
+ return XML_ERROR_NONE;
+ }
+ return XML_ERROR_PARTIAL_CHAR;
+ default:
+ return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+ }
+ eventPtr = s = next;
+ }
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
+ if (result)
+ return result;
+ if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+ poolChop(pool);
+ if (!poolAppendChar(pool, XML_T('\0')))
+ return XML_ERROR_NO_MEMORY;
+ return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+ const char *ptr, const char *end,
+ STRING_POOL *pool)
+{
+ const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+ for (;;) {
+ const char *next;
+ int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+ switch (tok) {
+ case XML_TOK_NONE:
+ return XML_ERROR_NONE;
+ case XML_TOK_INVALID:
+ if (enc == encoding)
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_PARTIAL:
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(enc, ptr);
+ if (n < 0) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ if (!isCdata
+ && n == 0x20 /* space */
+ && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ for (i = 0; i < n; i++) {
+ if (!poolAppendChar(pool, buf[i]))
+ return XML_ERROR_NO_MEMORY;
+ }
+ }
+ break;
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, ptr, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = ptr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_ATTRIBUTE_VALUE_S:
+ case XML_TOK_DATA_NEWLINE:
+ if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+ break;
+ if (!poolAppendChar(pool, 0x20))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_ENTITY_REF:
+ {
+ const XML_Char *name;
+ ENTITY *entity;
+ XML_Char ch = XmlPredefinedEntityName(enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (ch) {
+ if (!poolAppendChar(pool, ch))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ }
+ name = poolStoreString(&temp2Pool, enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ if (!name)
+ return XML_ERROR_NO_MEMORY;
+ entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+ poolDiscard(&temp2Pool);
+ if (!entity) {
+ if (dtd.complete) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_UNDEFINED_ENTITY;
+ }
+ }
+ else if (entity->open) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_RECURSIVE_ENTITY_REF;
+ }
+ else if (entity->notation) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_BINARY_ENTITY_REF;
+ }
+ else if (!entity->textPtr) {
+ if (enc == encoding)
+ eventPtr = ptr;
+ return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+ }
+ else {
+ enum XML_Error result;
+ const XML_Char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = 1;
+ result = appendAttributeValue(parser, internalEnc, isCdata, (char *)entity->textPtr, (char *)textEnd, pool);
+ entity->open = 0;
+ if (result)
+ return result;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ ptr = next;
+ }
+ /* not reached */
+}
+
+static
+enum XML_Error storeEntityValue(XML_Parser parser,
+ const char *entityTextPtr,
+ const char *entityTextEnd)
+{
+ const ENCODING *internalEnc;
+ STRING_POOL *pool = &(dtd.pool);
+ entityTextPtr += encoding->minBytesPerChar;
+ entityTextEnd -= encoding->minBytesPerChar;
+ internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+ for (;;) {
+ const char *next;
+ int tok = XmlEntityValueTok(encoding, entityTextPtr, entityTextEnd, &next);
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ eventPtr = entityTextPtr;
+ return XML_ERROR_SYNTAX;
+ case XML_TOK_NONE:
+ if (declEntity) {
+ declEntity->textPtr = pool->start;
+ declEntity->textLen = pool->ptr - pool->start;
+ poolFinish(pool);
+ }
+ else
+ poolDiscard(pool);
+ return XML_ERROR_NONE;
+ case XML_TOK_ENTITY_REF:
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, encoding, entityTextPtr, next))
+ return XML_ERROR_NO_MEMORY;
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = entityTextPtr + encoding->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_DATA_NEWLINE:
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return XML_ERROR_NO_MEMORY;
+ *(pool->ptr)++ = 0xA;
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ XML_Char buf[XML_ENCODE_MAX];
+ int i;
+ int n = XmlCharRefNumber(encoding, entityTextPtr);
+ if (n < 0) {
+ eventPtr = entityTextPtr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ n = XmlEncode(n, (ICHAR *)buf);
+ if (!n) {
+ eventPtr = entityTextPtr;
+ return XML_ERROR_BAD_CHAR_REF;
+ }
+ for (i = 0; i < n; i++) {
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return XML_ERROR_NO_MEMORY;
+ *(pool->ptr)++ = buf[i];
+ }
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ eventPtr = entityTextPtr;
+ return XML_ERROR_INVALID_TOKEN;
+ case XML_TOK_INVALID:
+ eventPtr = next;
+ return XML_ERROR_INVALID_TOKEN;
+ default:
+ abort();
+ }
+ entityTextPtr = next;
+ }
+ /* not reached */
+}
+
+static void
+normalizeLines(XML_Char *s)
+{
+ XML_Char *p;
+ for (;; s++) {
+ if (*s == XML_T('\0'))
+ return;
+ if (*s == 0xD)
+ break;
+ }
+ p = s;
+ do {
+ if (*s == 0xD) {
+ *p++ = 0xA;
+ if (*++s == 0xA)
+ s++;
+ }
+ else
+ *p++ = *s++;
+ } while (*s);
+ *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ const XML_Char *target;
+ XML_Char *data;
+ const char *tem;
+ if (!processingInstructionHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ start += enc->minBytesPerChar * 2;
+ tem = start + XmlNameLength(enc, start);
+ target = poolStoreString(&tempPool, enc, start, tem);
+ if (!target)
+ return 0;
+ poolFinish(&tempPool);
+ data = poolStoreString(&tempPool, enc,
+ XmlSkipS(enc, tem),
+ end - enc->minBytesPerChar*2);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ processingInstructionHandler(handlerArg, target, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ XML_Char *data;
+ if (!commentHandler) {
+ if (defaultHandler)
+ reportDefault(parser, enc, start, end);
+ return 1;
+ }
+ data = poolStoreString(&tempPool,
+ enc,
+ start + enc->minBytesPerChar * 4,
+ end - enc->minBytesPerChar * 3);
+ if (!data)
+ return 0;
+ normalizeLines(data);
+ commentHandler(handlerArg, data);
+ poolClear(&tempPool);
+ return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, const char *end)
+{
+ if (MUST_CONVERT(enc, s)) {
+ const char **eventPP;
+ const char **eventEndPP;
+ if (enc == encoding) {
+ eventPP = &eventPtr;
+ eventEndPP = &eventEndPtr;
+ }
+ else {
+ eventPP = &(openInternalEntities->internalEventPtr);
+ eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ }
+ do {
+ ICHAR *dataPtr = (ICHAR *)dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ *eventEndPP = s;
+ defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+ *eventPP = s;
+ } while (s != end);
+ }
+ else
+ defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s);
+}
+
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, const XML_Char *value)
+{
+ DEFAULT_ATTRIBUTE *att;
+ if (type->nDefaultAtts == type->allocDefaultAtts) {
+ if (type->allocDefaultAtts == 0) {
+ type->allocDefaultAtts = 8;
+ type->defaultAtts = malloc(type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+ }
+ else {
+ type->allocDefaultAtts *= 2;
+ type->defaultAtts = realloc(type->defaultAtts,
+ type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+ }
+ if (!type->defaultAtts)
+ return 0;
+ }
+ att = type->defaultAtts + type->nDefaultAtts;
+ att->id = attId;
+ att->value = value;
+ att->isCdata = isCdata;
+ if (!isCdata)
+ attId->maybeTokenized = 1;
+ type->nDefaultAtts += 1;
+ return 1;
+}
+
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
+{
+ const XML_Char *name;
+ for (name = elementType->name; *name; name++) {
+ if (*name == XML_T(':')) {
+ PREFIX *prefix;
+ const XML_Char *s;
+ for (s = elementType->name; s != name; s++) {
+ if (!poolAppendChar(&dtd.pool, *s))
+ return 0;
+ }
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+ if (!prefix)
+ return 0;
+ if (prefix->name == poolStart(&dtd.pool))
+ poolFinish(&dtd.pool);
+ else
+ poolDiscard(&dtd.pool);
+ elementType->prefix = prefix;
+
+ }
+ }
+ return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+ ATTRIBUTE_ID *id;
+ const XML_Char *name;
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ name = poolStoreString(&dtd.pool, enc, start, end);
+ if (!name)
+ return 0;
+ ++name;
+ id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID));
+ if (!id)
+ return 0;
+ if (id->name != name)
+ poolDiscard(&dtd.pool);
+ else {
+ poolFinish(&dtd.pool);
+ if (!ns)
+ ;
+ else if (name[0] == 'x'
+ && name[1] == 'm'
+ && name[2] == 'l'
+ && name[3] == 'n'
+ && name[4] == 's'
+ && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) {
+ if (name[5] == '\0')
+ id->prefix = &dtd.defaultPrefix;
+ else
+ id->prefix = (PREFIX *)lookup(&dtd.prefixes, name + 6, sizeof(PREFIX));
+ id->xmlns = 1;
+ }
+ else {
+ int i;
+ for (i = 0; name[i]; i++) {
+ if (name[i] == XML_T(':')) {
+ int j;
+ for (j = 0; j < i; j++) {
+ if (!poolAppendChar(&dtd.pool, name[j]))
+ return 0;
+ }
+ if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+ return 0;
+ id->prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+ if (id->prefix->name == poolStart(&dtd.pool))
+ poolFinish(&dtd.pool);
+ else
+ poolDiscard(&dtd.pool);
+ break;
+ }
+ }
+ }
+ }
+ return id;
+}
+
+#define CONTEXT_SEP XML_T('\f')
+
+static
+const XML_Char *getContext(XML_Parser parser)
+{
+ HASH_TABLE_ITER iter;
+ int needSep = 0;
+
+ if (dtd.defaultPrefix.binding) {
+ int i;
+ int len;
+ if (!poolAppendChar(&tempPool, XML_T('=')))
+ return 0;
+ len = dtd.defaultPrefix.binding->uriLen;
+ if (namespaceSeparator != XML_T('\0'))
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, dtd.defaultPrefix.binding->uri[i]))
+ return 0;
+ needSep = 1;
+ }
+
+ hashTableIterInit(&iter, &(dtd.prefixes));
+ for (;;) {
+ int i;
+ int len;
+ const XML_Char *s;
+ PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+ if (!prefix)
+ break;
+ if (!prefix->binding)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return 0;
+ for (s = prefix->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ if (!poolAppendChar(&tempPool, XML_T('=')))
+ return 0;
+ len = prefix->binding->uriLen;
+ if (namespaceSeparator != XML_T('\0'))
+ len--;
+ for (i = 0; i < len; i++)
+ if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+ return 0;
+ needSep = 1;
+ }
+
+
+ hashTableIterInit(&iter, &(dtd.generalEntities));
+ for (;;) {
+ const XML_Char *s;
+ ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (!e->open)
+ continue;
+ if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ return 0;
+ for (s = e->name; *s; s++)
+ if (!poolAppendChar(&tempPool, *s))
+ return 0;
+ needSep = 1;
+ }
+
+ if (!poolAppendChar(&tempPool, XML_T('\0')))
+ return 0;
+ return tempPool.start;
+}
+
+static
+void normalizePublicId(XML_Char *publicId)
+{
+ XML_Char *p = publicId;
+ XML_Char *s;
+ for (s = publicId; *s; s++) {
+ switch (*s) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ if (p != publicId && p[-1] != 0x20)
+ *p++ = 0x20;
+ break;
+ default:
+ *p++ = *s;
+ }
+ }
+ if (p != publicId && p[-1] == 0x20)
+ --p;
+ *p = XML_T('\0');
+}
+
+static int dtdInit(DTD *p)
+{
+ poolInit(&(p->pool));
+ hashTableInit(&(p->generalEntities));
+ hashTableInit(&(p->elementTypes));
+ hashTableInit(&(p->attributeIds));
+ hashTableInit(&(p->prefixes));
+ p->complete = 1;
+ p->standalone = 0;
+ p->base = 0;
+ p->defaultPrefix.name = 0;
+ p->defaultPrefix.binding = 0;
+ return 1;
+}
+
+static void dtdDestroy(DTD *p)
+{
+ HASH_TABLE_ITER iter;
+ hashTableIterInit(&iter, &(p->elementTypes));
+ for (;;) {
+ ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+ if (!e)
+ break;
+ if (e->allocDefaultAtts != 0)
+ g_free(e->defaultAtts);
+ }
+ hashTableDestroy(&(p->generalEntities));
+ hashTableDestroy(&(p->elementTypes));
+ hashTableDestroy(&(p->attributeIds));
+ hashTableDestroy(&(p->prefixes));
+ poolDestroy(&(p->pool));
+}
+
+static
+void poolInit(STRING_POOL *pool)
+{
+ pool->blocks = 0;
+ pool->freeBlocks = 0;
+ pool->start = 0;
+ pool->ptr = 0;
+ pool->end = 0;
+}
+
+static
+void poolClear(STRING_POOL *pool)
+{
+ if (!pool->freeBlocks)
+ pool->freeBlocks = pool->blocks;
+ else {
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ p->next = pool->freeBlocks;
+ pool->freeBlocks = p;
+ p = tem;
+ }
+ }
+ pool->blocks = 0;
+ pool->start = 0;
+ pool->ptr = 0;
+ pool->end = 0;
+}
+
+static
+void poolDestroy(STRING_POOL *pool)
+{
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ g_free(p);
+ p = tem;
+ }
+ pool->blocks = 0;
+ p = pool->freeBlocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ g_free(p);
+ p = tem;
+ }
+ pool->freeBlocks = 0;
+ pool->ptr = 0;
+ pool->start = 0;
+ pool->end = 0;
+}
+
+static
+XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!pool->ptr && !poolGrow(pool))
+ return 0;
+ for (;;) {
+ XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+ if (ptr == end)
+ break;
+ if (!poolGrow(pool))
+ return 0;
+ }
+ return pool->start;
+}
+
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s)
+{
+ do {
+ if (!poolAppendChar(pool, *s))
+ return 0;
+ } while (*s++);
+ s = pool->start;
+ poolFinish(pool);
+ return s;
+}
+
+static
+XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!poolAppend(pool, enc, ptr, end))
+ return 0;
+ if (pool->ptr == pool->end && !poolGrow(pool))
+ return 0;
+ *(pool->ptr)++ = 0;
+ return pool->start;
+}
+
+static
+int poolGrow(STRING_POOL *pool)
+{
+ if (pool->freeBlocks) {
+ if (pool->start == 0) {
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = pool->freeBlocks->next;
+ pool->blocks->next = 0;
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ pool->ptr = pool->start;
+ return 1;
+ }
+ if (pool->end - pool->start < pool->freeBlocks->size) {
+ BLOCK *tem = pool->freeBlocks->next;
+ pool->freeBlocks->next = pool->blocks;
+ pool->blocks = pool->freeBlocks;
+ pool->freeBlocks = tem;
+ memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char));
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + pool->blocks->size;
+ return 1;
+ }
+ }
+ if (pool->blocks && pool->start == pool->blocks->s) {
+ int blockSize = (pool->end - pool->start)*2;
+ pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+ if (!pool->blocks)
+ return 0;
+ pool->blocks->size = blockSize;
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + blockSize;
+ }
+ else {
+ BLOCK *tem;
+ int blockSize = pool->end - pool->start;
+ if (blockSize < INIT_BLOCK_SIZE)
+ blockSize = INIT_BLOCK_SIZE;
+ else
+ blockSize *= 2;
+ tem = malloc(offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+ if (!tem)
+ return 0;
+ tem->size = blockSize;
+ tem->next = pool->blocks;
+ pool->blocks = tem;
+ memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char));
+ pool->ptr = tem->s + (pool->ptr - pool->start);
+ pool->start = tem->s;
+ pool->end = tem->s + blockSize;
+ }
+ return 1;
+}
diff --git a/protocols/jabber/xmlparse.h b/protocols/jabber/xmlparse.h
new file mode 100644
index 00000000..f39edb8c
--- /dev/null
+++ b/protocols/jabber/xmlparse.h
@@ -0,0 +1,476 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlParse_INCLUDED
+#define XmlParse_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLPARSEAPI
+#define XMLPARSEAPI /* as nothing */
+#endif
+
+typedef void *XML_Parser;
+
+#ifdef XML_UNICODE_WCHAR_T
+
+/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t
+uses Unicode. */
+/* Information is UTF-16 encoded as wchar_ts */
+
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+
+#include <stddef.h>
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+
+#else /* not XML_UNICODE_WCHAR_T */
+
+#ifdef XML_UNICODE
+
+/* Information is UTF-16 encoded as unsigned shorts */
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+
+#else /* not XML_UNICODE */
+
+/* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+
+#endif /* not XML_UNICODE */
+
+#endif /* not XML_UNICODE_WCHAR_T */
+
+
+/* Constructs a new parser; encoding is the encoding specified by the external
+protocol or null if there is none specified. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor. Element type names
+and attribute names that belong to a namespace will be expanded;
+unprefixed attribute names are never expanded; unprefixed element type
+names are expanded only if there is a default namespace. The expanded
+name is the concatenation of the namespace URI, the namespace separator character,
+and the local part of the name. If the namespace separator is '\0' then
+the namespace URI and the local part will be concatenated without any
+separator. When a namespace is not declared, the name and prefix will be
+passed through without expansion. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+
+/* atts is array of name/value pairs, terminated by 0;
+ names and values are 0 terminated. */
+
+typedef void (*XML_StartElementHandler)(void *userData,
+ const XML_Char *name,
+ const XML_Char **atts);
+
+typedef void (*XML_EndElementHandler)(void *userData,
+ const XML_Char *name);
+
+/* s is not 0 terminated. */
+typedef void (*XML_CharacterDataHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+
+/* target and data are 0 terminated */
+typedef void (*XML_ProcessingInstructionHandler)(void *userData,
+ const XML_Char *target,
+ const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void (*XML_CommentHandler)(void *userData, const XML_Char *data);
+
+typedef void (*XML_StartCdataSectionHandler)(void *userData);
+typedef void (*XML_EndCdataSectionHandler)(void *userData);
+
+/* This is called for any characters in the XML document for
+which there is no applicable handler. This includes both
+characters that are part of markup which is of a kind that is
+not reported (comments, markup declarations), or characters
+that are part of a construct which could be reported but
+for which no handler has been supplied. The characters are passed
+exactly as they were in the XML document except that
+they will be encoded in UTF-8. Line boundaries are not normalized.
+Note that a byte order mark character is not passed to the default handler.
+There are no guarantees about how characters are divided between calls
+to the default handler: for example, a comment might be split between
+multiple calls. */
+
+typedef void (*XML_DefaultHandler)(void *userData,
+ const XML_Char *s,
+ int len);
+
+/* This is called for a declaration of an unparsed (NDATA)
+entity. The base argument is whatever was set by XML_SetBase.
+The entityName, systemId and notationName arguments will never be null.
+The other arguments may be. */
+
+typedef void (*XML_UnparsedEntityDeclHandler)(void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+
+/* This is called for a declaration of notation.
+The base argument is whatever was set by XML_SetBase.
+The notationName will never be null. The other arguments can be. */
+
+typedef void (*XML_NotationDeclHandler)(void *userData,
+ const XML_Char *notationName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+/* When namespace processing is enabled, these are called once for
+each namespace declaration. The call to the start and end element
+handlers occur between the calls to the start and end namespace
+declaration handlers. For an xmlns attribute, prefix will be null.
+For an xmlns="" attribute, uri will be null. */
+
+typedef void (*XML_StartNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix,
+ const XML_Char *uri);
+
+typedef void (*XML_EndNamespaceDeclHandler)(void *userData,
+ const XML_Char *prefix);
+
+/* This is called if the document is not standalone (it has an
+external subset or a reference to a parameter entity, but does not
+have standalone="yes"). If this handler returns 0, then processing
+will not continue, and the parser will return a
+XML_ERROR_NOT_STANDALONE error. */
+
+typedef int (*XML_NotStandaloneHandler)(void *userData);
+
+/* This is called for a reference to an external parsed general entity.
+The referenced entity is not automatically parsed.
+The application can parse it immediately or later using
+XML_ExternalEntityParserCreate.
+The parser argument is the parser parsing the entity containing the reference;
+it can be passed as the parser argument to XML_ExternalEntityParserCreate.
+The systemId argument is the system identifier as specified in the entity declaration;
+it will not be null.
+The base argument is the system identifier that should be used as the base for
+resolving systemId if systemId was relative; this is set by XML_SetBase;
+it may be null.
+The publicId argument is the public identifier as specified in the entity declaration,
+or null if none was specified; the whitespace in the public identifier
+will have been normalized as required by the XML spec.
+The context argument specifies the parsing context in the format
+expected by the context argument to
+XML_ExternalEntityParserCreate; context is valid only until the handler
+returns, so if the referenced entity is to be parsed later, it must be copied.
+The handler should return 0 if processing should not continue because of
+a fatal error in the handling of the external entity.
+In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING
+error.
+Note that unlike other handlers the first argument is the parser, not userData. */
+
+typedef int (*XML_ExternalEntityRefHandler)(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler
+to provide information to the parser about encodings that are unknown
+to the parser.
+The map[b] member gives information about byte sequences
+whose first byte is b.
+If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c.
+If map[b] is -1, then the byte sequence is malformed.
+If map[b] is -n, where n >= 2, then b is the first byte of an n-byte
+sequence that encodes a single Unicode scalar value.
+The data member will be passed as the first argument to the convert function.
+The convert function is used to convert multibyte sequences;
+s will point to a n-byte sequence where map[(unsigned char)*s] == -n.
+The convert function must return the Unicode scalar value
+represented by this byte sequence or -1 if the byte sequence is malformed.
+The convert function may be null if the encoding is a single-byte encoding,
+that is if map[b] >= -1 for all bytes b.
+When the parser is finished with the encoding, then if release is not null,
+it will call release passing it the data member;
+once release has been called, the convert function will not be called again.
+
+Expat places certain restrictions on the encodings that are supported
+using this mechanism.
+
+1. Every ASCII character that can appear in a well-formed XML document,
+other than the characters
+
+ $@\^`{}~
+
+must be represented by a single byte, and that byte must be the
+same byte that represents that character in ASCII.
+
+2. No character may require more than 4 bytes to encode.
+
+3. All characters encoded must have Unicode scalar values <= 0xFFFF,
+(ie characters that would be encoded by surrogates in UTF-16
+are not allowed). Note that this restriction doesn't apply to
+the built-in support for UTF-8 and UTF-16.
+
+4. No Unicode character may be encoded by more than one distinct sequence
+of bytes. */
+
+typedef struct {
+ int map[256];
+ void *data;
+ int (*convert)(void *data, const char *s);
+ void (*release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+The encodingHandlerData argument is that which was passed as the
+second argument to XML_SetUnknownEncodingHandler.
+The name argument gives the name of the encoding as specified in
+the encoding declaration.
+If the callback can provide information about the encoding,
+it must fill in the XML_Encoding structure, and return 1.
+Otherwise it must return 0.
+If info does not describe a suitable encoding,
+then the parser will return an XML_UNKNOWN_ENCODING error. */
+
+typedef int (*XML_UnknownEncodingHandler)(void *encodingHandlerData,
+ const XML_Char *name,
+ XML_Encoding *info);
+
+void XMLPARSEAPI
+XML_SetElementHandler(XML_Parser parser,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end);
+
+void XMLPARSEAPI
+XML_SetCharacterDataHandler(XML_Parser parser,
+ XML_CharacterDataHandler handler);
+
+void XMLPARSEAPI
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+ XML_ProcessingInstructionHandler handler);
+void XMLPARSEAPI
+XML_SetCommentHandler(XML_Parser parser,
+ XML_CommentHandler handler);
+
+void XMLPARSEAPI
+XML_SetCdataSectionHandler(XML_Parser parser,
+ XML_StartCdataSectionHandler start,
+ XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of internal entities.
+The entity reference will be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandler(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of internal entities.
+The entity reference will not be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+ XML_DefaultHandler handler);
+
+void XMLPARSEAPI
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+ XML_UnparsedEntityDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNotationDeclHandler(XML_Parser parser,
+ XML_NotationDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+ XML_StartNamespaceDeclHandler start,
+ XML_EndNamespaceDeclHandler end);
+
+void XMLPARSEAPI
+XML_SetNotStandaloneHandler(XML_Parser parser,
+ XML_NotStandaloneHandler handler);
+
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+ XML_ExternalEntityRefHandler handler);
+
+/* If a non-null value for arg is specified here, then it will be passed
+as the first argument to the external entity ref handler instead
+of the parser object. */
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg);
+
+void XMLPARSEAPI
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+ XML_UnknownEncodingHandler handler,
+ void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end element,
+processing instruction or character data. It causes the corresponding
+markup to be passed to the default handler. */
+void XMLPARSEAPI XML_DefaultCurrent(XML_Parser parser);
+
+/* This value is passed as the userData argument to callbacks. */
+void XMLPARSEAPI
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or null. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument
+to XML_CreateParser. It must not be called after XML_Parse
+or XML_ParseBuffer. */
+
+int XMLPARSEAPI
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed
+as the first argument to callbacks instead of userData.
+The userData will still be accessible using XML_GetUserData. */
+
+void XMLPARSEAPI
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* Sets the base to be used for resolving relative URIs in system identifiers in
+declarations. Resolving relative identifiers is left to the application:
+this value will be passed through as the base argument to the
+XML_ExternalEntityRefHandler, XML_NotationDeclHandler
+and XML_UnparsedEntityDeclHandler. The base argument will be copied.
+Returns zero if out of memory, non-zero otherwise. */
+
+int XMLPARSEAPI
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+const XML_Char XMLPARSEAPI *
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attributes passed in last call to the
+XML_StartElementHandler that were specified in the start-tag rather
+than defaulted. */
+
+int XMLPARSEAPI XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Parses some input. Returns 0 if a fatal error is detected.
+The last call to XML_Parse must have isFinal true;
+len may be zero for this call (or any other). */
+int XMLPARSEAPI
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+/* Creates an XML_Parser object that can parse an external general entity;
+context is a '\0'-terminated string specifying the parse context;
+encoding is a '\0'-terminated string giving the name of the externally specified encoding,
+or null if there is no externally specified encoding.
+The context string consists of a sequence of tokens separated by formfeeds (\f);
+a token consisting of a name specifies that the general entity of the name
+is open; a token of the form prefix=uri specifies the namespace for a particular
+prefix; a token of the form =uri specifies the default namespace.
+This can be called at any point after the first call to an ExternalEntityRefHandler
+so longer as the parser has not yet been freed.
+The new parser is completely independent and may safely be used in a separate thread.
+The handlers and userData are initialized from the parser argument.
+Returns 0 if out of memory. Otherwise returns a new XML_Parser object. */
+XML_Parser XMLPARSEAPI
+XML_ExternalEntityParserCreate(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *encoding);
+
+enum XML_Error {
+ XML_ERROR_NONE,
+ XML_ERROR_NO_MEMORY,
+ XML_ERROR_SYNTAX,
+ XML_ERROR_NO_ELEMENTS,
+ XML_ERROR_INVALID_TOKEN,
+ XML_ERROR_UNCLOSED_TOKEN,
+ XML_ERROR_PARTIAL_CHAR,
+ XML_ERROR_TAG_MISMATCH,
+ XML_ERROR_DUPLICATE_ATTRIBUTE,
+ XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+ XML_ERROR_PARAM_ENTITY_REF,
+ XML_ERROR_UNDEFINED_ENTITY,
+ XML_ERROR_RECURSIVE_ENTITY_REF,
+ XML_ERROR_ASYNC_ENTITY,
+ XML_ERROR_BAD_CHAR_REF,
+ XML_ERROR_BINARY_ENTITY_REF,
+ XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+ XML_ERROR_MISPLACED_XML_PI,
+ XML_ERROR_UNKNOWN_ENCODING,
+ XML_ERROR_INCORRECT_ENCODING,
+ XML_ERROR_UNCLOSED_CDATA_SECTION,
+ XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+ XML_ERROR_NOT_STANDALONE
+};
+
+/* If XML_Parse or XML_ParseBuffer have returned 0, then XML_GetErrorCode
+returns information about the error. */
+
+enum XML_Error XMLPARSEAPI XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse location.
+They may be called when XML_Parse or XML_ParseBuffer return 0;
+in this case the location is the location of the character at which
+the error was detected.
+They may also be called from any other callback called to report
+some parse event; in this the location is the location of the first
+of the sequence of characters that generated the event. */
+
+int XMLPARSEAPI XML_GetCurrentLineNumber(XML_Parser parser);
+int XMLPARSEAPI XML_GetCurrentColumnNumber(XML_Parser parser);
+long XMLPARSEAPI XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+Returns 0 if the event is in an internal entity. */
+
+int XMLPARSEAPI XML_GetCurrentByteCount(XML_Parser parser);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+
+/* Frees memory used by the parser. */
+void XMLPARSEAPI
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+const XML_LChar XMLPARSEAPI *XML_ErrorString(int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlParse_INCLUDED */
diff --git a/protocols/jabber/xmlrole.c b/protocols/jabber/xmlrole.c
new file mode 100644
index 00000000..320749e8
--- /dev/null
+++ b/protocols/jabber/xmlrole.c
@@ -0,0 +1,1104 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmlrole.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+#ifndef MIN_BYTES_PER_CHAR
+#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+typedef int PROLOG_HANDLER(struct prolog_state *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+
+static PROLOG_HANDLER
+prolog0, prolog1, prolog2,
+doctype0, doctype1, doctype2, doctype3, doctype4, doctype5,
+internalSubset,
+entity0, entity1, entity2, entity3, entity4, entity5, entity6,
+entity7, entity8, entity9,
+notation0, notation1, notation2, notation3, notation4,
+attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6,
+attlist7, attlist8, attlist9,
+element0, element1, element2, element3, element4, element5, element6,
+element7,
+declClose,
+error;
+
+static
+int syntaxError(PROLOG_STATE *);
+
+static
+int prolog0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ state->handler = prolog1;
+ return XML_ROLE_NONE;
+ case XML_TOK_XML_DECL:
+ state->handler = prolog1;
+ return XML_ROLE_XML_DECL;
+ case XML_TOK_PI:
+ state->handler = prolog1;
+ return XML_ROLE_NONE;
+ case XML_TOK_COMMENT:
+ state->handler = prolog1;
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "DOCTYPE"))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return syntaxError(state);
+}
+
+static
+int prolog1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ case XML_TOK_BOM:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (!XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "DOCTYPE"))
+ break;
+ state->handler = doctype0;
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return syntaxError(state);
+}
+
+static
+int prolog2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ return XML_ROLE_NONE;
+ case XML_TOK_INSTANCE_START:
+ state->handler = error;
+ return XML_ROLE_INSTANCE_START;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = doctype1;
+ return XML_ROLE_DOCTYPE_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+ state->handler = doctype3;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+ state->handler = doctype2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype3;
+ return XML_ROLE_DOCTYPE_PUBLIC_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = doctype4;
+ return XML_ROLE_DOCTYPE_SYSTEM_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_BRACKET:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return syntaxError(state);
+}
+
+static
+int doctype5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = prolog2;
+ return XML_ROLE_DOCTYPE_CLOSE;
+ }
+ return syntaxError(state);
+}
+
+static
+int internalSubset(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_OPEN:
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "ENTITY")) {
+ state->handler = entity0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "ATTLIST")) {
+ state->handler = attlist0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "ELEMENT")) {
+ state->handler = element0;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+ "NOTATION")) {
+ state->handler = notation0;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_PI:
+ case XML_TOK_COMMENT:
+ return XML_ROLE_NONE;
+ case XML_TOK_PARAM_ENTITY_REF:
+ return XML_ROLE_PARAM_ENTITY_REF;
+ case XML_TOK_CLOSE_BRACKET:
+ state->handler = doctype5;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_PERCENT:
+ state->handler = entity1;
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity2;
+ return XML_ROLE_GENERAL_ENTITY_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = entity7;
+ return XML_ROLE_PARAM_ENTITY_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+ state->handler = entity4;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+ state->handler = entity3;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity4;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return syntaxError(state);
+}
+
+
+static
+int entity4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity5;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "NDATA")) {
+ state->handler = entity6;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_NOTATION_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+ state->handler = entity9;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+ state->handler = entity8;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = entity9;
+ return XML_ROLE_ENTITY_PUBLIC_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int entity9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_ENTITY_SYSTEM_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int notation0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = notation1;
+ return XML_ROLE_NOTATION_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int notation1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+ state->handler = notation3;
+ return XML_ROLE_NONE;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+ state->handler = notation2;
+ return XML_ROLE_NONE;
+ }
+ break;
+ }
+ return syntaxError(state);
+}
+
+static
+int notation2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = notation4;
+ return XML_ROLE_NOTATION_PUBLIC_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int notation3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int notation4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = declClose;
+ return XML_ROLE_NOTATION_SYSTEM_ID;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist1;
+ return XML_ROLE_ATTLIST_ELEMENT_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist2;
+ return XML_ROLE_ATTRIBUTE_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ {
+ static const char *types[] = {
+ "CDATA",
+ "ID",
+ "IDREF",
+ "IDREFS",
+ "ENTITY",
+ "ENTITIES",
+ "NMTOKEN",
+ "NMTOKENS",
+ };
+ int i;
+ for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++)
+ if (XmlNameMatchesAscii(enc, ptr, types[i])) {
+ state->handler = attlist8;
+ return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "NOTATION")) {
+ state->handler = attlist5;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist3;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NMTOKEN:
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = attlist4;
+ return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist3;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = attlist6;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+
+static
+int attlist6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ state->handler = attlist7;
+ return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->handler = attlist8;
+ return XML_ROLE_NONE;
+ case XML_TOK_OR:
+ state->handler = attlist6;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+/* default value */
+static
+int attlist8(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ "IMPLIED")) {
+ state->handler = attlist1;
+ return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ "REQUIRED")) {
+ state->handler = attlist1;
+ return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+ }
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ "FIXED")) {
+ state->handler = attlist9;
+ return XML_ROLE_NONE;
+ }
+ break;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int attlist9(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_LITERAL:
+ state->handler = attlist1;
+ return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+ }
+ return syntaxError(state);
+}
+
+static
+int element0(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element1;
+ return XML_ROLE_ELEMENT_NAME;
+ }
+ return syntaxError(state);
+}
+
+static
+int element1(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ if (XmlNameMatchesAscii(enc, ptr, "EMPTY")) {
+ state->handler = declClose;
+ return XML_ROLE_CONTENT_EMPTY;
+ }
+ if (XmlNameMatchesAscii(enc, ptr, "ANY")) {
+ state->handler = declClose;
+ return XML_ROLE_CONTENT_ANY;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->handler = element2;
+ state->level = 1;
+ return XML_ROLE_GROUP_OPEN;
+ }
+ return syntaxError(state);
+}
+
+static
+int element2(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_POUND_NAME:
+ if (XmlNameMatchesAscii(enc,
+ ptr + MIN_BYTES_PER_CHAR(enc),
+ "PCDATA")) {
+ state->handler = element3;
+ return XML_ROLE_CONTENT_PCDATA;
+ }
+ break;
+ case XML_TOK_OPEN_PAREN:
+ state->level = 2;
+ state->handler = element6;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return syntaxError(state);
+}
+
+static
+int element3(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+static
+int element4(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element5;
+ return XML_ROLE_CONTENT_ELEMENT;
+ }
+ return syntaxError(state);
+}
+
+static
+int element5(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_OR:
+ state->handler = element4;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+static
+int element6(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_OPEN_PAREN:
+ state->level += 1;
+ return XML_ROLE_GROUP_OPEN;
+ case XML_TOK_NAME:
+ case XML_TOK_PREFIXED_NAME:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT;
+ case XML_TOK_NAME_QUESTION:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_OPT;
+ case XML_TOK_NAME_ASTERISK:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_REP;
+ case XML_TOK_NAME_PLUS:
+ state->handler = element7;
+ return XML_ROLE_CONTENT_ELEMENT_PLUS;
+ }
+ return syntaxError(state);
+}
+
+static
+int element7(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_CLOSE_PAREN:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE;
+ case XML_TOK_CLOSE_PAREN_ASTERISK:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_REP;
+ case XML_TOK_CLOSE_PAREN_QUESTION:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_OPT;
+ case XML_TOK_CLOSE_PAREN_PLUS:
+ state->level -= 1;
+ if (state->level == 0)
+ state->handler = declClose;
+ return XML_ROLE_GROUP_CLOSE_PLUS;
+ case XML_TOK_COMMA:
+ state->handler = element6;
+ return XML_ROLE_GROUP_SEQUENCE;
+ case XML_TOK_OR:
+ state->handler = element6;
+ return XML_ROLE_GROUP_CHOICE;
+ }
+ return syntaxError(state);
+}
+
+static
+int declClose(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_PROLOG_S:
+ return XML_ROLE_NONE;
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+
+#if 0
+
+static
+int ignore(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ switch (tok) {
+ case XML_TOK_DECL_CLOSE:
+ state->handler = internalSubset;
+ return 0;
+ default:
+ return XML_ROLE_NONE;
+ }
+ return syntaxError(state);
+}
+#endif
+
+static
+int error(PROLOG_STATE *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc)
+{
+ return XML_ROLE_NONE;
+}
+
+static
+int syntaxError(PROLOG_STATE *state)
+{
+ state->handler = error;
+ return XML_ROLE_ERROR;
+}
+
+void XmlPrologStateInit(PROLOG_STATE *state)
+{
+ state->handler = prolog0;
+}
diff --git a/protocols/jabber/xmlrole.h b/protocols/jabber/xmlrole.h
new file mode 100644
index 00000000..877c40ba
--- /dev/null
+++ b/protocols/jabber/xmlrole.h
@@ -0,0 +1,111 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ XML_ROLE_ERROR = -1,
+ XML_ROLE_NONE = 0,
+ XML_ROLE_XML_DECL,
+ XML_ROLE_INSTANCE_START,
+ XML_ROLE_DOCTYPE_NAME,
+ XML_ROLE_DOCTYPE_SYSTEM_ID,
+ XML_ROLE_DOCTYPE_PUBLIC_ID,
+ XML_ROLE_DOCTYPE_CLOSE,
+ XML_ROLE_GENERAL_ENTITY_NAME,
+ XML_ROLE_PARAM_ENTITY_NAME,
+ XML_ROLE_ENTITY_VALUE,
+ XML_ROLE_ENTITY_SYSTEM_ID,
+ XML_ROLE_ENTITY_PUBLIC_ID,
+ XML_ROLE_ENTITY_NOTATION_NAME,
+ XML_ROLE_NOTATION_NAME,
+ XML_ROLE_NOTATION_SYSTEM_ID,
+ XML_ROLE_NOTATION_NO_SYSTEM_ID,
+ XML_ROLE_NOTATION_PUBLIC_ID,
+ XML_ROLE_ATTRIBUTE_NAME,
+ XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+ XML_ROLE_ATTRIBUTE_TYPE_ID,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+ XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+ XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+ XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+ XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+ XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+ XML_ROLE_ATTLIST_ELEMENT_NAME,
+ XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+ XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+ XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+ XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+ XML_ROLE_ELEMENT_NAME,
+ XML_ROLE_CONTENT_ANY,
+ XML_ROLE_CONTENT_EMPTY,
+ XML_ROLE_CONTENT_PCDATA,
+ XML_ROLE_GROUP_OPEN,
+ XML_ROLE_GROUP_CLOSE,
+ XML_ROLE_GROUP_CLOSE_REP,
+ XML_ROLE_GROUP_CLOSE_OPT,
+ XML_ROLE_GROUP_CLOSE_PLUS,
+ XML_ROLE_GROUP_CHOICE,
+ XML_ROLE_GROUP_SEQUENCE,
+ XML_ROLE_CONTENT_ELEMENT,
+ XML_ROLE_CONTENT_ELEMENT_REP,
+ XML_ROLE_CONTENT_ELEMENT_OPT,
+ XML_ROLE_CONTENT_ELEMENT_PLUS,
+ XML_ROLE_PARAM_ENTITY_REF
+};
+
+typedef struct prolog_state {
+ int (*handler)(struct prolog_state *state,
+ int tok,
+ const char *ptr,
+ const char *end,
+ const ENCODING *enc);
+ unsigned level;
+} PROLOG_STATE;
+
+void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *);
+
+#define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
diff --git a/protocols/jabber/xmltok.c b/protocols/jabber/xmltok.c
new file mode 100644
index 00000000..8b7ae15e
--- /dev/null
+++ b/protocols/jabber/xmltok.c
@@ -0,0 +1,1518 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#define VTABLE1 \
+ { PREFIX(prologTok), PREFIX(contentTok), PREFIX(cdataSectionTok) }, \
+ { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
+ PREFIX(sameName), \
+ PREFIX(nameMatchesAscii), \
+ PREFIX(nameLength), \
+ PREFIX(skipS), \
+ PREFIX(getAtts), \
+ PREFIX(charRefNumber), \
+ PREFIX(predefinedEntityName), \
+ PREFIX(updatePosition), \
+ PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo) \
+ (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits
+between the bottom 5 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING2(pages, byte) \
+ (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \
+ + ((((byte)[0]) & 3) << 1) \
+ + ((((byte)[1]) >> 5) & 1)] \
+ & (1 << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits
+between the bottom 4, 6 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING3(pages, byte) \
+ (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \
+ + ((((byte)[1]) >> 2) & 0xF)] \
+ << 3) \
+ + ((((byte)[1]) & 3) << 1) \
+ + ((((byte)[2]) >> 5) & 1)] \
+ & (1 << (((byte)[2]) & 0x1F)))
+
+#define UTF8_GET_NAMING(pages, p, n) \
+ ((n) == 2 \
+ ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+ : ((n) == 3 \
+ ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+ : 0))
+
+#define UTF8_INVALID3(p) \
+ ((*p) == 0xED \
+ ? (((p)[1] & 0x20) != 0) \
+ : ((*p) == 0xEF \
+ ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \
+ : 0))
+
+#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0)
+
+static
+int isNever(const ENCODING *enc, const char *p)
+{
+ return 0;
+}
+
+static
+int utf8_isName2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static
+int utf8_isName3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static
+int utf8_isNmstrt2(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static
+int utf8_isNmstrt3(const ENCODING *enc, const char *p)
+{
+ return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+#define utf8_isInvalid2 isNever
+
+static
+int utf8_isInvalid3(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static
+int utf8_isInvalid4(const ENCODING *enc, const char *p)
+{
+ return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+ ENCODING enc;
+ unsigned char type[256];
+#ifdef XML_MIN_SIZE
+ int (*byteType)(const ENCODING *, const char *);
+ int (*isNameMin)(const ENCODING *, const char *);
+ int (*isNmstrtMin)(const ENCODING *, const char *);
+ int (*byteToAscii)(const ENCODING *, const char *);
+ int (*charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+ int (*isName2)(const ENCODING *, const char *);
+ int (*isName3)(const ENCODING *, const char *);
+ int (*isName4)(const ENCODING *, const char *);
+ int (*isNmstrt2)(const ENCODING *, const char *);
+ int (*isNmstrt3)(const ENCODING *, const char *);
+ int (*isNmstrt4)(const ENCODING *, const char *);
+ int (*isInvalid2)(const ENCODING *, const char *);
+ int (*isInvalid3)(const ENCODING *, const char *);
+ int (*isInvalid4)(const ENCODING *, const char *);
+};
+
+#ifdef XML_MIN_SIZE
+
+#define STANDARD_VTABLE(E) \
+ E ## byteType, \
+ E ## isNameMin, \
+ E ## isNmstrtMin, \
+ E ## byteToAscii, \
+ E ## charMatches,
+
+#else
+
+#define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E) \
+ E ## isName2, \
+ E ## isName3, \
+ E ## isName4, \
+ E ## isNmstrt2, \
+ E ## isNmstrt3, \
+ E ## isNmstrt4, \
+ E ## isInvalid2, \
+ E ## isInvalid3, \
+ E ## isInvalid4
+
+static int checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+
+#ifdef XML_MIN_SIZE
+#define sb_isNameMin isNever
+#define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p) \
+ (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static
+int sb_byteType(const ENCODING *enc, const char *p)
+{
+ return SB_BYTE_TYPE(enc, p);
+}
+#define BYTE_TYPE(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteType(enc, p))
+#else
+#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define BYTE_TO_ASCII(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteToAscii(enc, p))
+static
+int sb_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return *p;
+}
+#else
+#define BYTE_TO_ASCII(enc, p) (*p)
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isName ## n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p))
+#define IS_INVALID_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p))
+
+#ifdef XML_MIN_SIZE
+#define IS_NAME_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNameMin(enc, p))
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNmstrtMin(enc, p))
+#else
+#define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define CHAR_MATCHES(enc, p, c) \
+ (((const struct normal_encoding *)(enc))->charMatches(enc, p, c))
+static
+int sb_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return *p == c;
+}
+#else
+/* c is an ASCII character */
+#define CHAR_MATCHES(enc, p, c) (*(p) == c)
+#endif
+
+#define PREFIX(ident) normal_ ## ident
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */
+ UTF8_cval1 = 0x00,
+ UTF8_cval2 = 0xc0,
+ UTF8_cval3 = 0xe0,
+ UTF8_cval4 = 0xf0
+};
+
+static
+void utf8_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ char *to;
+ const char *from;
+ if (fromLim - *fromP > toLim - *toP) {
+ /* Avoid copying partial characters. */
+ for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--)
+ if (((unsigned char)fromLim[-1] & 0xc0) != 0x80)
+ break;
+ }
+ for (to = *toP, from = *fromP; from != fromLim; from++, to++)
+ *to = *from;
+ *fromP = from;
+ *toP = to;
+}
+
+static
+void utf8_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ unsigned short *to = *toP;
+ const char *from = *fromP;
+ while (from != fromLim && to != toLim) {
+ switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+ case BT_LEAD2:
+ *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f);
+ from += 2;
+ break;
+ case BT_LEAD3:
+ *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f);
+ from += 3;
+ break;
+ case BT_LEAD4:
+ {
+ unsigned long n;
+ if (to + 1 == toLim)
+ break;
+ n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+ n -= 0x10000;
+ to[0] = (unsigned short)((n >> 10) | 0xD800);
+ to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+ to += 2;
+ from += 4;
+ }
+ break;
+ default:
+ *to++ = *from++;
+ break;
+ }
+ }
+ *fromP = from;
+ *toP = to;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+ };
+#endif
+
+static const struct normal_encoding utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+ };
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#include "iasciitab.h"
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+ };
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding = {
+ { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+ },
+ STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+ };
+
+static
+void latin1_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ for (;;) {
+ unsigned char c;
+ if (*fromP == fromLim)
+ break;
+ c = (unsigned char)**fromP;
+ if (c & 0x80) {
+ if (toLim - *toP < 2)
+ break;
+ *(*toP)++ = ((c >> 6) | UTF8_cval2);
+ *(*toP)++ = ((c & 0x3f) | 0x80);
+ (*fromP)++;
+ }
+ else {
+ if (*toP == toLim)
+ break;
+ *(*toP)++ = *(*fromP)++;
+ }
+ }
+}
+
+static
+void latin1_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = (unsigned char)*(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+ };
+
+#endif
+
+static const struct normal_encoding latin1_encoding = {
+ { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(sb_)
+ };
+
+static
+void ascii_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim)
+ *(*toP)++ = *(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#include "asciitab.h"
+ /* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+ };
+
+#endif
+
+static const struct normal_encoding ascii_encoding = {
+ { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+ /* BT_NONXML == 0 */
+ },
+ STANDARD_VTABLE(sb_)
+ };
+
+static int unicode_byte_type(char hi, char lo)
+{
+ switch ((unsigned char)hi) {
+case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+ return BT_LEAD4;
+case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return BT_TRAIL;
+ case 0xFF:
+ switch ((unsigned char)lo) {
+ case 0xFF:
+ case 0xFE:
+ return BT_NONXML;
+ }
+ break;
+ }
+ return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E) \
+static \
+void E ## toUtf8(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ char **toP, const char *toLim) \
+{ \
+ const char *from; \
+ for (from = *fromP; from != fromLim; from += 2) { \
+ int plane; \
+ unsigned char lo2; \
+ unsigned char lo = GET_LO(from); \
+ unsigned char hi = GET_HI(from); \
+ switch (hi) { \
+ case 0: \
+ if (lo < 0x80) { \
+ if (*toP == toLim) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = lo; \
+ break; \
+ } \
+ /* fall through */ \
+ case 0x1: case 0x2: case 0x3: \
+ case 0x4: case 0x5: case 0x6: case 0x7: \
+ if (toLim - *toP < 2) { \
+ *fromP = from; \
+ return; \
+ } \
+ *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ default: \
+ if (toLim - *toP < 3) { \
+ *fromP = from; \
+ return; \
+ } \
+ /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \
+ *(*toP)++ = ((hi >> 4) | UTF8_cval3); \
+ *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \
+ *(*toP)++ = ((lo & 0x3f) | 0x80); \
+ break; \
+ case 0xD8: case 0xD9: case 0xDA: case 0xDB: \
+ if (toLim - *toP < 4) { \
+ *fromP = from; \
+ return; \
+ } \
+ plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \
+ *(*toP)++ = ((plane >> 2) | UTF8_cval4); \
+ *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \
+ from += 2; \
+ lo2 = GET_LO(from); \
+ *(*toP)++ = (((lo & 0x3) << 4) \
+ | ((GET_HI(from) & 0x3) << 2) \
+ | (lo2 >> 6) \
+ | 0x80); \
+ *(*toP)++ = ((lo2 & 0x3f) | 0x80); \
+ break; \
+ } \
+ } \
+ *fromP = from; \
+}
+
+#define DEFINE_UTF16_TO_UTF16(E) \
+static \
+void E ## toUtf16(const ENCODING *enc, \
+ const char **fromP, const char *fromLim, \
+ unsigned short **toP, const unsigned short *toLim) \
+{ \
+ /* Avoid copying first half only of surrogate */ \
+ if (fromLim - *fromP > ((toLim - *toP) << 1) \
+ && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \
+ fromLim -= 2; \
+ for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \
+ *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \
+}
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) \
+ (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p) \
+ ((p)[1] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \
+ : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c)
+#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static
+int little2_byteType(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static
+int little2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int little2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return LITTLE2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int little2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int little2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) little2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+ 1
+#else
+0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+ };
+
+#endif
+
+static const struct normal_encoding little2_encoding = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+ };
+
+#if XML_BYTE_ORDER != 21
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+ };
+
+#endif
+
+static const struct normal_encoding internal_little2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(little2_)
+ };
+
+#endif
+
+
+#define BIG2_BYTE_TYPE(enc, p) \
+ ((p)[0] == 0 \
+ ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \
+ : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c)
+#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static
+int big2_byteType(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TYPE(enc, p);
+}
+
+static
+int big2_byteToAscii(const ENCODING *enc, const char *p)
+{
+ return BIG2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int big2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+ return BIG2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int big2_isNameMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int big2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+ return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) big2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p)
+#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+ 1
+#else
+0
+#endif
+ },
+ {
+#include "asciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+ };
+
+#endif
+
+static const struct normal_encoding big2_encoding = {
+ { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+ 1
+#else
+ 0
+#endif
+ },
+ {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+ };
+
+#if XML_BYTE_ORDER != 12
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns = {
+ { VTABLE, 2, 0, 1 },
+ {
+#include "iasciitab.h"
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+ };
+
+#endif
+
+static const struct normal_encoding internal_big2_encoding = {
+ { VTABLE, 2, 0, 1 },
+ {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+ },
+ STANDARD_VTABLE(big2_)
+ };
+
+#endif
+
+#undef PREFIX
+
+static
+int streqci(const char *s1, const char *s2)
+{
+ for (;;) {
+ char c1 = *s1++;
+ char c2 = *s2++;
+ if ('a' <= c1 && c1 <= 'z')
+ c1 += 'A' - 'a';
+ if ('a' <= c2 && c2 <= 'z')
+ c2 += 'A' - 'a';
+ if (c1 != c2)
+ return 0;
+ if (!c1)
+ break;
+ }
+ return 1;
+}
+
+static
+void initUpdatePosition(const ENCODING *enc, const char *ptr,
+ const char *end, POSITION *pos)
+{
+ normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static
+int toAscii(const ENCODING *enc, const char *ptr, const char *end)
+{
+ char buf[1];
+ char *p = buf;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+ if (p == buf)
+ return -1;
+ else
+ return buf[0];
+}
+
+static
+int isSpace(int c)
+{
+ switch (c) {
+ case 0x20:
+ case 0xD:
+ case 0xA:
+ case 0x9:
+ return 1;
+ }
+ return 0;
+}
+
+/* Return 1 if there's just optional white space
+or there's an S followed by name=val. */
+static
+int parsePseudoAttribute(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **namePtr,
+ const char **valPtr,
+ const char **nextTokPtr)
+{
+ int c;
+ char open;
+ if (ptr == end) {
+ *namePtr = 0;
+ return 1;
+ }
+ if (!isSpace(toAscii(enc, ptr, end))) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(toAscii(enc, ptr, end)));
+ if (ptr == end) {
+ *namePtr = 0;
+ return 1;
+ }
+ *namePtr = ptr;
+ for (;;) {
+ c = toAscii(enc, ptr, end);
+ if (c == -1) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ if (c == '=')
+ break;
+ if (isSpace(c)) {
+ do {
+ ptr += enc->minBytesPerChar;
+ } while (isSpace(c = toAscii(enc, ptr, end)));
+ if (c != '=') {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ ptr += enc->minBytesPerChar;
+ }
+ if (ptr == *namePtr) {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ while (isSpace(c)) {
+ ptr += enc->minBytesPerChar;
+ c = toAscii(enc, ptr, end);
+ }
+ if (c != '"' && c != '\'') {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ open = c;
+ ptr += enc->minBytesPerChar;
+ *valPtr = ptr;
+ for (;; ptr += enc->minBytesPerChar) {
+ c = toAscii(enc, ptr, end);
+ if (c == open)
+ break;
+ if (!('a' <= c && c <= 'z')
+ && !('A' <= c && c <= 'Z')
+ && !('0' <= c && c <= '9')
+ && c != '.'
+ && c != '-'
+ && c != '_') {
+ *nextTokPtr = ptr;
+ return 0;
+ }
+ }
+ *nextTokPtr = ptr + enc->minBytesPerChar;
+ return 1;
+}
+
+static
+int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *,
+ const char *,
+ const char *),
+ int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ const char *val = 0;
+ const char *name = 0;
+ ptr += 5 * enc->minBytesPerChar;
+ end -= 2 * enc->minBytesPerChar;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr) || !name) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!XmlNameMatchesAscii(enc, name, "version")) {
+ if (!isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ }
+ else {
+ if (versionPtr)
+ *versionPtr = val;
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name) {
+ if (isGeneralTextEntity) {
+ /* a TextDecl must have an EncodingDecl */
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+ }
+ }
+ if (XmlNameMatchesAscii(enc, name, "encoding")) {
+ int c = toAscii(enc, val, end);
+ if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
+ *badPtr = val;
+ return 0;
+ }
+ if (encodingName)
+ *encodingName = val;
+ if (encoding)
+ *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+ if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) {
+ *badPtr = ptr;
+ return 0;
+ }
+ if (!name)
+ return 1;
+ }
+ if (!XmlNameMatchesAscii(enc, name, "standalone") || isGeneralTextEntity) {
+ *badPtr = name;
+ return 0;
+ }
+ if (XmlNameMatchesAscii(enc, val, "yes")) {
+ if (standalone)
+ *standalone = 1;
+ }
+ else if (XmlNameMatchesAscii(enc, val, "no")) {
+ if (standalone)
+ *standalone = 0;
+ }
+ else {
+ *badPtr = val;
+ return 0;
+ }
+ while (isSpace(toAscii(enc, ptr, end)))
+ ptr += enc->minBytesPerChar;
+ if (ptr != end) {
+ *badPtr = ptr;
+ return 0;
+ }
+ return 1;
+}
+
+static
+int checkCharRefNumber(int result)
+{
+ switch (result >> 8) {
+case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+ return -1;
+ case 0:
+ if (latin1_encoding.type[result] == BT_NONXML)
+ return -1;
+ break;
+ case 0xFF:
+ if (result == 0xFFFE || result == 0xFFFF)
+ return -1;
+ break;
+ }
+ return result;
+}
+
+int XmlUtf8Encode(int c, char *buf)
+{
+ enum {
+ /* minN is minimum legal resulting value for N byte sequence */
+ min2 = 0x80,
+ min3 = 0x800,
+ min4 = 0x10000
+ };
+
+ if (c < 0)
+ return 0;
+ if (c < min2) {
+ buf[0] = (c | UTF8_cval1);
+ return 1;
+ }
+ if (c < min3) {
+ buf[0] = ((c >> 6) | UTF8_cval2);
+ buf[1] = ((c & 0x3f) | 0x80);
+ return 2;
+ }
+ if (c < min4) {
+ buf[0] = ((c >> 12) | UTF8_cval3);
+ buf[1] = (((c >> 6) & 0x3f) | 0x80);
+ buf[2] = ((c & 0x3f) | 0x80);
+ return 3;
+ }
+ if (c < 0x110000) {
+ buf[0] = ((c >> 18) | UTF8_cval4);
+ buf[1] = (((c >> 12) & 0x3f) | 0x80);
+ buf[2] = (((c >> 6) & 0x3f) | 0x80);
+ buf[3] = ((c & 0x3f) | 0x80);
+ return 4;
+ }
+ return 0;
+}
+
+int XmlUtf16Encode(int charNum, unsigned short *buf)
+{
+ if (charNum < 0)
+ return 0;
+ if (charNum < 0x10000) {
+ buf[0] = charNum;
+ return 1;
+ }
+ if (charNum < 0x110000) {
+ charNum -= 0x10000;
+ buf[0] = (charNum >> 10) + 0xD800;
+ buf[1] = (charNum & 0x3FF) + 0xDC00;
+ return 2;
+ }
+ return 0;
+}
+
+struct unknown_encoding {
+ struct normal_encoding normal;
+ int (*convert)(void *userData, const char *p);
+ void *userData;
+ unsigned short utf16[256];
+ char utf8[256][4];
+};
+
+int XmlSizeOfUnknownEncoding()
+{
+ return sizeof(struct unknown_encoding);
+}
+
+static
+int unknown_isName(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isNmstrt(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ if (c & ~0xFFFF)
+ return 0;
+ return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isInvalid(const ENCODING *enc, const char *p)
+{
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, p);
+ return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static
+void unknown_toUtf8(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ char **toP, const char *toLim)
+{
+ char buf[XML_UTF8_ENCODE_MAX];
+ for (;;) {
+ const char *utf8;
+ int n;
+ if (*fromP == fromLim)
+ break;
+ utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP];
+ n = *utf8++;
+ if (n == 0) {
+ int c = ((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+ n = XmlUtf8Encode(c, buf);
+ if (n > toLim - *toP)
+ break;
+ utf8 = buf;
+ *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2);
+ }
+ else {
+ if (n > toLim - *toP)
+ break;
+ (*fromP)++;
+ }
+ do {
+ *(*toP)++ = *utf8++;
+ } while (--n != 0);
+ }
+}
+
+static
+void unknown_toUtf16(const ENCODING *enc,
+ const char **fromP, const char *fromLim,
+ unsigned short **toP, const unsigned short *toLim)
+{
+ while (*fromP != fromLim && *toP != toLim) {
+ unsigned short c
+ = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP];
+ if (c == 0) {
+ c = (unsigned short)((const struct unknown_encoding *)enc)
+ ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+ *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+ - (BT_LEAD2 - 2);
+ }
+ else
+ (*fromP)++;
+ *(*toP)++ = c;
+ }
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ int (*convert)(void *userData, const char *p),
+ void *userData)
+{
+ int i;
+ struct unknown_encoding *e = mem;
+ for (i = 0; i < sizeof(struct normal_encoding); i++)
+ ((char *)mem)[i] = ((char *)&latin1_encoding)[i];
+ for (i = 0; i < 128; i++)
+ if (latin1_encoding.type[i] != BT_OTHER
+ && latin1_encoding.type[i] != BT_NONXML
+ && table[i] != i)
+ return 0;
+ for (i = 0; i < 256; i++) {
+ int c = table[i];
+ if (c == -1) {
+ e->normal.type[i] = BT_MALFORM;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else if (c < 0) {
+ if (c < -4)
+ return 0;
+ e->normal.type[i] = BT_LEAD2 - (c + 2);
+ e->utf8[i][0] = 0;
+ e->utf16[i] = 0;
+ }
+ else if (c < 0x80) {
+ if (latin1_encoding.type[c] != BT_OTHER
+ && latin1_encoding.type[c] != BT_NONXML
+ && c != i)
+ return 0;
+ e->normal.type[i] = latin1_encoding.type[c];
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = (char)c;
+ e->utf16[i] = c == 0 ? 0xFFFF : c;
+ }
+ else if (checkCharRefNumber(c) < 0) {
+ e->normal.type[i] = BT_NONXML;
+ /* This shouldn't really get used. */
+ e->utf16[i] = 0xFFFF;
+ e->utf8[i][0] = 1;
+ e->utf8[i][1] = 0;
+ }
+ else {
+ if (c > 0xFFFF)
+ return 0;
+ if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NMSTRT;
+ else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+ e->normal.type[i] = BT_NAME;
+ else
+ e->normal.type[i] = BT_OTHER;
+ e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+ e->utf16[i] = c;
+ }
+ }
+ e->userData = userData;
+ e->convert = convert;
+ if (convert) {
+ e->normal.isName2 = unknown_isName;
+ e->normal.isName3 = unknown_isName;
+ e->normal.isName4 = unknown_isName;
+ e->normal.isNmstrt2 = unknown_isNmstrt;
+ e->normal.isNmstrt3 = unknown_isNmstrt;
+ e->normal.isNmstrt4 = unknown_isNmstrt;
+ e->normal.isInvalid2 = unknown_isInvalid;
+ e->normal.isInvalid3 = unknown_isInvalid;
+ e->normal.isInvalid4 = unknown_isInvalid;
+ }
+ e->normal.enc.utf8Convert = unknown_toUtf8;
+ e->normal.enc.utf16Convert = unknown_toUtf16;
+ return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+ UNKNOWN_ENC = -1,
+ ISO_8859_1_ENC = 0,
+ US_ASCII_ENC,
+ UTF_8_ENC,
+ UTF_16_ENC,
+ UTF_16BE_ENC,
+ UTF_16LE_ENC,
+ /* must match encodingNames up to here */
+ NO_ENC
+};
+
+static
+int getEncodingIndex(const char *name)
+{
+ static const char *encodingNames[] = {
+ "ISO-8859-1",
+ "US-ASCII",
+ "UTF-8",
+ "UTF-16",
+ "UTF-16BE"
+ "UTF-16LE",
+ };
+ int i;
+ if (name == 0)
+ return NO_ENC;
+ for (i = 0; i < sizeof(encodingNames)/sizeof(encodingNames[0]); i++)
+ if (streqci(name, encodingNames[i]))
+ return i;
+ return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding specified
+at initialization in the isUtf16 member. */
+
+#define INIT_ENC_INDEX(enc) ((enc)->initEnc.isUtf16)
+
+/* This is what detects the encoding.
+encodingTable maps from encoding indices to encodings;
+INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding;
+state is XML_CONTENT_STATE if we're parsing an external text entity,
+and XML_PROLOG_STATE otherwise.
+*/
+
+
+static
+int initScan(const ENCODING **encodingTable,
+ const INIT_ENCODING *enc,
+ int state,
+ const char *ptr,
+ const char *end,
+ const char **nextTokPtr)
+{
+ const ENCODING **encPtr;
+
+ if (ptr == end)
+ return XML_TOK_NONE;
+ encPtr = enc->encPtr;
+ if (ptr + 1 == end) {
+ /* only a single byte available for auto-detection */
+ /* a well-formed document entity must have more than one byte */
+ if (state != XML_CONTENT_STATE)
+ return XML_TOK_PARTIAL;
+ /* so we're parsing an external text entity... */
+ /* if UTF-16 was externally specified, then we need at least 2 bytes */
+ switch (INIT_ENC_INDEX(enc)) {
+ case UTF_16_ENC:
+ case UTF_16LE_ENC:
+ case UTF_16BE_ENC:
+ return XML_TOK_PARTIAL;
+ }
+ switch ((unsigned char)*ptr) {
+ case 0xFE:
+ case 0xFF:
+ case 0xEF: /* possibly first byte of UTF-8 BOM */
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ /* fall through */
+ case 0x00:
+ case 0x3C:
+ return XML_TOK_PARTIAL;
+ }
+ }
+ else {
+ switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+ case 0xFEFF:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XML_TOK_BOM;
+ /* 00 3C is handled in the default case */
+ case 0x3C00:
+ if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+ || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+ && state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ case 0xFFFE:
+ if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+ && state == XML_CONTENT_STATE)
+ break;
+ *nextTokPtr = ptr + 2;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XML_TOK_BOM;
+ case 0xEFBB:
+ /* Maybe a UTF-8 BOM (EF BB BF) */
+ /* If there's an explicitly specified (external) encoding
+ of ISO-8859-1 or some flavour of UTF-16
+ and this is an external text entity,
+ don't look for the BOM,
+ because it might be a legal data. */
+ if (state == XML_CONTENT_STATE) {
+ int e = INIT_ENC_INDEX(enc);
+ if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC || e == UTF_16_ENC)
+ break;
+ }
+ if (ptr + 2 == end)
+ return XML_TOK_PARTIAL;
+ if ((unsigned char)ptr[2] == 0xBF) {
+ *encPtr = encodingTable[UTF_8_ENC];
+ return XML_TOK_BOM;
+ }
+ break;
+ default:
+ if (ptr[0] == '\0') {
+ /* 0 isn't a legal data character. Furthermore a document entity can only
+ start with ASCII characters. So the only way this can fail to be big-endian
+ UTF-16 if it it's an external parsed general entity that's labelled as
+ UTF-16LE. */
+ if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+ break;
+ *encPtr = encodingTable[UTF_16BE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ else if (ptr[1] == '\0') {
+ /* We could recover here in the case:
+ - parsing an external entity
+ - second byte is 0
+ - no externally specified encoding
+ - no encoding declaration
+ by assuming UTF-16LE. But we don't, because this would mean when
+ presented just with a single byte, we couldn't reliably determine
+ whether we needed further bytes. */
+ if (state == XML_CONTENT_STATE)
+ break;
+ *encPtr = encodingTable[UTF_16LE_ENC];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+ }
+ break;
+ }
+ }
+ *encPtr = encodingTable[(int)INIT_ENC_INDEX(enc)];
+ return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+
+#define NS(x) x
+#define ns(x) x
+#include "xmltok_ns.c"
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#define NS(x) x ## NS
+#define ns(x) x ## _ns
+
+#include "xmltok_ns.c"
+
+#undef NS
+#undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ int (*convert)(void *userData, const char *p),
+ void *userData)
+{
+ ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+ if (enc)
+ ((struct normal_encoding *)enc)->type[':'] = BT_COLON;
+ return enc;
+}
+
+#endif /* XML_NS */
diff --git a/protocols/jabber/xmltok.h b/protocols/jabber/xmltok.h
new file mode 100644
index 00000000..06544d15
--- /dev/null
+++ b/protocols/jabber/xmltok.h
@@ -0,0 +1,307 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLTOKAPI
+#define XMLTOKAPI /* as nothing */
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of
+ illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_NONE -4 /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan;
+ might be part of CRLF sequence */
+#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1 /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+ returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */
+
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10 /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_PI 11 /* processing instruction */
+#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14 /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16 /* <!foo */
+#define XML_TOK_DECL_CLOSE 17 /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20 /* #name */
+#define XML_TOK_OR 21 /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30 /* name? */
+#define XML_TOK_NAME_ASTERISK 31 /* name* */
+#define XML_TOK_NAME_PLUS 32 /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok
+ for a name with a colon. */
+#define XML_TOK_PREFIXED_NAME 41
+
+#define XML_N_STATES 3
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+ /* first line and first column are 0 not 1 */
+ unsigned long lineNumber;
+ unsigned long columnNumber;
+} POSITION;
+
+typedef struct {
+ const char *name;
+ const char *valuePtr;
+ const char *valueEnd;
+ char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+struct encoding {
+ int (*scanners[XML_N_STATES])(const ENCODING *,
+ const char *,
+ const char *,
+ const char **);
+ int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *,
+ const char *,
+ const char *,
+ const char **);
+ int (*sameName)(const ENCODING *,
+ const char *, const char *);
+ int (*nameMatchesAscii)(const ENCODING *,
+ const char *, const char *);
+ int (*nameLength)(const ENCODING *, const char *);
+ const char *(*skipS)(const ENCODING *, const char *);
+ int (*getAtts)(const ENCODING *enc, const char *ptr,
+ int attsMax, ATTRIBUTE *atts);
+ int (*charRefNumber)(const ENCODING *enc, const char *ptr);
+ int (*predefinedEntityName)(const ENCODING *, const char *, const char *);
+ void (*updatePosition)(const ENCODING *,
+ const char *ptr,
+ const char *end,
+ POSITION *);
+ int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **badPtr);
+ void (*utf8Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ char **toP,
+ const char *toLim);
+ void (*utf16Convert)(const ENCODING *enc,
+ const char **fromP,
+ const char *fromLim,
+ unsigned short **toP,
+ const unsigned short *toLim);
+ int minBytesPerChar;
+ char isUtf8;
+ char isUtf16;
+};
+
+/*
+Scan the string starting at ptr until the end of the next complete token,
+but do not scan past eptr. Return an integer giving the type of token.
+
+Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+Return XML_TOK_PARTIAL when the string does not contain a complete token;
+nextTokPtr will not be set.
+
+Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr
+will be set to point to the character which made the token invalid.
+
+Otherwise the string starts with a valid token; nextTokPtr will be set to point
+to the character following the end of that token.
+
+Each data character counts as a single token, but adjacent data characters
+may be returned together. Similarly for characters in the prolog outside
+literals, comments and processing instructions.
+*/
+
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr) \
+ (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+/* This is used for performing a 2nd-level tokenization on
+the content of a literal that has already been returned by XmlTok. */
+
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+ (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
+
+#define XmlNameMatchesAscii(enc, ptr1, ptr2) \
+ (((enc)->nameMatchesAscii)(enc, ptr1, ptr2))
+
+#define XmlNameLength(enc, ptr) \
+ (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) \
+ (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts) \
+ (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) \
+ (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end) \
+ (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos) \
+ (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr) \
+ (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+ ENCODING initEnc;
+ const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+
+int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding();
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding();
+int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf);
+int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf);
+
+int XMLTOKAPI XmlSizeOfUnknownEncoding();
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncoding(void *mem,
+ int *table,
+ int (*conv)(void *userData, const char *p),
+ void *userData);
+
+int XMLTOKAPI XmlParseXmlDeclNS(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingNamePtr,
+ const ENCODING **namedEncodingPtr,
+ int *standalonePtr);
+int XMLTOKAPI XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncodingNS();
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncodingNS();
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncodingNS(void *mem,
+ int *table,
+ int (*conv)(void *userData, const char *p),
+ void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
diff --git a/protocols/jabber/xmltok_impl.c b/protocols/jabber/xmltok_impl.c
new file mode 100644
index 00000000..de11c2a8
--- /dev/null
+++ b/protocols/jabber/xmltok_impl.c
@@ -0,0 +1,1737 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#ifndef IS_INVALID_CHAR
+#define IS_INVALID_CHAR(enc, ptr, n) (0)
+#endif
+
+#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_INVALID_CHAR(enc, ptr, n)) { \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define INVALID_CASES(ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(2, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(3, ptr, nextTokPtr) \
+ INVALID_LEAD_CASE(4, ptr, nextTokPtr) \
+ case BT_NONXML: \
+ case BT_MALFORM: \
+ case BT_TRAIL: \
+ *(nextTokPtr) = (ptr); \
+ return XML_TOK_INVALID;
+
+#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NAME_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ case BT_DIGIT: \
+ case BT_NAME: \
+ case BT_MINUS: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ ptr += n; \
+ break;
+
+#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \
+ case BT_NONASCII: \
+ if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID; \
+ } \
+ case BT_NMSTRT: \
+ case BT_HEX: \
+ ptr += MINBPC(enc); \
+ break; \
+ CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \
+ CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#ifndef PREFIX
+#define PREFIX(ident) ident
+#endif
+
+/* ptr points to character following "<!-" */
+
+static
+int PREFIX(scanComment)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (!CHAR_MATCHES(enc, ptr, '-')) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_MINUS:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, '-')) {
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, '>')) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMENT;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static
+int PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COND_SECT_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_PERCNT:
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ /* don't allow <!ENTITY% foo "whatever"> */
+ switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* fall through */
+case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DECL_OPEN;
+ case BT_NMSTRT:
+ case BT_HEX:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr)
+{
+ int upper = 0;
+ *tokPtr = XML_TOK_PI;
+ if (end - ptr != MINBPC(enc)*3)
+ return 1;
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 'x':
+ break;
+ case 'X':
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 'm':
+ break;
+ case 'M':
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ ptr += MINBPC(enc);
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 'l':
+ break;
+ case 'L':
+ upper = 1;
+ break;
+ default:
+ return 1;
+ }
+ if (upper)
+ return 0;
+ *tokPtr = XML_TOK_XML_DECL;
+ return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static
+int PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int tok;
+ const char *target = ptr;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_CR: case BT_LF:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUEST:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, '>')) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+ case BT_QUEST:
+ if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, '>')) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return tok;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+
+static
+int PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int i;
+ /* CDATA[ */
+ if (end - ptr < 6 * MINBPC(enc))
+ return XML_TOK_PARTIAL;
+ for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+ if (!CHAR_MATCHES(enc, ptr, "CDATA["[i])) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static
+int PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, ']'))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, '>')) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CDATA_SECT_CLOSE;
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ case BT_RSQB:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static
+int PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_CR: case BT_LF:
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ break;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+#ifdef XML_NS
+ case BT_COLON:
+ /* no need to check qname syntax here, since end-tag must match exactly */
+ ptr += MINBPC(enc);
+ break;
+#endif
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_END_TAG;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static
+int PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static
+int PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr != end) {
+ if (CHAR_MATCHES(enc, ptr, 'x'))
+ return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ break;
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CHAR_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static
+int PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_NUM:
+ return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static
+int PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon = 0;
+#endif
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+case BT_S: case BT_CR: case BT_LF:
+ for (;;) {
+ int t;
+
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == BT_EQUALS)
+ break;
+ switch (t) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_EQUALS:
+ {
+ int open;
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ for (;;) {
+
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ open = BYTE_TYPE(enc, ptr);
+ if (open == BT_QUOT || open == BT_APOS)
+ break;
+ switch (open) {
+ case BT_S:
+ case BT_LF:
+ case BT_CR:
+ break;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ ptr += MINBPC(enc);
+ /* in attribute value */
+ for (;;) {
+ int t;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ t = BYTE_TYPE(enc, ptr);
+ if (t == open)
+ break;
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_AMP:
+ {
+ int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+ if (tok <= 0) {
+ if (tok == XML_TOK_INVALID)
+ *nextTokPtr = ptr;
+ return tok;
+ }
+ break;
+ }
+ case BT_LT:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S:
+ case BT_CR:
+ case BT_LF:
+ break;
+ case BT_SOL:
+ goto sol;
+ case BT_GT:
+ goto gt;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ /* ptr points to closing quote */
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_S: case BT_CR: case BT_LF:
+ continue;
+ case BT_GT:
+gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_WITH_ATTS;
+ case BT_SOL:
+sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, '>')) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static
+int PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+#ifdef XML_NS
+ int hadColon;
+#endif
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_EXCL:
+ if ((ptr += MINBPC(enc)) == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_MINUS:
+ return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LSQB:
+ return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_SOL:
+ return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+#ifdef XML_NS
+ hadColon = 0;
+#endif
+ /* we have a start-tag */
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+ case BT_COLON:
+ if (hadColon) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ hadColon = 1;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ break;
+#endif
+case BT_S: case BT_CR: case BT_LF:
+ {
+ ptr += MINBPC(enc);
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ case BT_GT:
+ goto gt;
+ case BT_SOL:
+ goto sol;
+ case BT_S: case BT_CR: case BT_LF:
+ ptr += MINBPC(enc);
+ continue;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+ }
+ return XML_TOK_PARTIAL;
+ }
+ case BT_GT:
+gt:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_START_TAG_NO_ATTS;
+ case BT_SOL:
+sol:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (!CHAR_MATCHES(enc, ptr, '>')) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LT:
+ return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_AMP:
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_CR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ case BT_LF:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, ']'))
+ break;
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_RSQB;
+ if (!CHAR_MATCHES(enc, ptr, '>')) {
+ ptr -= MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ INVALID_CASES(ptr, nextTokPtr)
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+ *nextTokPtr = ptr; \
+ return XML_TOK_DATA_CHARS; \
+ } \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_RSQB:
+ if (ptr + MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ']')) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ if (ptr + 2*MINBPC(enc) != end) {
+ if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), '>')) {
+ ptr += MINBPC(enc);
+ break;
+ }
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_INVALID;
+ }
+ }
+ /* fall through */
+ case BT_AMP:
+ case BT_LT:
+ case BT_NONXML:
+ case BT_MALFORM:
+ case BT_TRAIL:
+ case BT_CR:
+ case BT_LF:
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static
+int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
+ *nextTokPtr = ptr;
+ return XML_TOK_PERCENT;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ case BT_SEMI:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_PARAM_ENTITY_REF;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_CR: case BT_LF: case BT_S:
+case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_POUND_NAME;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(scanLit)(int open, const ENCODING *enc,
+ const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ while (ptr != end) {
+ int t = BYTE_TYPE(enc, ptr);
+ switch (t) {
+ INVALID_CASES(ptr, nextTokPtr)
+ case BT_QUOT:
+ case BT_APOS:
+ ptr += MINBPC(enc);
+ if (t != open)
+ break;
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ *nextTokPtr = ptr;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_CR: case BT_LF:
+ case BT_GT: case BT_PERCNT: case BT_LSQB:
+ return XML_TOK_LITERAL;
+ default:
+ return XML_TOK_INVALID;
+ }
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ int tok;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ if (MINBPC(enc) > 1) {
+ size_t n = end - ptr;
+ if (n & (MINBPC(enc) - 1)) {
+ n &= ~(MINBPC(enc) - 1);
+ if (n == 0)
+ return XML_TOK_PARTIAL;
+ end = ptr + n;
+ }
+ }
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_QUOT:
+ return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_APOS:
+ return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_LT:
+ {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_EXCL:
+ return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_QUEST:
+ return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_NMSTRT:
+ case BT_HEX:
+ case BT_NONASCII:
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ *nextTokPtr = ptr - MINBPC(enc);
+ return XML_TOK_INSTANCE_START;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ case BT_CR:
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_TRAILING_CR;
+ /* fall through */
+case BT_S: case BT_LF:
+ for (;;) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ break;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_S: case BT_LF:
+ break;
+ case BT_CR:
+ /* don't split CR/LF pair */
+ if (ptr + MINBPC(enc) != end)
+ break;
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_PROLOG_S;
+ case BT_PERCNT:
+ return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ case BT_COMMA:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_COMMA;
+ case BT_LSQB:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_BRACKET;
+ case BT_RSQB:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr, ']')) {
+ if (ptr + MINBPC(enc) == end)
+ return XML_TOK_PARTIAL;
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), '>')) {
+ *nextTokPtr = ptr + 2*MINBPC(enc);
+ return XML_TOK_COND_SECT_CLOSE;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_BRACKET;
+ case BT_LPAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OPEN_PAREN;
+ case BT_RPAR:
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_AST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_ASTERISK;
+ case BT_QUEST:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_QUESTION;
+ case BT_PLUS:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_CLOSE_PAREN_PLUS;
+case BT_CR: case BT_LF: case BT_S:
+case BT_GT: case BT_COMMA: case BT_VERBAR:
+ case BT_RPAR:
+ *nextTokPtr = ptr;
+ return XML_TOK_CLOSE_PAREN;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_VERBAR:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_OR;
+ case BT_GT:
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DECL_CLOSE;
+ case BT_NUM:
+ return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (end - ptr < n) \
+ return XML_TOK_PARTIAL_CHAR; \
+ if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NAME; \
+ break; \
+ } \
+ if (IS_NAME_CHAR(enc, ptr, n)) { \
+ ptr += n; \
+ tok = XML_TOK_NMTOKEN; \
+ break; \
+ } \
+ *nextTokPtr = ptr; \
+ return XML_TOK_INVALID;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NMSTRT:
+ case BT_HEX:
+ tok = XML_TOK_NAME;
+ ptr += MINBPC(enc);
+ break;
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ tok = XML_TOK_NMTOKEN;
+ ptr += MINBPC(enc);
+ break;
+ case BT_NONASCII:
+ if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NAME;
+ break;
+ }
+ if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+ ptr += MINBPC(enc);
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ /* fall through */
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_GT: case BT_RPAR: case BT_COMMA:
+case BT_VERBAR: case BT_LSQB: case BT_PERCNT:
+case BT_S: case BT_CR: case BT_LF:
+ *nextTokPtr = ptr;
+ return tok;
+#ifdef XML_NS
+ case BT_COLON:
+ ptr += MINBPC(enc);
+ switch (tok) {
+ case XML_TOK_NAME:
+ if (ptr == end)
+ return XML_TOK_PARTIAL;
+ tok = XML_TOK_PREFIXED_NAME;
+ switch (BYTE_TYPE(enc, ptr)) {
+ CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+ default:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+ case XML_TOK_PREFIXED_NAME:
+ tok = XML_TOK_NMTOKEN;
+ break;
+ }
+ break;
+#endif
+ case BT_PLUS:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_PLUS;
+ case BT_AST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_ASTERISK;
+ case BT_QUEST:
+ if (tok == XML_TOK_NMTOKEN) {
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_NAME_QUESTION;
+ default:
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ }
+ }
+ return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LT:
+ /* this is for inside entity references */
+ *nextTokPtr = ptr;
+ return XML_TOK_INVALID;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_S:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_ATTRIBUTE_VALUE_S;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+static
+int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ const char *start;
+ if (ptr == end)
+ return XML_TOK_NONE;
+ start = ptr;
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_AMP:
+ if (ptr == start)
+ return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_PERCNT:
+ if (ptr == start)
+ return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_LF:
+ if (ptr == start) {
+ *nextTokPtr = ptr + MINBPC(enc);
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ case BT_CR:
+ if (ptr == start) {
+ ptr += MINBPC(enc);
+ if (ptr == end)
+ return XML_TOK_TRAILING_CR;
+ if (BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_NEWLINE;
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ }
+ *nextTokPtr = ptr;
+ return XML_TOK_DATA_CHARS;
+}
+
+static
+int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **badPtr)
+{
+ ptr += MINBPC(enc);
+ end -= MINBPC(enc);
+ for (; ptr != end; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_DIGIT:
+ case BT_HEX:
+ case BT_MINUS:
+ case BT_APOS:
+ case BT_LPAR:
+ case BT_RPAR:
+ case BT_PLUS:
+ case BT_COMMA:
+ case BT_SOL:
+ case BT_EQUALS:
+ case BT_QUEST:
+ case BT_CR:
+ case BT_LF:
+ case BT_SEMI:
+ case BT_EXCL:
+ case BT_AST:
+ case BT_PERCNT:
+ case BT_NUM:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ break;
+ case BT_S:
+ if (CHAR_MATCHES(enc, ptr, '\t')) {
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ case BT_NAME:
+ case BT_NMSTRT:
+ if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+ break;
+ default:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 0x24: /* $ */
+ case 0x40: /* @ */
+ break;
+ default:
+ *badPtr = ptr;
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty element tag.
+Returns the number of attributes. Pointers to the first attsMax attributes
+are stored in atts. */
+
+static
+int PREFIX(getAtts)(const ENCODING *enc, const char *ptr,
+ int attsMax, ATTRIBUTE *atts)
+{
+ enum { other, inName, inValue } state = inName;
+ int nAtts = 0;
+ int open = 0;
+
+ for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define START_NAME \
+ if (state == other) { \
+ if (nAtts < attsMax) { \
+ atts[nAtts].name = ptr; \
+ atts[nAtts].normalized = 1; \
+ } \
+ state = inName; \
+ }
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+ case BT_HEX:
+ START_NAME
+ break;
+#undef START_NAME
+ case BT_QUOT:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_QUOT;
+ }
+ else if (open == BT_QUOT) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_APOS:
+ if (state != inValue) {
+ if (nAtts < attsMax)
+ atts[nAtts].valuePtr = ptr + MINBPC(enc);
+ state = inValue;
+ open = BT_APOS;
+ }
+ else if (open == BT_APOS) {
+ state = other;
+ if (nAtts < attsMax)
+ atts[nAtts].valueEnd = ptr;
+ nAtts++;
+ }
+ break;
+ case BT_AMP:
+ if (nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_S:
+ if (state == inName)
+ state = other;
+ else if (state == inValue
+ && nAtts < attsMax
+ && atts[nAtts].normalized
+ && (ptr == atts[nAtts].valuePtr
+ || BYTE_TO_ASCII(enc, ptr) != ' '
+ || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ' '
+ || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_CR: case BT_LF:
+ /* This case ensures that the first attribute name is counted
+ Apart from that we could just change state on the quote. */
+ if (state == inName)
+ state = other;
+ else if (state == inValue && nAtts < attsMax)
+ atts[nAtts].normalized = 0;
+ break;
+ case BT_GT:
+ case BT_SOL:
+ if (state != inValue)
+ return nAtts;
+ break;
+ default:
+ break;
+ }
+ }
+ /* not reached */
+}
+
+static
+int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr)
+{
+ int result = 0;
+ /* skip &# */
+ ptr += 2*MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'x')) {
+ for (ptr += MINBPC(enc); !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ switch (c) {
+case '0': case '1': case '2': case '3': case '4':
+case '5': case '6': case '7': case '8': case '9':
+ result <<= 4;
+ result |= (c - '0');
+ break;
+case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ result <<= 4;
+ result += 10 + (c - 'A');
+ break;
+case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ result <<= 4;
+ result += 10 + (c - 'a');
+ break;
+ }
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ else {
+ for (; !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) {
+ int c = BYTE_TO_ASCII(enc, ptr);
+ result *= 10;
+ result += (c - '0');
+ if (result >= 0x110000)
+ return -1;
+ }
+ }
+ return checkCharRefNumber(result);
+}
+
+static
+int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end)
+{
+ switch ((end - ptr)/MINBPC(enc)) {
+ case 2:
+ if (CHAR_MATCHES(enc, ptr + MINBPC(enc), 't')) {
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 'l':
+ return '<';
+ case 'g':
+ return '>';
+ }
+ }
+ break;
+ case 3:
+ if (CHAR_MATCHES(enc, ptr, 'a')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'm')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'p'))
+ return '&';
+ }
+ }
+ break;
+ case 4:
+ switch (BYTE_TO_ASCII(enc, ptr)) {
+ case 'q':
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'u')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'o')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 't'))
+ return '"';
+ }
+ }
+ break;
+ case 'a':
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'p')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 'o')) {
+ ptr += MINBPC(enc);
+ if (CHAR_MATCHES(enc, ptr, 's'))
+ return '\'';
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static
+int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr1)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ if (*ptr1++ != *ptr2++) \
+ return 0;
+ LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
+#undef LEAD_CASE
+ /* fall through */
+ if (*ptr1++ != *ptr2++)
+ return 0;
+ break;
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 1) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 2) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ if (MINBPC(enc) > 3) {
+ if (*ptr2++ != *ptr1++)
+ return 0;
+ }
+ }
+ }
+ break;
+ default:
+ if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
+ return 1;
+ switch (BYTE_TYPE(enc, ptr2)) {
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ return 0;
+ default:
+ return 1;
+ }
+ }
+ }
+ /* not reached */
+}
+
+static
+int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+ for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+ if (!CHAR_MATCHES(enc, ptr1, *ptr2))
+ return 0;
+ }
+ switch (BYTE_TYPE(enc, ptr1)) {
+ case BT_LEAD2:
+ case BT_LEAD3:
+ case BT_LEAD4:
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static
+int PREFIX(nameLength)(const ENCODING *enc, const char *ptr)
+{
+ const char *start = ptr;
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: ptr += n; break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_NONASCII:
+ case BT_NMSTRT:
+#ifdef XML_NS
+ case BT_COLON:
+#endif
+ case BT_HEX:
+ case BT_DIGIT:
+ case BT_NAME:
+ case BT_MINUS:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return ptr - start;
+ }
+ }
+}
+
+static
+const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr)
+{
+ for (;;) {
+ switch (BYTE_TYPE(enc, ptr)) {
+ case BT_LF:
+ case BT_CR:
+ case BT_S:
+ ptr += MINBPC(enc);
+ break;
+ default:
+ return ptr;
+ }
+ }
+}
+
+static
+void PREFIX(updatePosition)(const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ POSITION *pos)
+{
+ while (ptr != end) {
+ switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+ case BT_LEAD ## n: \
+ ptr += n; \
+ break;
+ LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+ case BT_LF:
+ pos->columnNumber = (unsigned)-1;
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ break;
+ case BT_CR:
+ pos->lineNumber++;
+ ptr += MINBPC(enc);
+ if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF)
+ ptr += MINBPC(enc);
+ pos->columnNumber = (unsigned)-1;
+ break;
+ default:
+ ptr += MINBPC(enc);
+ break;
+ }
+ pos->columnNumber++;
+ }
+}
+
+#undef DO_LEAD_CASE
+#undef MULTIBYTE_CASES
+#undef INVALID_CASES
+#undef CHECK_NAME_CASE
+#undef CHECK_NAME_CASES
+#undef CHECK_NMSTRT_CASE
+#undef CHECK_NMSTRT_CASES
diff --git a/protocols/jabber/xmltok_impl.h b/protocols/jabber/xmltok_impl.h
new file mode 100644
index 00000000..e72b225c
--- /dev/null
+++ b/protocols/jabber/xmltok_impl.h
@@ -0,0 +1,71 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above. If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+enum {
+ BT_NONXML,
+ BT_MALFORM,
+ BT_LT,
+ BT_AMP,
+ BT_RSQB,
+ BT_LEAD2,
+ BT_LEAD3,
+ BT_LEAD4,
+ BT_TRAIL,
+ BT_CR,
+ BT_LF,
+ BT_GT,
+ BT_QUOT,
+ BT_APOS,
+ BT_EQUALS,
+ BT_QUEST,
+ BT_EXCL,
+ BT_SOL,
+ BT_SEMI,
+ BT_NUM,
+ BT_LSQB,
+ BT_S,
+ BT_NMSTRT,
+ BT_COLON,
+ BT_HEX,
+ BT_DIGIT,
+ BT_NAME,
+ BT_MINUS,
+ BT_OTHER, /* known not to be a name or name start character */
+ BT_NONASCII, /* might be a name or name start character */
+ BT_PERCNT,
+ BT_LPAR,
+ BT_RPAR,
+ BT_AST,
+ BT_PLUS,
+ BT_COMMA,
+ BT_VERBAR
+};
+
+#include <stddef.h>
diff --git a/protocols/jabber/xmltok_ns.c b/protocols/jabber/xmltok_ns.c
new file mode 100644
index 00000000..ace3e5a4
--- /dev/null
+++ b/protocols/jabber/xmltok_ns.c
@@ -0,0 +1,117 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+const ENCODING *NS(XmlGetUtf8InternalEncoding)()
+{
+ return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *NS(XmlGetUtf16InternalEncoding)()
+{
+#if XML_BYTE_ORDER == 12
+ return &ns(internal_little2_encoding).enc;
+#elif XML_BYTE_ORDER == 21
+return &ns(internal_big2_encoding).enc;
+#else
+const short n = 1;
+ return *(const char *)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc;
+#endif
+}
+
+static
+const ENCODING *NS(encodings)[] = {
+ &ns(latin1_encoding).enc,
+ &ns(ascii_encoding).enc,
+ &ns(utf8_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(big2_encoding).enc,
+ &ns(little2_encoding).enc,
+ &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static
+int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr);
+}
+
+static
+int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+ const char **nextTokPtr)
+{
+ return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr);
+}
+
+int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name)
+{
+ int i = getEncodingIndex(name);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ INIT_ENC_INDEX(p) = (char)i;
+ p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+ p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+ p->initEnc.updatePosition = initUpdatePosition;
+ p->encPtr = encPtr;
+ *encPtr = &(p->initEnc);
+ return 1;
+}
+
+static
+const ENCODING *NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
+{
+#define ENCODING_MAX 128
+ char buf[ENCODING_MAX];
+ char *p = buf;
+ int i;
+ XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+ if (ptr != end)
+ return 0;
+ *p = 0;
+ if (streqci(buf, "UTF-16") && enc->minBytesPerChar == 2)
+ return enc;
+ i = getEncodingIndex(buf);
+ if (i == UNKNOWN_ENC)
+ return 0;
+ return NS(encodings)[i];
+}
+
+int NS(XmlParseXmlDecl)(int isGeneralTextEntity,
+ const ENCODING *enc,
+ const char *ptr,
+ const char *end,
+ const char **badPtr,
+ const char **versionPtr,
+ const char **encodingName,
+ const ENCODING **encoding,
+ int *standalone)
+{
+ return doParseXmlDecl(NS(findEncoding),
+ isGeneralTextEntity,
+ enc,
+ ptr,
+ end,
+ badPtr,
+ versionPtr,
+ encodingName,
+ encoding,
+ standalone);
+}
diff --git a/protocols/jabber/xstream.c b/protocols/jabber/xstream.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/jabber/xstream.c
diff --git a/protocols/md5.c b/protocols/md5.c
new file mode 100644
index 00000000..e6273585
--- /dev/null
+++ b/protocols/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/protocols/md5.h b/protocols/md5.h
new file mode 100644
index 00000000..f24f2ff1
--- /dev/null
+++ b/protocols/md5.h
@@ -0,0 +1,85 @@
+/*
+ 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):
+
+ 2004-03-09 Jelmer Vernooij add G_MODULE_EXPORT for Bitlbee
+ 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
+
+#include <glib.h>
+#include <gmodule.h>
+
+/*
+ * 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. */
+G_MODULE_EXPORT void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+G_MODULE_EXPORT void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+G_MODULE_EXPORT void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile
new file mode 100644
index 00000000..e6620323
--- /dev/null
+++ b/protocols/msn/Makefile
@@ -0,0 +1,39 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = msn.o msn_util.o ns.o passport.o sb.o tables.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: msnn.o
+
+.PHONY: all clean distclean
+
+clean:
+ rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+msnn.o: $(objects)
+ @echo '*' Linking msnn.o
+ @$(LD) $(LFLAGS) $(objects) -o msnn.o
+
+
diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c
new file mode 100644
index 00000000..33e12af5
--- /dev/null
+++ b/protocols/msn/msn.c
@@ -0,0 +1,402 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module - Main file; functions to be called from BitlBee */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+
+static struct prpl *my_protocol = NULL;
+
+static void msn_login( struct aim_user *acct )
+{
+ struct gaim_connection *gc = new_gaim_conn( acct );
+ struct msn_data *md = g_new0( struct msn_data, 1 );
+
+ set_login_progress( gc, 1, "Connecting" );
+
+ gc->proto_data = md;
+ md->fd = -1;
+
+ if( strchr( acct->username, '@' ) == NULL )
+ {
+ hide_login_progress( gc, "Invalid account name" );
+ signoff( gc );
+ return;
+ }
+
+ md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, gc );
+ if( md->fd < 0 )
+ {
+ hide_login_progress( gc, "Could not connect to server" );
+ signoff( gc );
+ }
+ else
+ {
+ md->gc = gc;
+ md->away_state = msn_away_state_list;
+
+ msn_connections = g_slist_append( msn_connections, gc );
+ }
+}
+
+static void msn_close( struct gaim_connection *gc )
+{
+ struct msn_data *md = gc->proto_data;
+ GSList *l;
+
+ if( md->fd >= 0 )
+ closesocket( md->fd );
+
+ if( md->handler )
+ {
+ if( md->handler->rxq ) g_free( md->handler->rxq );
+ if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
+ g_free( md->handler );
+ }
+
+ while( md->switchboards )
+ msn_sb_destroy( md->switchboards->data );
+
+ if( md->msgq )
+ {
+ struct msn_message *m;
+
+ for( l = md->msgq; l; l = l->next )
+ {
+ m = l->data;
+ g_free( m->who );
+ g_free( m->text );
+ g_free( m );
+ }
+ g_slist_free( md->msgq );
+
+ serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message(s), you'll have to resend them." );
+ }
+
+ for( l = gc->permit; l; l = l->next )
+ g_free( l->data );
+ g_slist_free( gc->permit );
+
+ for( l = gc->deny; l; l = l->next )
+ g_free( l->data );
+ g_slist_free( gc->deny );
+
+ g_free( md );
+
+ msn_connections = g_slist_remove( msn_connections, gc );
+}
+
+static int msn_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
+{
+ struct msn_switchboard *sb;
+ struct msn_data *md = gc->proto_data;
+
+ if( ( sb = msn_sb_by_handle( gc, who ) ) )
+ {
+ return( msn_sb_sendmessage( sb, message ) );
+ }
+ else
+ {
+ struct msn_message *m;
+ char buf[1024];
+
+ /* Create a message. We have to arrange a usable switchboard, and send the message later. */
+ m = g_new0( struct msn_message, 1 );
+ m->who = g_strdup( who );
+ m->text = g_strdup( message );
+
+ /* FIXME: *CHECK* the reliability of using spare sb's! */
+ if( ( sb = msn_sb_spare( gc ) ) )
+ {
+ debug( "Trying to use a spare switchboard to message %s", who );
+
+ sb->who = g_strdup( who );
+ g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+ if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ {
+ /* He/She should join the switchboard soon, let's queue the message. */
+ sb->msgq = g_slist_append( sb->msgq, m );
+ return( 1 );
+ }
+ }
+
+ debug( "Creating a new switchboard to message %s", who );
+
+ /* If we reach this line, there was no spare switchboard, so let's make one. */
+ g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+ if( !msn_write( gc, buf, strlen( buf ) ) )
+ {
+ g_free( m->who );
+ g_free( m->text );
+ g_free( m );
+
+ return( 0 );
+ }
+
+ /* And queue the message to md. We'll pick it up when the switchboard comes up. */
+ md->msgq = g_slist_append( md->msgq, m );
+
+ /* FIXME: If the switchboard creation fails, the message will not be sent. */
+
+ return( 1 );
+ }
+
+ return( 0 );
+}
+
+static GList *msn_away_states( struct gaim_connection *gc )
+{
+ GList *l = NULL;
+ int i;
+
+ for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ l = g_list_append( l, msn_away_state_list[i].name );
+
+ return( l );
+}
+
+static char *msn_get_status_string( struct gaim_connection *gc, int number )
+{
+ struct msn_away_state *st = msn_away_state_by_number( number );
+
+ if( st )
+ return( st->name );
+ else
+ return( "" );
+}
+
+static void msn_set_away( struct gaim_connection *gc, char *state, char *message )
+{
+ char buf[1024];
+ struct msn_data *md = gc->proto_data;
+ struct msn_away_state *st;
+
+ if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
+ st = msn_away_state_by_name( "Away" );
+ else
+ st = msn_away_state_by_name( state );
+
+ if( !st ) st = msn_away_state_list;
+ md->away_state = st;
+
+ g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
+ msn_write( gc, buf, strlen( buf ) );
+}
+
+static void msn_set_info( struct gaim_connection *gc, char *info )
+{
+ int i;
+ char buf[1024], *fn, *s;
+ struct msn_data *md = gc->proto_data;
+
+ if( strlen( info ) > 129 )
+ {
+ do_error_dialog( gc, "Maximum name length exceeded", "MSN" );
+ return;
+ }
+
+ /* Of course we could use http_encode() here, but when we encode
+ every character, the server is less likely to complain about the
+ chosen name. However, the MSN server doesn't seem to like escaped
+ non-ASCII chars, so we keep those unescaped. */
+ s = fn = g_new0( char, strlen( info ) * 3 + 1 );
+ for( i = 0; info[i]; i ++ )
+ if( info[i] & 128 )
+ {
+ *s = info[i];
+ s ++;
+ }
+ else
+ {
+ g_snprintf( s, 4, "%%%02X", info[i] );
+ s += 3;
+ }
+
+ g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn );
+ msn_write( gc, buf, strlen( buf ) );
+ g_free( fn );
+}
+
+static void msn_get_info(struct gaim_connection *gc, char *who)
+{
+ /* Just make an URL and let the user fetch the info */
+ serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
+}
+
+static void msn_add_buddy( struct gaim_connection *gc, char *who )
+{
+ msn_buddy_list_add( gc, "FL", who, who );
+}
+
+static void msn_remove_buddy( struct gaim_connection *gc, char *who, char *group )
+{
+ msn_buddy_list_remove( gc, "FL", who );
+}
+
+static int msn_chat_send( struct gaim_connection *gc, int id, char *message )
+{
+ struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+
+ if( sb )
+ return( msn_sb_sendmessage( sb, message ) );
+ else
+ return( 0 );
+}
+
+static void msn_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
+{
+ struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+ char buf[1024];
+
+ if( sb )
+ {
+ g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+ msn_sb_write( sb, buf, strlen( buf ) );
+ }
+}
+
+static void msn_chat_leave( struct gaim_connection *gc, int id )
+{
+ struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+
+ if( sb )
+ msn_sb_write( sb, "OUT\r\n", 5 );
+}
+
+static int msn_chat_open( struct gaim_connection *gc, char *who )
+{
+ struct msn_switchboard *sb;
+ struct msn_data *md = gc->proto_data;
+ char buf[1024];
+
+ if( ( sb = msn_sb_by_handle( gc, who ) ) )
+ {
+ debug( "Converting existing switchboard to %s to a groupchat", who );
+ msn_sb_to_chat( sb );
+ return( 1 );
+ }
+ else
+ {
+ struct msn_message *m;
+
+ if( ( sb = msn_sb_spare( gc ) ) )
+ {
+ debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
+ g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+ if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ {
+ msn_sb_to_chat( sb );
+ return( 1 );
+ }
+ }
+
+ /* If the stuff above failed for some reason: */
+ debug( "Creating a new switchboard to groupchat with %s", who );
+
+ /* Request a new switchboard. */
+ g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+ if( !msn_write( gc, buf, strlen( buf ) ) )
+ return( 0 );
+
+ /* Create a magic message. This is quite hackish, but who cares? :-P */
+ m = g_new0( struct msn_message, 1 );
+ m->who = g_strdup( who );
+ m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
+
+ /* Queue the magic message and cross your fingers. */
+ md->msgq = g_slist_append( md->msgq, m );
+
+ return( 1 );
+ }
+
+ return( 0 );
+}
+
+static void msn_keepalive( struct gaim_connection *gc )
+{
+ msn_write( gc, "PNG\r\n", strlen( "PNG\r\n" ) );
+}
+
+static void msn_add_permit( struct gaim_connection *gc, char *who )
+{
+ msn_buddy_list_add( gc, "AL", who, who );
+}
+
+static void msn_rem_permit( struct gaim_connection *gc, char *who )
+{
+ msn_buddy_list_remove( gc, "AL", who );
+}
+
+static void msn_add_deny( struct gaim_connection *gc, char *who )
+{
+ struct msn_switchboard *sb;
+
+ msn_buddy_list_add( gc, "BL", who, who );
+
+ /* If there's still a conversation with this person, close it. */
+ if( ( sb = msn_sb_by_handle( gc, who ) ) )
+ {
+ msn_sb_destroy( sb );
+ }
+}
+
+static void msn_rem_deny( struct gaim_connection *gc, char *who )
+{
+ msn_buddy_list_remove( gc, "BL", who );
+}
+
+static int msn_send_typing( struct gaim_connection *gc, char *who, int typing )
+{
+ if( typing )
+ return( msn_send_im( gc, who, TYPING_NOTIFICATION_MESSAGE, strlen( TYPING_NOTIFICATION_MESSAGE ), 0 ) );
+ else
+ return( 1 );
+}
+
+void msn_init(struct prpl *ret)
+{
+ ret->protocol = PROTO_MSN;
+ ret->login = msn_login;
+ ret->close = msn_close;
+ ret->send_im = msn_send_im;
+ ret->away_states = msn_away_states;
+ ret->get_status_string = msn_get_status_string;
+ ret->set_away = msn_set_away;
+ ret->set_info = msn_set_info;
+ ret->get_info = msn_get_info;
+ ret->add_buddy = msn_add_buddy;
+ ret->remove_buddy = msn_remove_buddy;
+ ret->chat_send = msn_chat_send;
+ ret->chat_invite = msn_chat_invite;
+ ret->chat_leave = msn_chat_leave;
+ ret->chat_open = msn_chat_open;
+ ret->keepalive = msn_keepalive;
+ ret->add_permit = msn_add_permit;
+ ret->rem_permit = msn_rem_permit;
+ ret->add_deny = msn_add_deny;
+ ret->rem_deny = msn_rem_deny;
+ ret->send_typing = msn_send_typing;
+
+ my_protocol = ret;
+}
diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h
new file mode 100644
index 00000000..61231d8a
--- /dev/null
+++ b/protocols/msn/msn.h
@@ -0,0 +1,170 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Some hackish magicstrings to make special-purpose messages/switchboards.
+ */
+#define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
+#define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
+
+#ifdef _WIN32
+#define debug
+#else
+#define debug( text... ) irc_usermsg( IRC, text );
+#undef debug
+#define debug( text... )
+#endif
+
+#define QRY_NAME "msmsgs@msnmsgr.com"
+#define QRY_CODE "Q1P7W2E4J9R8U3S5"
+
+#define MSN_SB_NEW -24062002
+
+#define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \
+ "Content-Type: text/plain; charset=UTF-8\r\n" \
+ "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+ "X-MMS-IM-Format: FN=MS%20Shell%20Dlg; EF=; CO=0; CS=0; PF=0\r\n" \
+ "\r\n"
+
+#define MSN_TYPING_HEADERS "MIME-Version: 1.0\r\n" \
+ "Content-Type: text/x-msmsgscontrol\r\n" \
+ "TypingUser: %s\r\n" \
+ "\r\n\r\n"
+
+#define PROFILE_URL "http://members.msn.com/"
+
+struct msn_data
+{
+ struct gaim_connection *gc;
+
+ int fd;
+ struct msn_handler_data *handler;
+
+ int trId;
+
+ GSList *msgq;
+ GSList *switchboards;
+ int buddycount;
+ struct msn_away_state *away_state;
+};
+
+struct msn_switchboard
+{
+ struct gaim_connection *gc;
+
+ int fd;
+ gint inp;
+ struct msn_handler_data *handler;
+
+ int trId;
+ int ready;
+
+ int session;
+ char *key;
+
+ GSList *msgq;
+ char *who;
+ struct conversation *chat;
+};
+
+struct msn_away_state
+{
+ int number;
+ char code[4];
+ char name[16];
+};
+
+struct msn_status_code
+{
+ int number;
+ char *text;
+ int flags;
+};
+
+struct msn_message
+{
+ char *who;
+ char *text;
+};
+
+struct msn_handler_data
+{
+ int fd;
+ int rxlen;
+ char *rxq;
+
+ int msglen;
+ char *cmd_text;
+
+ gpointer data;
+
+ int (*exec_command) ( gpointer data, char **cmd, int count );
+ int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count );
+};
+
+/* Bitfield values for msn_status_code.flags */
+#define STATUS_FATAL 1
+#define STATUS_SB_FATAL 2
+
+int msn_chat_id;
+extern struct msn_away_state msn_away_state_list[];
+extern struct msn_status_code msn_status_code_list[];
+
+/* Keep a list of all the active connections. We need these lists because
+ "connected" callbacks might be called when the connection they belong too
+ is down already (for example, when an impatient user disabled the
+ connection), the callback should check whether it's still listed here
+ before doing *anything* else. */
+GSList *msn_connections;
+GSList *msn_switchboards;
+
+/* ns.c */
+void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond );
+
+/* msn_util.c */
+int msn_write( struct gaim_connection *gc, char *s, int len );
+int msn_logged_in( struct gaim_connection *gc );
+int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname );
+int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who );
+void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname );
+char *msn_findheader( char *text, char *header, int len );
+char **msn_linesplit( char *line );
+int msn_handler( struct msn_handler_data *h );
+
+/* tables.c */
+struct msn_away_state *msn_away_state_by_number( int number );
+struct msn_away_state *msn_away_state_by_code( char *code );
+struct msn_away_state *msn_away_state_by_name( char *name );
+struct msn_status_code *msn_status_by_number( int number );
+
+/* sb.c */
+int msn_sb_write( struct msn_switchboard *sb, char *s, int len );
+struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session );
+struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle );
+struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id );
+struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc );
+int msn_sb_sendmessage( struct msn_switchboard *sb, char *text );
+void msn_sb_to_chat( struct msn_switchboard *sb );
+void msn_sb_destroy( struct msn_switchboard *sb );
+void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond );
diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c
new file mode 100644
index 00000000..2f22acfc
--- /dev/null
+++ b/protocols/msn/msn_util.c
@@ -0,0 +1,359 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module - Miscellaneous utilities */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+#include <ctype.h>
+
+int msn_write( struct gaim_connection *gc, char *s, int len )
+{
+ struct msn_data *md = gc->proto_data;
+ int st;
+
+ st = write( md->fd, s, len );
+ if( st != len )
+ {
+ hide_login_progress_error( gc, "Short write() to main server" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ return( 1 );
+}
+
+int msn_logged_in( struct gaim_connection *gc )
+{
+ struct msn_data *md = gc->proto_data;
+ char buf[1024];
+
+ account_online( gc );
+
+ /* account_online() sets an away state if there is any, so only
+ execute this code if we're not away. */
+ if( md->away_state == msn_away_state_list )
+ {
+ g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+
+ return( 0 );
+}
+
+int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname_ )
+{
+ struct msn_data *md = gc->proto_data;
+ GSList *l, **lp = NULL;
+ char buf[1024], *realname;
+
+ if( strcmp( list, "AL" ) == 0 )
+ lp = &gc->permit;
+ else if( strcmp( list, "BL" ) == 0 )
+ lp = &gc->deny;
+
+ if( lp )
+ for( l = *lp; l; l = l->next )
+ if( g_strcasecmp( l->data, who ) == 0 )
+ return( 1 );
+
+ realname = g_new0( char, strlen( realname_ ) * 3 + 1 );
+ strcpy( realname, realname_ );
+ http_encode( realname );
+
+ g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname );
+ if( msn_write( gc, buf, strlen( buf ) ) )
+ {
+ g_free( realname );
+
+ if( lp )
+ *lp = g_slist_append( *lp, g_strdup( who ) );
+
+ return( 1 );
+ }
+
+ g_free( realname );
+
+ return( 0 );
+}
+
+int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who )
+{
+ struct msn_data *md = gc->proto_data;
+ GSList *l = NULL, **lp = NULL;
+ char buf[1024];
+
+ if( strcmp( list, "AL" ) == 0 )
+ lp = &gc->permit;
+ else if( strcmp( list, "BL" ) == 0 )
+ lp = &gc->deny;
+
+ if( lp )
+ {
+ for( l = *lp; l; l = l->next )
+ if( g_strcasecmp( l->data, who ) == 0 )
+ break;
+
+ if( !l )
+ return( 1 );
+ }
+
+ g_snprintf( buf, sizeof( buf ), "REM %d %s %s\r\n", ++md->trId, list, who );
+ if( msn_write( gc, buf, strlen( buf ) ) )
+ {
+ if( lp )
+ *lp = g_slist_remove( *lp, l->data );
+
+ return( 1 );
+ }
+
+ return( 0 );
+}
+
+struct msn_buddy_ask_data
+{
+ struct gaim_connection *gc;
+ char *handle;
+ char *realname;
+};
+
+static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
+{
+ msn_buddy_list_add( bla->gc, "AL", bla->handle, bla->realname );
+
+ g_free( bla->handle );
+ g_free( bla->realname );
+ g_free( bla );
+}
+
+static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
+{
+ msn_buddy_list_add( bla->gc, "BL", bla->handle, bla->realname );
+
+ g_free( bla->handle );
+ g_free( bla->realname );
+ g_free( bla );
+}
+
+void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname )
+{
+ struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 );
+ char buf[1024];
+
+ bla->gc = gc;
+ bla->handle = g_strdup( handle );
+ bla->realname = g_strdup( realname );
+
+ g_snprintf( buf, sizeof( buf ),
+ "The user %s (%s) wants to add you to his/her buddy list. Do you want to allow this?",
+ handle, realname );
+ do_ask_dialog( gc, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
+}
+
+char *msn_findheader( char *text, char *header, int len )
+{
+ int hlen = strlen( header ), i;
+ char *ret;
+
+ if( len == 0 )
+ len = strlen( text );
+
+ i = 0;
+ while( ( i + hlen ) < len )
+ {
+ /* Maybe this is a bit over-commented, but I just hate this part... */
+ if( g_strncasecmp( text + i, header, hlen ) == 0 )
+ {
+ /* Skip to the (probable) end of the header */
+ i += hlen;
+
+ /* Find the first non-[: \t] character */
+ while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++;
+
+ /* Make sure we're still inside the string */
+ if( i >= len ) return( NULL );
+
+ /* Save the position */
+ ret = text + i;
+
+ /* Search for the end of this line */
+ while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++;
+
+ /* Make sure we're still inside the string */
+ if( i >= len ) return( NULL );
+
+ /* Copy the found data */
+ return( g_strndup( ret, text + i - ret ) );
+ }
+
+ /* This wasn't the header we were looking for, skip to the next line. */
+ while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++;
+ while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;
+
+ /* End of headers? */
+ if( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
+ strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ||
+ strncmp( text + i - 2, "\r\r", 2 ) == 0 )
+ {
+ break;
+ }
+ }
+
+ return( NULL );
+}
+
+/* *NOT* thread-safe, but that's not a problem for now... */
+char **msn_linesplit( char *line )
+{
+ static char **ret = NULL;
+ static int size = 3;
+ int i, n = 0;
+
+ if( ret == NULL )
+ ret = g_new0( char*, size );
+
+ for( i = 0; line[i] && line[i] == ' '; i ++ );
+ if( line[i] )
+ {
+ ret[n++] = line + i;
+ for( i ++; line[i]; i ++ )
+ {
+ if( line[i] == ' ' )
+ line[i] = 0;
+ else if( line[i] != ' ' && !line[i-1] )
+ ret[n++] = line + i;
+
+ if( n >= size )
+ ret = g_renew( char*, ret, size += 2 );
+ }
+ }
+ ret[n] = NULL;
+
+ return( ret );
+}
+
+/* This one handles input from a MSN Messenger server. Both the NS and SB servers usually give
+ commands, but sometimes they give additional data (payload). This function tries to handle
+ this all in a nice way and send all data to the right places. */
+
+/* Return values: -1: Read error, abort connection.
+ 0: Command reported error; Abort *immediately*. (The connection does not exist anymore)
+ 1: OK */
+
+int msn_handler( struct msn_handler_data *h )
+{
+ int st;
+
+ h->rxq = g_renew( char, h->rxq, h->rxlen + 1024 );
+ st = read( h->fd, h->rxq + h->rxlen, 1024 );
+ h->rxlen += st;
+
+ if( st <= 0 )
+ return( -1 );
+
+ while( st )
+ {
+ int i;
+
+ if( h->msglen == 0 )
+ {
+ for( i = 0; i < h->rxlen; i ++ )
+ {
+ if( h->rxq[i] == '\r' || h->rxq[i] == '\n' )
+ {
+ char *cmd_text, **cmd;
+ int count;
+
+ cmd_text = g_strndup( h->rxq, i );
+ cmd = msn_linesplit( cmd_text );
+ for( count = 0; cmd[count]; count ++ );
+ st = h->exec_command( h->data, cmd, count );
+ g_free( cmd_text );
+
+ /* If the connection broke, don't continue. We don't even exist anymore. */
+ if( !st )
+ return( 0 );
+
+ if( h->msglen )
+ h->cmd_text = g_strndup( h->rxq, i );
+
+ /* Skip to the next non-emptyline */
+ while( i < h->rxlen && ( h->rxq[i] == '\r' || h->rxq[i] == '\n' ) ) i ++;
+
+ break;
+ }
+ }
+
+ /* If we reached the end of the buffer, there's still an incomplete command there.
+ Return and wait for more data. */
+ if( i == h->rxlen && h->rxq[i-1] != '\r' && h->rxq[i-1] != '\n' )
+ break;
+ }
+ else
+ {
+ char *msg, **cmd;
+ int count;
+
+ /* Do we have the complete message already? */
+ if( h->msglen > h->rxlen )
+ break;
+
+ msg = g_strndup( h->rxq, h->msglen );
+ cmd = msn_linesplit( h->cmd_text );
+ for( count = 0; cmd[count]; count ++ );
+
+ st = h->exec_message( h->data, msg, h->msglen, cmd, count );
+ g_free( msg );
+ g_free( h->cmd_text );
+ h->cmd_text = NULL;
+
+ if( !st )
+ return( 0 );
+
+ i = h->msglen;
+ h->msglen = 0;
+ }
+
+ /* More data after this block? */
+ if( i < h->rxlen )
+ {
+ char *tmp;
+
+ tmp = g_memdup( h->rxq + i, h->rxlen - i );
+ g_free( h->rxq );
+ h->rxq = tmp;
+ h->rxlen -= i;
+ i = 0;
+ }
+ else
+ /* If not, reset the rx queue and get lost. */
+ {
+ g_free( h->rxq );
+ h->rxq = g_new0( char, 1 );
+ h->rxlen = 0;
+ return( 1 );
+ }
+ }
+
+ return( 1 );
+}
diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c
new file mode 100644
index 00000000..f1bda1a4
--- /dev/null
+++ b/protocols/msn/ns.c
@@ -0,0 +1,661 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module - Notification server callbacks */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <ctype.h>
+#include "nogaim.h"
+#include "msn.h"
+#include "passport.h"
+#include "md5.h"
+
+static void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond );
+static int msn_ns_command( gpointer data, char **cmd, int num_parts );
+static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
+
+static void msn_auth_got_passport_id( struct passport_reply *rep );
+
+void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct gaim_connection *gc = data;
+ struct msn_data *md;
+ char s[1024];
+
+ if( !g_slist_find( msn_connections, gc ) )
+ return;
+
+ if( source == -1 )
+ {
+ hide_login_progress( gc, "Could not connect to server" );
+ signoff( gc );
+ return;
+ }
+
+ md = gc->proto_data;
+
+ if( !md->handler )
+ {
+ md->handler = g_new0( struct msn_handler_data, 1 );
+ md->handler->data = gc;
+ md->handler->exec_command = msn_ns_command;
+ md->handler->exec_message = msn_ns_message;
+ }
+ else
+ {
+ if( md->handler->rxq )
+ g_free( md->handler->rxq );
+
+ md->handler->rxlen = 0;
+ }
+
+ md->handler->fd = md->fd;
+ md->handler->rxq = g_new0( char, 1 );
+
+ g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );
+ if( msn_write( gc, s, strlen( s ) ) )
+ {
+ gc->inpa = gaim_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, gc );
+ set_login_progress( gc, 1, "Connected to server, waiting for reply" );
+ }
+}
+
+void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct gaim_connection *gc = data;
+ struct msn_data *md = gc->proto_data;
+
+ if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
+ {
+ hide_login_progress( gc, "Error while reading from server" );
+ signoff( gc );
+ }
+}
+
+static int msn_ns_command( gpointer data, char **cmd, int num_parts )
+{
+ struct gaim_connection *gc = data;
+ struct msn_data *md = gc->proto_data;
+ char buf[1024];
+
+ if( num_parts == 0 )
+ {
+ /* Hrrm... Empty command...? Ignore? */
+ return( 1 );
+ }
+
+ if( strcmp( cmd[0], "VER" ) == 0 )
+ {
+ if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 )
+ {
+ hide_login_progress( gc, "Unsupported protocol" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
+ ++md->trId, gc->username );
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+ else if( strcmp( cmd[0], "CVR" ) == 0 )
+ {
+ /* We don't give a damn about the information we just received */
+ g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, gc->username );
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+ else if( strcmp( cmd[0], "XFR" ) == 0 )
+ {
+ char *server;
+ int port;
+
+ if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 )
+ {
+ gaim_input_remove( gc->inpa );
+ gc->inpa = 0;
+ closesocket( md->fd );
+
+ server = strchr( cmd[3], ':' );
+ if( !server )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ *server = 0;
+ port = atoi( server + 1 );
+ server = cmd[3];
+
+ set_login_progress( gc, 1, "Transferring to other server" );
+
+ md->fd = proxy_connect( server, port, msn_ns_connected, gc );
+ }
+ else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 )
+ {
+ struct msn_switchboard *sb;
+
+ server = strchr( cmd[3], ':' );
+ if( !server )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ *server = 0;
+ port = atoi( server + 1 );
+ server = cmd[3];
+
+ if( strcmp( cmd[4], "CKI" ) != 0 )
+ {
+ hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ debug( "Connecting to a new switchboard with key %s", cmd[5] );
+ sb = msn_sb_create( gc, server, port, cmd[5], MSN_SB_NEW );
+
+ if( md->msgq )
+ {
+ struct msn_message *m = md->msgq->data;
+ GSList *l;
+
+ sb->who = g_strdup( m->who );
+
+ /* Move all the messages to the first user in the message
+ queue to the switchboard message queue. */
+ l = md->msgq;
+ while( l )
+ {
+ m = l->data;
+ l = l->next;
+ if( strcmp( m->who, sb->who ) == 0 )
+ {
+ sb->msgq = g_slist_append( sb->msgq, m );
+ md->msgq = g_slist_remove( md->msgq, m );
+ }
+ }
+ }
+ }
+ else
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else if( strcmp( cmd[0], "USR" ) == 0 )
+ {
+ if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 )
+ {
+ /* Time for some Passport black magic... */
+ if( !passport_get_id( gc, gc->username, gc->password, cmd[4], msn_auth_got_passport_id ) )
+ {
+ hide_login_progress_error( gc, "Error while contacting Passport server" );
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 )
+ {
+ http_decode( cmd[4] );
+
+ strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
+ gc->displayname[sizeof(gc->displayname)-1] = 0;
+
+ set_login_progress( gc, 1, "Authenticated, getting buddy list" );
+
+ g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId );
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+ else
+ {
+ hide_login_progress( gc, "Unknown authentication type" );
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else if( strcmp( cmd[0], "MSG" ) == 0 )
+ {
+ if( num_parts != 4 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ md->handler->msglen = atoi( cmd[3] );
+
+ if( md->handler->msglen <= 0 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else if( strcmp( cmd[0], "SYN" ) == 0 )
+ {
+ if( num_parts == 5 )
+ {
+ md->buddycount = atoi( cmd[3] );
+
+ if( !*cmd[3] || md->buddycount == 0 )
+ msn_logged_in( gc );
+ }
+ else
+ {
+ /* Hrrm... This SYN reply doesn't really look like something we expected.
+ Let's assume everything is okay. */
+
+ msn_logged_in( gc );
+ }
+ }
+ else if( strcmp( cmd[0], "GTC" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "BLP" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "PRP" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "LSG" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "LST" ) == 0 )
+ {
+ int list;
+
+ if( num_parts != 4 && num_parts != 5 )
+ {
+ hide_login_progress( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ http_decode( cmd[2] );
+ list = atoi( cmd[3] );
+
+ if( list & 1 ) /* FL */
+ {
+ add_buddy( gc, NULL, cmd[1], cmd[2] );
+ }
+ if( list & 2 ) /* AL */
+ {
+ gc->permit = g_slist_append( gc->permit, g_strdup( cmd[1] ) );
+ }
+ if( list & 4 ) /* BL */
+ {
+ gc->deny = g_slist_append( gc->deny, g_strdup( cmd[1] ) );
+ }
+ if( list & 8 ) /* RL */
+ {
+ if( ( list & 6 ) == 0 )
+ msn_buddy_ask( gc, cmd[1], cmd[2] );
+ }
+
+ if( --md->buddycount == 0 )
+ {
+ if( gc->flags & OPT_LOGGED_IN )
+ {
+ serv_got_crap( gc, "Successfully transferred to different server" );
+ g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+ else
+ {
+ msn_logged_in( gc );
+ }
+ }
+ }
+ else if( strcmp( cmd[0], "BPR" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "CHG" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "CHL" ) == 0 )
+ {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int i;
+
+ if( num_parts != 3 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ md5_init( &state );
+ md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) );
+ md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) );
+ md5_finish( &state, digest );
+
+ g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 );
+ for( i = 0; i < 16; i ++ )
+ g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] );
+
+ return( msn_write( gc, buf, strlen( buf ) ) );
+ }
+ else if( strcmp( cmd[0], "QRY" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "QNG" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "ILN" ) == 0 )
+ {
+ struct msn_away_state *st;
+
+ if( num_parts != 6 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ http_decode( cmd[4] );
+ serv_buddy_rename( gc, cmd[3], cmd[4] );
+
+ st = msn_away_state_by_code( cmd[2] );
+ if( !st )
+ {
+ /* FIXME: Warn/Bomb about unknown away state? */
+ st = msn_away_state_list;
+ }
+
+ serv_got_update( gc, cmd[3], 1, 0, 0, 0, st->number, 0 );
+ }
+ else if( strcmp( cmd[0], "FLN" ) == 0 )
+ {
+ if( cmd[1] )
+ serv_got_update( gc, cmd[1], 0, 0, 0, 0, 0, 0 );
+ }
+ else if( strcmp( cmd[0], "NLN" ) == 0 )
+ {
+ struct msn_away_state *st;
+
+ if( num_parts != 5 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ http_decode( cmd[3] );
+ serv_buddy_rename( gc, cmd[2], cmd[3] );
+
+ st = msn_away_state_by_code( cmd[1] );
+ if( !st )
+ {
+ /* FIXME: Warn/Bomb about unknown away state? */
+ st = msn_away_state_list;
+ }
+
+ serv_got_update( gc, cmd[2], 1, 0, 0, 0, st->number, 0 );
+ }
+ else if( strcmp( cmd[0], "RNG" ) == 0 )
+ {
+ struct msn_switchboard *sb;
+ char *server;
+ int session, port;
+
+ if( num_parts != 7 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ session = atoi( cmd[1] );
+
+ server = strchr( cmd[2], ':' );
+ if( !server )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ *server = 0;
+ port = atoi( server + 1 );
+ server = cmd[2];
+
+ if( strcmp( cmd[3], "CKI" ) != 0 )
+ {
+ hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
+
+ sb = msn_sb_create( gc, server, port, cmd[4], session );
+ sb->who = g_strdup( cmd[5] );
+ }
+ else if( strcmp( cmd[0], "ADD" ) == 0 )
+ {
+ if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 )
+ {
+ GSList *l;
+
+ http_decode( cmd[5] );
+
+ if( strchr( cmd[4], '@' ) == NULL )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ /* We got added by someone. If we don't have this person in permit/deny yet, inform the user. */
+ for( l = gc->permit; l; l = l->next )
+ if( g_strcasecmp( l->data, cmd[4] ) == 0 )
+ return( 1 );
+
+ for( l = gc->deny; l; l = l->next )
+ if( g_strcasecmp( l->data, cmd[4] ) == 0 )
+ return( 1 );
+
+ msn_buddy_ask( gc, cmd[4], cmd[5] );
+ }
+ }
+ else if( strcmp( cmd[0], "REM" ) == 0 )
+ {
+ }
+ else if( strcmp( cmd[0], "OUT" ) == 0 )
+ {
+ if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 )
+ {
+ hide_login_progress_error( gc, "Someone else logged in with your account" );
+ gc->wants_to_die = 1;
+ }
+ else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 )
+ {
+ hide_login_progress_error( gc, "Terminating session because of server shutdown" );
+ }
+ else
+ {
+ hide_login_progress_error( gc, "Session terminated by remote server (reason unknown)" );
+ }
+
+ signoff( gc );
+ return( 0 );
+ }
+ else if( strcmp( cmd[0], "REA" ) == 0 )
+ {
+ if( num_parts != 5 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+
+ if( g_strcasecmp( cmd[3], gc->username ) == 0 )
+ {
+ http_decode( cmd[4] );
+ strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
+ gc->displayname[sizeof(gc->displayname)-1] = 0;
+ }
+ else
+ {
+ /* This is not supposed to happen, but let's handle it anyway... */
+ http_decode( cmd[4] );
+ serv_buddy_rename( gc, cmd[3], cmd[4] );
+ }
+ }
+ else if( strcmp( cmd[0], "IPG" ) == 0 )
+ {
+ do_error_dialog( gc, "Received IPG command, we don't handle them yet.", "MSN" );
+
+ md->handler->msglen = atoi( cmd[1] );
+
+ if( md->handler->msglen <= 0 )
+ {
+ hide_login_progress_error( gc, "Syntax error" );
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else if( isdigit( cmd[0][0] ) )
+ {
+ int num = atoi( cmd[0] );
+ struct msn_status_code *err = msn_status_by_number( num );
+
+ g_snprintf( buf, sizeof( buf ), "Error reported by MSN server: %s", err->text );
+ do_error_dialog( gc, buf, "MSN" );
+
+ if( err->flags & STATUS_FATAL )
+ {
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else
+ {
+ debug( "Received unknown command from main server: %s", cmd[0] );
+ }
+
+ return( 1 );
+}
+
+static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+{
+ struct gaim_connection *gc = data;
+ char *body;
+ int blen = 0;
+
+ if( !num_parts )
+ return( 1 );
+
+ if( ( body = strstr( msg, "\r\n\r\n" ) ) )
+ {
+ body += 4;
+ blen = msglen - ( body - msg );
+ }
+
+ if( strcmp( cmd[0], "MSG" ) == 0 )
+ {
+ if( g_strcasecmp( cmd[1], "Hotmail" ) == 0 )
+ {
+ char *ct = msn_findheader( msg, "Content-Type:", msglen );
+
+ if( !ct )
+ return( 1 );
+
+ if( g_strncasecmp( ct, "application/x-msmsgssystemmessage", 33 ) == 0 )
+ {
+ char *mtype;
+ char *arg1;
+
+ if( !body )
+ return( 1 );
+
+ mtype = msn_findheader( body, "Type:", blen );
+ arg1 = msn_findheader( body, "Arg1:", blen );
+
+ if( mtype && strcmp( mtype, "1" ) == 0 )
+ {
+ if( arg1 )
+ serv_got_crap( gc, "The server is going down for maintenance in %s minutes.", arg1 );
+ }
+
+ if( arg1 ) g_free( arg1 );
+ if( mtype ) g_free( mtype );
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
+ {
+ /* We don't care about this profile for now... */
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
+ {
+ char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
+ char *folders = msn_findheader( body, "Folders-Unread:", blen );
+
+ if( inbox && folders )
+ {
+ serv_got_crap( gc, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+ }
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
+ {
+ char *from = msn_findheader( body, "From-Addr:", blen );
+ char *fromname = msn_findheader( body, "From:", blen );
+
+ if( from && fromname )
+ {
+ serv_got_crap( gc, "Received an e-mail message from %s <%s>.", fromname, from );
+ }
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 )
+ {
+ /* Sorry, but this one really is *USELESS* */
+ }
+ else
+ {
+ debug( "Can't handle %s packet from notification server", ct );
+ }
+
+ g_free( ct );
+ }
+ }
+
+ return( 1 );
+}
+
+static void msn_auth_got_passport_id( struct passport_reply *rep )
+{
+ struct gaim_connection *gc = rep->data;
+ struct msn_data *md = gc->proto_data;
+ char *key = rep->result;
+ char buf[1024];
+
+ if( key == NULL )
+ {
+ hide_login_progress( gc, "Error during Passport authentication" );
+ signoff( gc );
+ }
+ else
+ {
+ g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
+ msn_write( gc, buf, strlen( buf ) );
+ }
+}
diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c
new file mode 100644
index 00000000..640126a0
--- /dev/null
+++ b/protocols/msn/passport.c
@@ -0,0 +1,313 @@
+/* passport.c
+ *
+ * Functions to login to microsoft passport service for Messenger
+ * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>
+ * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that is will be useful,
+ * bit WITHOU ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "ssl_client.h"
+#include "passport.h"
+#include "msn.h"
+#include "bitlbee.h"
+#include <ctype.h>
+#include <errno.h>
+
+#define MSN_BUF_LEN 8192
+
+static char *prd_cached = NULL;
+
+static char *passport_create_header( char *reply, char *email, char *pwd );
+static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
+static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond );
+static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url );
+static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond );
+static void destroy_reply( struct passport_reply *rep );
+
+
+int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func )
+{
+ char *header = passport_create_header( cookie, username, password );
+
+ if( prd_cached )
+ {
+ int st;
+
+ st = passport_get_id_from( data, func, header, prd_cached );
+ g_free( header );
+ return( st );
+ }
+ else
+ {
+ return( passport_retrieve_dalogin( data, func, header ) );
+ }
+}
+
+
+static char *passport_create_header( char *reply, char *email, char *pwd )
+{
+ char *buffer = g_new0( char, 2048 );
+ char *currenttoken;
+ char *email_enc, *pwd_enc;
+
+ email_enc = g_new0( char, strlen( email ) * 3 + 1 );
+ strcpy( email_enc, email );
+ http_encode( email_enc );
+
+ pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
+ strcpy( pwd_enc, pwd );
+ http_encode( pwd_enc );
+
+ currenttoken = strstr( reply, "lc=" );
+ if( currenttoken == NULL )
+ return( NULL );
+
+ g_snprintf( buffer, 2048,
+ "Authorization: Passport1.4 OrgVerb=GET,"
+ "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
+ "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
+ currenttoken );
+
+ g_free( email_enc );
+ g_free( pwd_enc );
+
+ return( buffer );
+}
+
+
+static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header )
+{
+ struct passport_reply *rep = g_new0( struct passport_reply, 1 );
+ void *ssl;
+
+ rep->data = data;
+ rep->func = func;
+ rep->header = header;
+
+ ssl = ssl_connect( "nexus.passport.com", 443, passport_retrieve_dalogin_connected, rep );
+
+ if( !ssl )
+ destroy_reply( rep );
+
+ return( ssl != NULL );
+}
+
+#define PPR_BUFFERSIZE 2048
+#define PPR_REQUEST "GET /rdr/pprdr.asp HTTP/1.0\r\n\r\n"
+static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond )
+{
+ int ret;
+ char buffer[PPR_BUFFERSIZE+1];
+ struct passport_reply *rep = data;
+
+ if( !g_slist_find( msn_connections, rep->data ) )
+ {
+ if( ssl ) ssl_disconnect( ssl );
+ destroy_reply( rep );
+ return;
+ }
+
+ if( !ssl )
+ {
+ rep->func( rep );
+ destroy_reply( rep );
+ return;
+ }
+
+ ssl_write( ssl, PPR_REQUEST, strlen( PPR_REQUEST ) );
+
+ if( ( ret = ssl_read( ssl, buffer, PPR_BUFFERSIZE ) ) <= 0 )
+ {
+ goto failure;
+ }
+
+ {
+ char *dalogin = strstr( buffer, "DALogin=" );
+ char *urlend;
+
+ if( !dalogin )
+ goto failure;
+
+ dalogin += strlen( "DALogin=" );
+ urlend = strchr( dalogin, ',' );
+ if( urlend )
+ *urlend = 0;
+
+ /* strip the http(s):// part from the url */
+ urlend = strstr( urlend, "://" );
+ if( urlend )
+ dalogin = urlend + strlen( "://" );
+
+ if( prd_cached == NULL )
+ prd_cached = g_strdup( dalogin );
+ }
+
+ if( passport_get_id_from( rep->data, rep->func, rep->header, prd_cached ) )
+ {
+ ssl_disconnect( ssl );
+ destroy_reply( rep );
+ return;
+ }
+
+failure:
+ ssl_disconnect( ssl );
+ rep->func( rep );
+ destroy_reply( rep );
+}
+
+
+static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url )
+{
+ struct passport_reply *rep = g_new0( struct passport_reply, 1 );
+ char server[512], *dummy;
+ void *ssl;
+
+ rep->data = data;
+ rep->func = func;
+ rep->redirects = 4;
+
+ strncpy( server, url, 512 );
+ dummy = strchr( server, '/' );
+ if( dummy )
+ *dummy = 0;
+
+ ssl = ssl_connect( server, 443, passport_get_id_connected, rep );
+
+ if( ssl )
+ {
+ rep->header = g_strdup( header_i );
+ rep->url = g_strdup( url );
+ }
+ else
+ {
+ destroy_reply( rep );
+ }
+
+ return( ssl != NULL );
+}
+
+#define PPG_BUFFERSIZE 4096
+static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond )
+{
+ struct passport_reply *rep = data;
+ char server[512], buffer[PPG_BUFFERSIZE+1], *dummy;
+ int ret;
+
+ if( !g_slist_find( msn_connections, rep->data ) )
+ {
+ if( ssl ) ssl_disconnect( ssl );
+ destroy_reply( rep );
+ return;
+ }
+
+ if( !ssl )
+ {
+ rep->func( rep );
+ destroy_reply( rep );
+ return;
+ }
+
+ memset( buffer, 0, PPG_BUFFERSIZE + 1 );
+
+ strncpy( server, rep->url, 512 );
+ dummy = strchr( server, '/' );
+ if( dummy == NULL )
+ goto end;
+
+ g_snprintf( buffer, PPG_BUFFERSIZE - 1, "GET %s HTTP/1.0\r\n"
+ "%s\r\n\r\n", dummy, rep->header );
+
+ ssl_write( ssl, buffer, strlen( buffer ) );
+ memset( buffer, 0, PPG_BUFFERSIZE + 1 );
+
+ {
+ char *buffer2 = buffer;
+
+ while( ( ( ret = ssl_read( ssl, buffer2, 512 ) ) > 0 ) &&
+ ( buffer + PPG_BUFFERSIZE - buffer2 - ret - 512 >= 0 ) )
+ {
+ buffer2 += ret;
+ }
+ }
+
+ if( *buffer == 0 )
+ goto end;
+
+ if( ( dummy = strstr( buffer, "Location:" ) ) )
+ {
+ char *urlend;
+
+ rep->redirects --;
+ if( rep->redirects == 0 )
+ goto end;
+
+ dummy += strlen( "Location:" );
+ while( isspace( *dummy ) ) dummy ++;
+ urlend = dummy;
+ while( !isspace( *urlend ) ) urlend ++;
+ *urlend = 0;
+ if( ( urlend = strstr( dummy, "://" ) ) )
+ dummy = urlend + strlen( "://" );
+
+ g_free( rep->url );
+ rep->url = g_strdup( dummy );
+
+ strncpy( server, dummy, sizeof( server ) - 1 );
+ dummy = strchr( server, '/' );
+ if( dummy ) *dummy = 0;
+
+ ssl_disconnect( ssl );
+
+ if( ssl_connect( server, 443, passport_get_id_connected, rep ) )
+ {
+ return;
+ }
+ else
+ {
+ rep->func( rep );
+ destroy_reply( rep );
+ return;
+ }
+ }
+ else if( strstr( buffer, "200 OK" ) )
+ {
+ if( ( dummy = strstr( buffer, "from-PP='" ) ) )
+ {
+ char *responseend;
+
+ dummy += strlen( "from-PP='" );
+ responseend = strchr( dummy, '\'' );
+ if( responseend )
+ *responseend = 0;
+
+ rep->result = g_strdup( dummy );
+ }
+ }
+
+end:
+ ssl_disconnect( ssl );
+ rep->func( rep );
+ destroy_reply( rep );
+}
+
+
+static void destroy_reply( struct passport_reply *rep )
+{
+ if( rep->result ) g_free( rep->result );
+ if( rep->url ) g_free( rep->url );
+ if( rep->header ) g_free( rep->header );
+ if( rep ) g_free( rep );
+}
diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h
new file mode 100644
index 00000000..63fef2e9
--- /dev/null
+++ b/protocols/msn/passport.h
@@ -0,0 +1,47 @@
+#ifndef __PASSPORT_H__
+#define __PASSPORT_H__
+/* passport.h
+ *
+ * Functions to login to Microsoft Passport Service for Messenger
+ * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>,
+ * Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that is will be useful,
+ * bit WITHOU ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#include "nogaim.h"
+
+struct passport_reply
+{
+ void *data;
+ char *result;
+ void (*func)( struct passport_reply * );
+ char *url;
+ char *header;
+ int redirects;
+};
+
+int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func );
+
+#endif /* __PASSPORT_H__ */
diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c
new file mode 100644
index 00000000..38ead0bd
--- /dev/null
+++ b/protocols/msn/sb.c
@@ -0,0 +1,659 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module - Switchboard server callbacks and utilities */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <ctype.h>
+#include "nogaim.h"
+#include "msn.h"
+#include "passport.h"
+#include "md5.h"
+
+static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond );
+static int msn_sb_command( gpointer data, char **cmd, int num_parts );
+static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
+
+int msn_sb_write( struct msn_switchboard *sb, char *s, int len )
+{
+ int st;
+
+ st = write( sb->fd, s, len );
+ if( st != len )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ return( 1 );
+}
+
+struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session )
+{
+ struct msn_data *md = gc->proto_data;
+ struct msn_switchboard *sb = g_new0( struct msn_switchboard, 1 );
+
+ sb->fd = proxy_connect( host, port, msn_sb_connected, sb );
+ if( sb->fd < 0 )
+ {
+ g_free( sb );
+ return( NULL );
+ }
+
+ sb->gc = gc;
+ sb->key = g_strdup( key );
+ sb->session = session;
+
+ msn_switchboards = g_slist_append( msn_switchboards, sb );
+ md->switchboards = g_slist_append( md->switchboards, sb );
+
+ return( sb );
+}
+
+struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle )
+{
+ struct msn_data *md = gc->proto_data;
+ struct msn_switchboard *sb;
+ GSList *l;
+
+ for( l = md->switchboards; l; l = l->next )
+ {
+ sb = l->data;
+ if( sb->who && strcmp( sb->who, handle ) == 0 )
+ return( sb );
+ }
+
+ return( NULL );
+}
+
+struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id )
+{
+ struct msn_data *md = gc->proto_data;
+ struct msn_switchboard *sb;
+ GSList *l;
+
+ for( l = md->switchboards; l; l = l->next )
+ {
+ sb = l->data;
+ if( sb->chat && sb->chat->id == id )
+ return( sb );
+ }
+
+ return( NULL );
+}
+
+struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc )
+{
+ struct msn_data *md = gc->proto_data;
+ struct msn_switchboard *sb;
+ GSList *l;
+
+ for( l = md->switchboards; l; l = l->next )
+ {
+ sb = l->data;
+ if( !sb->who && !sb->chat )
+ return( sb );
+ }
+
+ return( NULL );
+}
+
+int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )
+{
+ if( sb->ready )
+ {
+ char cmd[1024], *buf;
+ int i, j;
+
+ if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 )
+ {
+ buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 );
+ i = strlen( MSN_MESSAGE_HEADERS );
+
+ strcpy( buf, MSN_MESSAGE_HEADERS );
+ for( j = 0; text[j]; j ++ )
+ {
+ if( text[j] == '\n' )
+ buf[i++] = '\r';
+
+ buf[i++] = text[j];
+ }
+ }
+ else
+ {
+ i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username );
+ buf = g_new0( char, strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username ) );
+ i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->gc->username );
+ }
+
+ g_snprintf( cmd, sizeof( cmd ), "MSG %d N %d\r\n", ++sb->trId, i );
+ if( msn_sb_write( sb, cmd, strlen( cmd ) ) && msn_sb_write( sb, buf, i ) )
+ {
+ g_free( buf );
+ return( 1 );
+ }
+ else
+ {
+ g_free( buf );
+ return( 0 );
+ }
+ }
+ else if( sb->who )
+ {
+ struct msn_message *m = g_new0( struct msn_message, 1 );
+
+ m->who = g_strdup( "" );
+ m->text = g_strdup( text );
+ sb->msgq = g_slist_append( sb->msgq, m );
+
+ return( 1 );
+ }
+ else
+ {
+ return( 0 );
+ }
+}
+
+void msn_sb_to_chat( struct msn_switchboard *sb )
+{
+ struct gaim_connection *gc = sb->gc;
+ char buf[1024];
+
+ /* Create the groupchat structure. */
+ g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
+ sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf );
+
+ /* Populate the channel. */
+ if( sb->who ) add_chat_buddy( sb->chat, sb->who );
+ add_chat_buddy( sb->chat, gc->username );
+
+ /* And make sure the switchboard doesn't look like a regular chat anymore. */
+ if( sb->who )
+ {
+ g_free( sb->who );
+ sb->who = NULL;
+ }
+}
+
+void msn_sb_destroy( struct msn_switchboard *sb )
+{
+ struct gaim_connection *gc = sb->gc;
+ struct msn_data *md = gc->proto_data;
+
+ debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" );
+
+ if( sb->key ) g_free( sb->key );
+ if( sb->who ) g_free( sb->who );
+
+ if( sb->msgq )
+ {
+ struct msn_message *m;
+ GSList *l;
+
+ for( l = sb->msgq; l; l = l->next )
+ {
+ m = l->data;
+ g_free( m->who );
+ g_free( m->text );
+ g_free( m );
+ }
+ g_slist_free( sb->msgq );
+
+ serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with unsent message(s), you'll have to resend them." );
+ }
+
+ if( sb->chat )
+ {
+ serv_got_chat_left( gc, sb->chat->id );
+ }
+
+ if( sb->handler )
+ {
+ if( sb->handler->rxq ) g_free( sb->handler->rxq );
+ if( sb->handler->cmd_text ) g_free( sb->handler->cmd_text );
+ g_free( sb->handler );
+ }
+
+ if( sb->inp ) gaim_input_remove( sb->inp );
+ closesocket( sb->fd );
+
+ msn_switchboards = g_slist_remove( msn_switchboards, sb );
+ md->switchboards = g_slist_remove( md->switchboards, sb );
+ g_free( sb );
+}
+
+void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct msn_switchboard *sb = data;
+ struct gaim_connection *gc;
+ struct msn_data *md;
+ char buf[1024];
+
+ /* Are we still alive? */
+ if( !g_slist_find( msn_switchboards, sb ) )
+ return;
+
+ gc = sb->gc;
+ md = gc->proto_data;
+
+ if( source != sb->fd )
+ {
+ debug( "ERROR %d while connecting to switchboard server", 1 );
+ msn_sb_destroy( sb );
+ return;
+ }
+
+ /* Prepare the callback */
+ sb->handler = g_new0( struct msn_handler_data, 1 );
+ sb->handler->fd = sb->fd;
+ sb->handler->rxq = g_new0( char, 1 );
+ sb->handler->data = sb;
+ sb->handler->exec_command = msn_sb_command;
+ sb->handler->exec_message = msn_sb_message;
+
+ if( sb->session == MSN_SB_NEW )
+ g_snprintf( buf, sizeof( buf ), "USR %d %s %s\r\n", ++sb->trId, gc->username, sb->key );
+ else
+ g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, gc->username, sb->key, sb->session );
+
+ if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ sb->inp = gaim_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb );
+ else
+ debug( "ERROR %d while connecting to switchboard server", 2 );
+}
+
+static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct msn_switchboard *sb = data;
+
+ if( msn_handler( sb->handler ) == -1 )
+ {
+ debug( "ERROR: Switchboard died" );
+ msn_sb_destroy( sb );
+ }
+}
+
+static int msn_sb_command( gpointer data, char **cmd, int num_parts )
+{
+ struct msn_switchboard *sb = data;
+ struct gaim_connection *gc = sb->gc;
+ char buf[1024];
+
+ if( !num_parts )
+ {
+ /* Hrrm... Empty command...? Ignore? */
+ return( 1 );
+ }
+
+ if( strcmp( cmd[0], "XFR" ) == 0 )
+ {
+ hide_login_progress_error( gc, "Received an XFR from a switchboard server, unable to comply! This is likely to be a bug, please report it!" );
+ signoff( gc );
+ return( 0 );
+ }
+ else if( strcmp( cmd[0], "USR" ) == 0 )
+ {
+ if( num_parts != 5 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ if( strcmp( cmd[2], "OK" ) != 0 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ if( sb->who )
+ {
+ g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who );
+ return( msn_sb_write( sb, buf, strlen( buf ) ) );
+ }
+ else
+ {
+ debug( "Just created a switchboard, but I don't know what to do with it." );
+ }
+ }
+ else if( strcmp( cmd[0], "IRO" ) == 0 )
+ {
+ int num, tot;
+
+ if( num_parts != 6 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ num = atoi( cmd[2] );
+ tot = atoi( cmd[3] );
+
+ if( tot <= 0 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+ else if( tot > 1 )
+ {
+ char buf[1024];
+
+ if( num == 1 )
+ {
+ g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
+ sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf );
+
+ g_free( sb->who );
+ sb->who = NULL;
+ }
+
+ add_chat_buddy( sb->chat, cmd[4] );
+
+ if( num == tot )
+ {
+ add_chat_buddy( sb->chat, gc->username );
+ }
+ }
+ }
+ else if( strcmp( cmd[0], "ANS" ) == 0 )
+ {
+ if( num_parts != 3 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ if( strcmp( cmd[2], "OK" ) != 0 )
+ {
+ debug( "Switchboard server sent a negative ANS reply" );
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ sb->ready = 1;
+ }
+ else if( strcmp( cmd[0], "CAL" ) == 0 )
+ {
+ if( num_parts != 4 || !isdigit( cmd[3][0] ) )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ sb->session = atoi( cmd[3] );
+ }
+ else if( strcmp( cmd[0], "JOI" ) == 0 )
+ {
+ if( num_parts != 3 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ if( sb->who && g_strcasecmp( cmd[1], sb->who ) == 0 )
+ {
+ /* The user we wanted to talk to is finally there, let's send the queued messages then. */
+ struct msn_message *m;
+ GSList *l;
+ int st = 1;
+
+ debug( "%s arrived in the switchboard session, now sending queued message(s)", cmd[1] );
+
+ /* Without this, sendmessage() will put everything back on the queue... */
+ sb->ready = 1;
+
+ while( ( l = sb->msgq ) )
+ {
+ m = l->data;
+ if( st )
+ {
+ /* This hack is meant to convert a regular new chat into a groupchat */
+ if( strcmp( m->text, GROUPCHAT_SWITCHBOARD_MESSAGE ) == 0 )
+ msn_sb_to_chat( sb );
+ else
+ st = msn_sb_sendmessage( sb, m->text );
+ }
+ g_free( m->text );
+ g_free( m->who );
+ g_free( m );
+
+ sb->msgq = g_slist_remove( sb->msgq, m );
+ }
+
+ return( st );
+ }
+ else if( sb->who )
+ {
+ debug( "Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1] );
+
+ /* This SB is a one-to-one chat right now, but someone else is joining. */
+ msn_sb_to_chat( sb );
+
+ add_chat_buddy( sb->chat, cmd[1] );
+ }
+ else if( sb->chat )
+ {
+ add_chat_buddy( sb->chat, cmd[1] );
+ sb->ready = 1;
+ }
+ else
+ {
+ /* PANIC! */
+ }
+ }
+ else if( strcmp( cmd[0], "MSG" ) == 0 )
+ {
+ if( num_parts != 4 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ sb->handler->msglen = atoi( cmd[3] );
+
+ if( sb->handler->msglen <= 0 )
+ {
+ debug( "Received a corrupted message on the switchboard, the switchboard will be closed" );
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+ }
+ else if( strcmp( cmd[0], "BYE" ) == 0 )
+ {
+ if( num_parts < 2 )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+
+ /* if( cmd[2] && *cmd[2] == '1' ) -=> Chat is being cleaned up because of idleness */
+
+ if( sb->who )
+ {
+ /* This is a single-person chat, and the other person is leaving. */
+ g_free( sb->who );
+ sb->who = NULL;
+ sb->ready = 0;
+
+ debug( "Person %s left the one-to-one switchboard connection. Keeping it around as a spare...", cmd[1] );
+
+ /* We could clean up the switchboard now, but keeping it around
+ as a spare for a next conversation sounds more sane to me.
+ The server will clean it up when it's idle for too long. */
+ }
+ else if( sb->chat )
+ {
+ remove_chat_buddy( sb->chat, cmd[1], "" );
+ }
+ else
+ {
+ /* PANIC! */
+ }
+ }
+ else if( isdigit( cmd[0][0] ) )
+ {
+ int num = atoi( cmd[0] );
+ struct msn_status_code *err = msn_status_by_number( num );
+
+ g_snprintf( buf, sizeof( buf ), "Error reported by switchboard server: %s", err->text );
+ do_error_dialog( gc, buf, "MSN" );
+
+ if( err->flags & STATUS_SB_FATAL )
+ {
+ msn_sb_destroy( sb );
+ return( 0 );
+ }
+ else if( err->flags & STATUS_FATAL )
+ {
+ signoff( gc );
+ return( 0 );
+ }
+ }
+ else
+ {
+ debug( "Received unknown command from switchboard server: %s", cmd[0] );
+ }
+
+ return( 1 );
+}
+
+static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+{
+ struct msn_switchboard *sb = data;
+ struct gaim_connection *gc = sb->gc;
+ char *body;
+ int blen = 0;
+
+ if( !num_parts )
+ return( 1 );
+
+ if( ( body = strstr( msg, "\r\n\r\n" ) ) )
+ {
+ body += 4;
+ blen = msglen - ( body - msg );
+ }
+
+ if( strcmp( cmd[0], "MSG" ) == 0 )
+ {
+ char *ct = msn_findheader( msg, "Content-Type:", msglen );
+
+ if( !ct )
+ return( 1 );
+
+ if( g_strncasecmp( ct, "text/plain", 10 ) == 0 )
+ {
+ g_free( ct );
+
+ if( !body )
+ return( 1 );
+
+ if( sb->who )
+ {
+ serv_got_im( gc, cmd[1], body, 0, 0, blen );
+ }
+ else if( sb->chat )
+ {
+ serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, body, 0 );
+ }
+ else
+ {
+ /* PANIC! */
+ }
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )
+ {
+ char *itype = msn_findheader( body, "Application-GUID:", blen );
+ char buf[1024];
+
+ g_free( ct );
+
+ *buf = 0;
+
+ if( !itype )
+ return( 1 );
+
+ /* File transfer. */
+ if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 )
+ {
+ char *name = msn_findheader( body, "Application-File:", blen );
+ char *size = msn_findheader( body, "Application-FileSize:", blen );
+
+ if( name && size )
+ {
+ g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n"
+ "Filetransfers are not supported by BitlBee for now...", name, size );
+ }
+ else
+ {
+ strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" );
+ }
+
+ if( name ) g_free( name );
+ if( size ) g_free( size );
+ }
+ else
+ {
+ char *iname = msn_findheader( body, "Application-Name:", blen );
+
+ g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>",
+ itype, iname ? iname : "no name" );
+
+ if( iname ) g_free( iname );
+ }
+
+ g_free( itype );
+
+ if( !*buf )
+ return( 1 );
+
+ if( sb->who )
+ {
+ serv_got_im( gc, cmd[1], buf, 0, 0, strlen( buf ) );
+ }
+ else if( sb->chat )
+ {
+ serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, buf, 0 );
+ }
+ else
+ {
+ /* PANIC! */
+ }
+ }
+ else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )
+ {
+ char *who = msn_findheader( msg, "TypingUser:", msglen );
+
+ if( who )
+ {
+ serv_got_typing( gc, who, 5 );
+ g_free( who );
+ }
+
+ g_free( ct );
+ }
+ else
+ {
+ g_free( ct );
+ }
+ }
+
+ return( 1 );
+}
diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c
new file mode 100644
index 00000000..0cd80c01
--- /dev/null
+++ b/protocols/msn/tables.c
@@ -0,0 +1,166 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* MSN module - Some tables with useful data */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+
+struct msn_away_state msn_away_state_list[] =
+{
+ { 0, "NLN", "Available" },
+ { 1, "BSY", "Busy" },
+ { 3, "IDL", "Idle" },
+ { 5, "BRB", "Be Right Back" },
+ { 7, "AWY", "Away" },
+ { 9, "PHN", "On the Phone" },
+ { 11, "LUN", "Out to Lunch" },
+ { 13, "HDN", "Hidden" },
+ { -1, "", "" }
+};
+
+struct msn_away_state *msn_away_state_by_number( int number )
+{
+ int i;
+
+ for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ if( msn_away_state_list[i].number == number )
+ return( msn_away_state_list + i );
+
+ return( NULL );
+}
+
+struct msn_away_state *msn_away_state_by_code( char *code )
+{
+ int i;
+
+ for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )
+ return( msn_away_state_list + i );
+
+ return( NULL );
+}
+
+struct msn_away_state *msn_away_state_by_name( char *name )
+{
+ int i;
+
+ for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+ if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )
+ return( msn_away_state_list + i );
+
+ return( NULL );
+}
+
+struct msn_status_code msn_status_code_list[] =
+{
+ { 200, "Invalid syntax", 0 },
+ { 201, "Invalid parameter", 0 },
+ { 205, "Invalid (non-existent) handle", 0 },
+ { 206, "Domain name missing", 0 },
+ { 207, "Already logged in", 0 },
+ { 208, "Invalid handle", 0 },
+ { 209, "Forbidden nickname", 0 },
+ { 210, "Buddy list too long", 0 },
+ { 215, "Handle is already in list", 0 },
+ { 216, "Handle is not in list", 0 },
+ { 217, "Person is off-line or non-existent", 0 },
+ { 218, "Already in that mode", 0 },
+ { 219, "Handle is already in opposite list", 0 },
+ { 223, "Too many groups", 0 },
+ { 224, "Invalid group or already in list", 0 },
+ { 225, "Handle is not in that group", 0 },
+ { 229, "Group name too long", 0 },
+ { 230, "Cannot remove that group", 0 },
+ { 231, "Invalid group", 0 },
+ { 280, "Switchboard failed", STATUS_SB_FATAL },
+ { 281, "Transfer to switchboard failed", 0 },
+
+ { 300, "Required field missing", 0 },
+ { 302, "Not logged in", 0 },
+
+ { 500, "Internal server error/Account banned", STATUS_FATAL },
+ { 501, "Database server error", STATUS_FATAL },
+ { 502, "Command disabled", 0 },
+ { 510, "File operation failed", STATUS_FATAL },
+ { 520, "Memory allocation failed", STATUS_FATAL },
+ { 540, "Challenge response invalid", STATUS_FATAL },
+
+ { 600, "Server is busy", STATUS_FATAL },
+ { 601, "Server is unavailable", STATUS_FATAL },
+ { 602, "Peer nameserver is down", STATUS_FATAL },
+ { 603, "Database connection failed", STATUS_FATAL },
+ { 604, "Server is going down", STATUS_FATAL },
+ { 605, "Server is unavailable", STATUS_FATAL },
+
+ { 700, "Could not create connection", STATUS_FATAL },
+ { 710, "Invalid CVR parameters", STATUS_FATAL },
+ { 711, "Write is blocking", STATUS_FATAL },
+ { 712, "Session is overloaded", STATUS_FATAL },
+ { 713, "Calling too rapidly", 0 },
+ { 714, "Too many sessions", STATUS_FATAL },
+ { 715, "Not expected/Invalid argument/action", 0 },
+ { 717, "Bad friend file", STATUS_FATAL },
+ { 731, "Not expected/Invalid argument", 0 },
+
+ { 800, "Changing too rapidly", 0 },
+
+ { 910, "Server is busy", STATUS_FATAL },
+ { 911, "Authentication failed", STATUS_FATAL },
+ { 912, "Server is busy", STATUS_FATAL },
+ { 913, "Not allowed when hiding", 0 },
+ { 914, "Server is unavailable", STATUS_FATAL },
+ { 915, "Server is unavailable", STATUS_FATAL },
+ { 916, "Server is unavailable", STATUS_FATAL },
+ { 917, "Authentication failed", STATUS_FATAL },
+ { 918, "Server is busy", STATUS_FATAL },
+ { 919, "Server is busy", STATUS_FATAL },
+ { 920, "Not accepting new principals", 0 }, /* When a sb is full? */
+ { 922, "Server is busy", STATUS_FATAL },
+ { 923, "Kids Passport without parental consent", STATUS_FATAL },
+ { 924, "Passport account not yet verified", STATUS_FATAL },
+ { 928, "Bad ticket", STATUS_FATAL },
+ { -1, NULL, 0 }
+};
+
+struct msn_status_code *msn_status_by_number( int number )
+{
+ static struct msn_status_code *unknown = NULL;
+ int i;
+
+ for( i = 0; msn_status_code_list[i].number >= 0; i ++ )
+ if( msn_status_code_list[i].number == number )
+ return( msn_status_code_list + i );
+
+ if( unknown == NULL )
+ {
+ unknown = g_new0( struct msn_status_code, 1 );
+ unknown->text = g_new0( char, 128 );
+ }
+
+ unknown->number = number;
+ unknown->flags = 0;
+ g_snprintf( unknown->text, 128, "Unknown error (%d)", number );
+
+ return( unknown );
+}
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
new file mode 100644
index 00000000..97aa30cf
--- /dev/null
+++ b/protocols/nogaim.c
@@ -0,0 +1,1082 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * nogaim
+ *
+ * Gaim without gaim - for BitlBee
+ *
+ * This file contains functions called by the Gaim IM-modules. It's written
+ * from scratch for BitlBee and doesn't contain any code from Gaim anymore
+ * (except for the function names).
+ *
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define BITLBEE_CORE
+#include "nogaim.h"
+#include <ctype.h>
+#include <iconv.h>
+
+struct prpl *proto_prpl[PROTO_MAX];
+char proto_name[PROTO_MAX][8] = { "TOC", "OSCAR", "YAHOO", "ICQ", "MSN", "", "", "", "JABBER", "", "", "", "", "", "", "" };
+
+static char *proto_away_alias[7][5] =
+{
+ { "Away from computer", "Away", "Extended away", NULL },
+ { "NA", "N/A", "Not available", NULL },
+ { "Busy", "Do not disturb", "DND", "Occupied", NULL },
+ { "Be right back", "BRB", NULL },
+ { "On the phone", "Phone", "On phone", NULL },
+ { "Out to lunch", "Lunch", "Food", NULL },
+ { NULL }
+};
+static char *proto_away_alias_find( GList *gcm, char *away );
+
+static int remove_chat_buddy_silent( struct conversation *b, char *handle );
+
+GSList *connections;
+
+
+/* nogaim.c */
+
+void nogaim_init()
+{
+ proto_prpl[PROTO_MSN] = g_new0 ( struct prpl, 1 );
+#ifdef WITH_MSN
+ msn_init( proto_prpl[PROTO_MSN] );
+#endif
+
+ proto_prpl[PROTO_OSCAR] = g_new0( struct prpl, 1 );
+#ifdef WITH_OSCAR
+ oscar_init( proto_prpl[PROTO_OSCAR] );
+#endif
+
+ proto_prpl[PROTO_YAHOO] = g_new0( struct prpl, 1 );
+#ifdef WITH_YAHOO
+ byahoo_init( proto_prpl[PROTO_YAHOO] );
+#endif
+
+ proto_prpl[PROTO_JABBER] = g_new0( struct prpl, 1 );
+#ifdef WITH_JABBER
+ jabber_init( proto_prpl[PROTO_JABBER] );
+#endif
+}
+
+GSList *get_connections() { return connections; }
+
+int proto_away( struct gaim_connection *gc, char *away )
+{
+ GList *m, *ms;
+ char *s;
+
+ if( !away ) away = "";
+ ms = m = gc->prpl->away_states( gc );
+
+ while( m )
+ {
+ if( *away )
+ {
+ if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+ break;
+ }
+ else
+ {
+ if( g_strcasecmp( m->data, "Available" ) == 0 )
+ break;
+ if( g_strcasecmp( m->data, "Online" ) == 0 )
+ break;
+ }
+ m = m->next;
+ }
+
+ if( m )
+ {
+ gc->prpl->set_away( gc, m->data, *away ? away : NULL );
+ }
+ else
+ {
+ s = proto_away_alias_find( ms, away );
+ if( s )
+ {
+ gc->prpl->set_away( gc, s, away );
+ if( set_getint( gc->irc, "debug" ) )
+ irc_usermsg( gc->irc, "Setting away state for %s to %s", proto_name[gc->protocol], s );
+ }
+ else
+ gc->prpl->set_away( gc, GAIM_AWAY_CUSTOM, away );
+ }
+
+ g_list_free( ms );
+
+ return( 1 );
+}
+
+static char *proto_away_alias_find( GList *gcm, char *away )
+{
+ GList *m;
+ int i, j;
+
+ for( i = 0; *proto_away_alias[i]; i ++ )
+ {
+ for( j = 0; proto_away_alias[i][j]; j ++ )
+ if( g_strncasecmp( away, proto_away_alias[i][j], strlen( proto_away_alias[i][j] ) ) == 0 )
+ break;
+
+ if( !proto_away_alias[i][j] ) /* If we reach the end, this row */
+ continue; /* is not what we want. Next! */
+
+ /* Now find an entry in this row which exists in gcm */
+ for( j = 0; proto_away_alias[i][j]; j ++ )
+ {
+ m = gcm;
+ while( m )
+ {
+ if( g_strcasecmp( proto_away_alias[i][j], m->data ) == 0 )
+ return( proto_away_alias[i][j] );
+ m = m->next;
+ }
+ }
+ }
+
+ return( NULL );
+}
+
+/* Compare two handles for a specific protocol. For most protocols,
+ g_strcasecmp is okay, but for AIM, for example, it's not. This really
+ should be a compare function inside the PRPL module, but I do it this
+ way for now because I don't want to touch the Gaim code too much since
+ it's not going to be here for too long anymore. */
+int handle_cmp( char *a, char *b, int protocol )
+{
+ if( protocol == PROTO_TOC || protocol == PROTO_ICQ )
+ {
+ /* AIM, being teh evil, thinks it's cool that users can put
+ random spaces in screennames. But "A B" and "AB" are
+ equal. Hrmm, okay. */
+ while( 1 )
+ {
+ while( *a == ' ' ) a ++;
+ while( *b == ' ' ) b ++;
+
+ if( *a && *b )
+ {
+ if( tolower( *a ) != tolower( *b ) )
+ return( 1 );
+ }
+ else if( *a || *b )
+ return( 1 );
+ else
+ return( 0 );
+
+ a ++;
+ b ++;
+ }
+ }
+ else
+ {
+ return( g_strcasecmp( a, b ) );
+ }
+}
+
+
+/* multi.c */
+
+struct gaim_connection *new_gaim_conn( struct aim_user *user )
+{
+ struct gaim_connection *gc;
+ account_t *a;
+
+ gc = g_new0( struct gaim_connection, 1 );
+
+ gc->protocol = user->protocol;
+ gc->prpl = proto_prpl[gc->protocol];
+ g_snprintf( gc->username, sizeof( gc->username ), "%s", user->username );
+ g_snprintf( gc->password, sizeof( gc->password ), "%s", user->password );
+ /* [MD] BUGFIX: don't set gc->irc to the global IRC, but use the one from the struct aim_user.
+ * This fixes daemon mode breakage where IRC doesn't point to the currently active connection.
+ */
+ gc->irc=user->irc;
+
+ connections = g_slist_append( connections, gc );
+
+ user->gc = gc;
+ gc->user = user;
+
+ // Find the account_t so we can set its gc pointer
+ for( a = gc->irc->accounts; a; a = a->next )
+ if( ( struct aim_user * ) a->gc == user )
+ {
+ a->gc = gc;
+ break;
+ }
+
+ return( gc );
+}
+
+void destroy_gaim_conn( struct gaim_connection *gc )
+{
+ account_t *a;
+
+ /* Destroy the pointer to this connection from the account list */
+ for( a = gc->irc->accounts; a; a = a->next )
+ if( a->gc == gc )
+ {
+ a->gc = NULL;
+ break;
+ }
+
+ connections = g_slist_remove( connections, gc );
+ g_free( gc->user );
+ g_free( gc );
+}
+
+void set_login_progress( struct gaim_connection *gc, int step, char *msg )
+{
+ irc_usermsg( gc->irc, "%s(%s) - Logging in: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+/* Errors *while* logging in */
+void hide_login_progress( struct gaim_connection *gc, char *msg )
+{
+ irc_usermsg( gc->irc, "%s(%s) - Login error: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+/* Errors *after* logging in */
+void hide_login_progress_error( struct gaim_connection *gc, char *msg )
+{
+ irc_usermsg( gc->irc, "%s(%s) - Logged out: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+void serv_got_crap( struct gaim_connection *gc, char *format, ... )
+{
+ va_list params;
+ char text[1024], buf[1024];
+ char *msg;
+
+ va_start( params, format );
+ g_vsnprintf( text, sizeof( text ), format, params );
+ va_end( params );
+
+ if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( "UTF8", set_getstr( gc->irc, "charset" ), text, buf, 0, 1024 ) != -1 )
+ msg = buf;
+ else
+ msg = text;
+
+ /* if( g_strcasecmp( set_getstr(gc->irc, "html" ), "strip" ) == 0 ) */
+ if( gc->flags & OPT_CONN_HTML )
+ strip_html( msg );
+
+ irc_usermsg( gc->irc, "%s(%s) - %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+static gboolean send_keepalive( gpointer d )
+{
+ struct gaim_connection *gc = d;
+
+ if( gc->prpl && gc->prpl->keepalive )
+ gc->prpl->keepalive( gc );
+
+ return TRUE;
+}
+
+void account_online( struct gaim_connection *gc )
+{
+ user_t *u;
+
+ /* MSN servers sometimes redirect you to a different server and do
+ the whole login sequence again, so subsequent calls to this
+ function should be handled correctly. (IOW, ignored) */
+ if( gc->flags & OPT_LOGGED_IN )
+ return;
+
+ u = user_find( gc->irc, gc->irc->nick );
+
+ irc_usermsg( gc->irc, "%s(%s) - Logged in", proto_name[gc->protocol], gc->username );
+
+ gc->keepalive = g_timeout_add( 60000, send_keepalive, gc );
+ gc->flags |= OPT_LOGGED_IN;
+
+ if( u && u->away ) proto_away( gc, u->away );
+
+ if( gc->protocol == PROTO_ICQ )
+ {
+ for( u = gc->irc->users; u; u = u->next )
+ if( u->gc == gc )
+ break;
+
+ if( u == NULL )
+ irc_usermsg( gc->irc, "\x02""***\x02"" BitlBee now supports ICQ server-side contact lists. "
+ "See \x02""help import_buddies\x02"" for more information." );
+ }
+}
+
+gboolean auto_reconnect( gpointer data )
+{
+ account_t *a = data;
+
+ a->reconnect = 0;
+ account_on( a->irc, a );
+
+ return( FALSE ); /* Only have to run the timeout once */
+}
+
+void cancel_auto_reconnect( account_t *a )
+{
+ while( g_source_remove_by_user_data( (gpointer) a ) );
+ a->reconnect = 0;
+}
+
+void account_offline( struct gaim_connection *gc )
+{
+ gc->wants_to_die = TRUE;
+ signoff( gc );
+}
+
+void signoff( struct gaim_connection *gc )
+{
+ irc_t *irc = gc->irc;
+ user_t *t, *u = irc->users;
+ account_t *a;
+
+ irc_usermsg( gc->irc, "%s(%s) - Signing off..", proto_name[gc->protocol], gc->username );
+
+ gaim_input_remove( gc->keepalive );
+ gc->keepalive = 0;
+ gc->prpl->close( gc );
+ gaim_input_remove( gc->inpa );
+
+ while( u )
+ {
+ if( u->gc == gc )
+ {
+ t = u->next;
+ user_del( irc, u->nick );
+ u = t;
+ }
+ else
+ u = u->next;
+ }
+
+ query_del_by_gc( gc->irc, gc );
+
+ for( a = irc->accounts; a; a = a->next )
+ if( a->gc == gc )
+ break;
+
+ if( !a )
+ {
+ /* Uhm... This is very sick. */
+ }
+ else if( !gc->wants_to_die && set_getint( irc, "auto_reconnect" ) )
+ {
+ int delay = set_getint( irc, "auto_reconnect_delay" );
+ irc_usermsg( gc->irc, "%s(%s) - Reconnecting in %d seconds..", proto_name[gc->protocol], gc->username, delay);
+
+ a->reconnect = 1;
+ g_timeout_add( delay * 1000, auto_reconnect, a );
+ }
+
+ destroy_gaim_conn( gc );
+}
+
+
+/* dialogs.c */
+
+void do_error_dialog( struct gaim_connection *gc, char *msg, char *title )
+{
+ irc_usermsg( gc->irc, "%s(%s) - Error: %s", gc->username, title, msg );
+}
+
+void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont )
+{
+ query_add( gc->irc, gc, msg, doit, dont, data );
+}
+
+
+/* list.c */
+
+int bud_list_cache_exists( struct gaim_connection *gc )
+{
+ return( 0 );
+}
+
+void do_import( struct gaim_connection *gc, void *null )
+{
+ return;
+}
+
+void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname )
+{
+ user_t *u;
+ char nick[MAX_NICK_LENGTH+1];
+ char *s;
+ irc_t *irc = gc->irc;
+
+ if( set_getint( irc, "debug" ) && 0 ) /* This message is too useless */
+ irc_usermsg( irc, "Receiving user add from protocol: %s", handle );
+
+ if( user_findhandle( gc, handle ) )
+ {
+ if( set_getint( irc, "debug" ) )
+ irc_usermsg( irc, "User already exists, ignoring add request: %s", handle );
+
+ return;
+
+ /* Buddy seems to exist already. Let's ignore this request then... */
+ }
+
+ memset( nick, 0, MAX_NICK_LENGTH + 1 );
+ strcpy( nick, nick_get( gc->irc, handle, gc->protocol, realname ) );
+
+ u = user_add( gc->irc, nick );
+
+ if( !realname || !*realname ) realname = nick;
+ u->realname = g_strdup( realname );
+
+ if( ( s = strchr( handle, '@' ) ) )
+ {
+ u->host = g_strdup( s + 1 );
+ u->user = g_strndup( handle, s - handle );
+ }
+ else if( gc->user->proto_opt[0] && *gc->user->proto_opt[0] )
+ {
+ u->host = g_strdup( gc->user->proto_opt[0] );
+ u->user = g_strdup( handle );
+
+ /* s/ /_/ ... important for AOL screennames */
+ for( s = u->user; *s; s ++ )
+ if( *s == ' ' )
+ *s = '_';
+ }
+ else
+ {
+ u->host = g_strdup( proto_name[gc->user->protocol] );
+ u->user = g_strdup( handle );
+ }
+
+ u->gc = gc;
+ u->handle = g_strdup( handle );
+ u->send_handler = buddy_send_handler;
+ u->last_typing_notice = 0;
+}
+
+struct buddy *find_buddy( struct gaim_connection *gc, char *handle )
+{
+ static struct buddy b[1];
+ user_t *u;
+
+ u = user_findhandle( gc, handle );
+
+ if( !u )
+ return( NULL );
+
+ memset( b, 0, sizeof( b ) );
+ strncpy( b->name, handle, 80 );
+ strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN );
+ b->present = u->online;
+ b->gc = u->gc;
+
+ return( b );
+}
+
+void do_export( struct gaim_connection *gc )
+{
+ return;
+}
+
+void signoff_blocked( struct gaim_connection *gc )
+{
+ return; /* Make all blocked users look invisible (TODO?) */
+}
+
+
+void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname )
+{
+ user_t *u = user_findhandle( gc, handle );
+ char *name, buf[1024];
+
+ if( !u ) return;
+
+ /* Convert all UTF-8 */
+ if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), realname, buf, 0, sizeof( buf ) ) != -1 )
+ name = buf;
+ else
+ name = realname;
+
+ if( g_strcasecmp( u->realname, name ) != 0 )
+ {
+ if( u->realname != u->nick ) g_free( u->realname );
+
+ u->realname = g_strdup( name );
+
+ if( ( gc->flags & OPT_LOGGED_IN ) && set_getint( gc->irc, "display_namechanges" ) )
+ irc_usermsg( gc->irc, "User `%s' changed name to `%s'", u->nick, u->realname );
+ }
+}
+
+
+/* prpl.c */
+
+void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg )
+{
+ return;
+}
+
+
+/* server.c */
+
+void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps )
+{
+ user_t *u;
+ int oa, oo;
+
+ u = user_findhandle( gc, handle );
+
+ if( !u )
+ {
+ if( g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "add" ) == 0 )
+ {
+ add_buddy( gc, NULL, handle, NULL );
+ u = user_findhandle( gc, handle );
+ }
+ else
+ {
+ if( set_getint( gc->irc, "debug" ) || g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "ignore" ) != 0 )
+ {
+ irc_usermsg( gc->irc, "serv_got_update() for handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username );
+ irc_usermsg( gc->irc, "loggedin = %d, type = %d", loggedin, type );
+ }
+
+ return;
+ }
+ return;
+ }
+
+ oa = u->away != NULL;
+ oo = u->online;
+
+ if( u->away )
+ {
+ g_free( u->away );
+ u->away = NULL;
+ }
+
+ if( loggedin && !u->online )
+ {
+ irc_spawn( gc->irc, u );
+ u->online = 1;
+ }
+ else if( !loggedin && u->online )
+ {
+ struct conversation *c;
+
+ irc_kill( gc->irc, u );
+ u->online = 0;
+
+ /* Remove him/her from the conversations to prevent PART messages after he/she QUIT already */
+ for( c = gc->conversations; c; c = c->next )
+ remove_chat_buddy_silent( c, handle );
+ }
+
+ if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_OSCAR || gc->protocol == PROTO_TOC ) )
+ {
+ u->away = g_strdup( "Away" );
+ }
+ else if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_JABBER ) )
+ {
+ if( type & UC_DND )
+ u->away = g_strdup( "Do Not Disturb" );
+ else if( type & UC_XA )
+ u->away = g_strdup( "Extended Away" );
+ else // if( type & UC_AWAY )
+ u->away = g_strdup( "Away" );
+ }
+ else if( ( type & UC_UNAVAILABLE ) && gc->prpl->get_status_string )
+ {
+ u->away = g_strdup( gc->prpl->get_status_string( gc, type ) );
+ }
+ else
+ u->away = NULL;
+
+ /* LISPy... */
+ if( ( set_getint( gc->irc, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */
+ ( u->online ) && /* Don't touch offline people */
+ ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */
+ ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */
+ {
+ irc_write( gc->irc, ":%s!%s@%s MODE %s %cv %s", gc->irc->mynick, gc->irc->mynick, gc->irc->myhost,
+ gc->irc->channel, u->away?'-':'+', u->nick );
+ }
+}
+
+void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len )
+{
+ irc_t *irc = gc->irc;
+ user_t *u;
+ char buf[8192];
+
+ u = user_findhandle( gc, handle );
+
+ if( !u )
+ {
+ char *h = set_getstr( irc, "handle_unknown" );
+
+ if( g_strcasecmp( h, "ignore" ) == 0 )
+ {
+ if( set_getint( irc, "debug" ) )
+ irc_usermsg( irc, "Ignoring message from unknown handle %s on connection %s(%s)", handle, proto_name[gc->protocol], gc->username );
+
+ return;
+ }
+ else if( g_strncasecmp( h, "add", 3 ) == 0 )
+ {
+ int private = set_getint( irc, "private" );
+
+ if( h[3] )
+ {
+ if( g_strcasecmp( h + 3, "_private" ) == 0 )
+ private = 1;
+ else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
+ private = 0;
+ }
+
+ add_buddy( gc, NULL, handle, NULL );
+ u = user_findhandle( gc, handle );
+ u->is_private = private;
+ }
+ else
+ {
+ irc_usermsg( irc, "Message from unknown handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username );
+ u = user_find( irc, irc->mynick );
+ }
+ }
+
+ /* if( g_strcasecmp( set_getstr( irc, "html" ), "strip" ) == 0 ) */
+ if( gc->flags & OPT_CONN_HTML )
+ strip_html( msg );
+
+ if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( "UTF-8", set_getstr( irc, "charset" ), msg, buf, 0, 8192 ) != -1 )
+ msg = buf;
+
+ while( strlen( msg ) > 450 )
+ {
+ char tmp, *nl;
+
+ tmp = msg[450];
+ msg[450] = 0;
+
+ /* If there's a newline in this string, split up there so we're not
+ going to split up lines. If there isn't a newline, well, too bad. */
+ if( ( nl = strrchr( msg, '\n' ) ) )
+ *nl = 0;
+
+ irc_msgfrom( irc, u->nick, msg );
+
+ msg[450] = tmp;
+
+ /* Move on. */
+ if( nl )
+ {
+ *nl = '\n';
+ msg = nl + 1;
+ }
+ else
+ {
+ msg += 450;
+ }
+ }
+ irc_msgfrom( irc, u->nick, msg );
+}
+
+void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout )
+{
+ user_t *u;
+
+ if( !set_getint( gc->irc, "typing_notice" ) )
+ return;
+
+ if( ( u = user_findhandle( gc, handle ) ) )
+ irc_noticefrom( gc->irc, u->nick, "* Typing a message *" );
+}
+
+void serv_got_chat_left( struct gaim_connection *gc, int id )
+{
+ struct conversation *c, *l = NULL;
+ GList *ir;
+
+ if( set_getint( gc->irc, "debug" ) )
+ irc_usermsg( gc->irc, "You were removed from conversation %d", (int) id );
+
+ for( c = gc->conversations; c && c->id != id; c = (l=c)->next );
+
+ if( c )
+ {
+ if( c->joined )
+ {
+ user_t *u, *r;
+
+ r = user_find( gc->irc, gc->irc->mynick );
+ irc_privmsg( gc->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
+
+ u = user_find( gc->irc, gc->irc->nick );
+ irc_kick( gc->irc, u, c->channel, r );
+ /* irc_part( gc->irc, u, c->channel ); */
+ }
+
+ if( l )
+ l->next = c->next;
+ else
+ gc->conversations = c->next;
+
+ for( ir = c->in_room; ir; ir = ir->next )
+ g_free( ir->data );
+ g_list_free( c->in_room );
+ g_free( c->channel );
+ g_free( c->title );
+ g_free( c );
+ }
+}
+
+void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime )
+{
+ struct conversation *c;
+ user_t *u;
+ char buf[8192];
+
+ /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
+ if( g_strcasecmp( who, gc->user->username ) == 0 )
+ return;
+
+ u = user_findhandle( gc, who );
+ for( c = gc->conversations; c && c->id != id; c = c->next );
+
+ /* if( g_strcasecmp( set_getstr( gc->irc, "html" ), "strip" ) == 0 ) */
+ if( gc->flags & OPT_CONN_HTML )
+ strip_html( msg );
+
+ if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), msg, buf, 0, 8192 ) != -1 )
+ msg = buf;
+
+ if( c && u )
+ irc_privmsg( gc->irc, u, "PRIVMSG", c->channel, "", msg );
+ else
+ irc_usermsg( gc->irc, "Message from/to conversation %s@%d (unknown conv/user): %s", who, id, msg );
+}
+
+struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle )
+{
+ struct conversation *c;
+ char *s;
+
+ /* This one just creates the conversation structure, user won't see anything yet */
+
+ if( gc->conversations )
+ {
+ for( c = gc->conversations; c->next; c = c->next );
+ c = c->next = g_new0( struct conversation, 1 );
+ }
+ else
+ gc->conversations = c = g_new0( struct conversation, 1);
+
+ c->id = id;
+ c->gc = gc;
+ c->title = g_strdup( handle );
+
+ s = g_new( char, 16 );
+ sprintf( s, "#chat_%03d", gc->irc->c_id++ );
+ c->channel = g_strdup( s );
+ g_free( s );
+
+ if( set_getint( gc->irc, "debug" ) )
+ irc_usermsg( gc->irc, "Creating new conversation: (id=%d,handle=%s)", id, handle );
+
+ return( c );
+}
+
+void serv_finish_login( struct gaim_connection *gc )
+{
+ return;
+}
+
+
+/* buddy_chat.c */
+
+void add_chat_buddy( struct conversation *b, char *handle )
+{
+ user_t *u = user_findhandle( b->gc, handle );
+ int me = 0;
+
+ if( set_getint( b->gc->irc, "debug" ) )
+ irc_usermsg( b->gc->irc, "User %s added to conversation %d", handle, b->id );
+
+ /* It might be yourself! */
+ if( handle_cmp ( handle, b->gc->user->username, b->gc->protocol ) == 0 )
+ {
+ u = user_find( b->gc->irc, b->gc->irc->nick );
+ if( !b->joined )
+ irc_join( b->gc->irc, u, b->channel );
+ b->joined = me = 1;
+ }
+
+ /* Most protocols allow people to join, even when they're not in
+ your contact list. Try to handle that here */
+ if( !u )
+ {
+ add_buddy( b->gc, NULL, handle, NULL );
+ u = user_findhandle( b->gc, handle );
+ }
+
+ /* Add the handle to the room userlist, if it's not 'me' */
+ if( !me )
+ {
+ if( b->joined )
+ irc_join( b->gc->irc, u, b->channel );
+ b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
+ }
+}
+
+void remove_chat_buddy( struct conversation *b, char *handle, char *reason )
+{
+ user_t *u;
+ int me = 0;
+
+ if( set_getint( b->gc->irc, "debug" ) )
+ irc_usermsg( b->gc->irc, "User %s removed from conversation %d (%s)", handle, b->id, reason ? reason : "" );
+
+ /* It might be yourself! */
+ if( g_strcasecmp( handle, b->gc->user->username ) == 0 )
+ {
+ u = user_find( b->gc->irc, b->gc->irc->nick );
+ b->joined = 0;
+ me = 1;
+ }
+ else
+ {
+ u = user_findhandle( b->gc, handle );
+ }
+
+ if( remove_chat_buddy_silent( b, handle ) )
+ if( ( b->joined || me ) && u )
+ irc_part( b->gc->irc, u, b->channel );
+}
+
+static int remove_chat_buddy_silent( struct conversation *b, char *handle )
+{
+ GList *i;
+
+ /* Find the handle in the room userlist and shoot it */
+ i = b->in_room;
+ while( i )
+ {
+ if( g_strcasecmp( handle, i->data ) == 0 )
+ {
+ g_free( i->data );
+ b->in_room = g_list_remove( b->in_room, i->data );
+ return( 1 );
+ }
+
+ i = i->next;
+ }
+
+ return( 0 );
+}
+
+
+/* prefs.c */
+
+/* Necessary? */
+void build_block_list()
+{
+ return;
+}
+
+void build_allow_list()
+{
+ return;
+}
+
+
+/* Misc. BitlBee stuff which shouldn't really be here */
+
+struct conversation *conv_findchannel( char *channel )
+{
+ struct gaim_connection *gc;
+ struct conversation *c;
+ GSList *l;
+
+ /* This finds the connection which has a conversation which belongs to this channel */
+ for( l = connections; l; l = l->next )
+ {
+ gc = l->data;
+ for( c = gc->conversations; c && g_strcasecmp( c->channel, channel ) != 0; c = c->next );
+ if( c )
+ return( c );
+ }
+
+ return( NULL );
+}
+
+char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value )
+{
+ int st;
+
+ if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+ st = 1;
+ else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+ st = 0;
+ else if( sscanf( value, "%d", &st ) != 1 )
+ return( NULL );
+
+ st = st != 0;
+
+ /* Horror.... */
+
+ if( st != set_getint( irc, "away_devoice" ) )
+ {
+ char list[80] = "";
+ user_t *u = irc->users;
+ int i = 0, count = 0;
+ char pm;
+ char v[80];
+
+ if( st )
+ pm = '+';
+ else
+ pm = '-';
+
+ while( u )
+ {
+ if( u->gc && u->online && !u->away )
+ {
+ if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
+ {
+ for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
+ irc_write( irc, ":%s!%s@%s MODE %s %c%s%s",
+ irc->mynick, irc->mynick, irc->myhost,
+ irc->channel, pm, v, list );
+
+ *list = 0;
+ count = 0;
+ }
+
+ sprintf( list + strlen( list ), " %s", u->nick );
+ count ++;
+ }
+ u = u->next;
+ }
+
+ /* $v = 'v' x $i */
+ for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
+ irc_write( irc, ":%s!%s@%s MODE %s %c%s%s", irc->mynick, irc->mynick, irc->myhost,
+ irc->channel, pm, v, list );
+ }
+
+ return( set_eval_bool( irc, set, value ) );
+}
+
+int serv_send_im( irc_t *irc, user_t *u, char *msg, int flags )
+{
+ char buf[8192];
+
+ if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 )
+ msg = buf;
+
+ if( u->gc->flags & OPT_CONN_HTML) {
+ char * html = escape_html(msg);
+ strncpy(buf, html, 8192);
+ g_free(html);
+ }
+
+ return( ((struct gaim_connection *)u->gc)->prpl->send_im( u->gc, u->handle, msg, strlen( msg ), flags ) );
+}
+
+int serv_send_chat( irc_t *irc, struct gaim_connection *gc, int id, char *msg )
+{
+ char buf[8192];
+
+ if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+ do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 )
+ msg = buf;
+
+ if( gc->flags & OPT_CONN_HTML) {
+ char * html = escape_html(msg);
+ strncpy(buf, html, 8192);
+ g_free(html);
+ }
+
+ return( gc->prpl->chat_send( gc, id, msg ) );
+}
+
+/* Convert from one charset to another.
+
+ from_cs, to_cs: Source and destination charsets
+ src, dst: Source and destination strings
+ size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
+ maxbuf: Maximum number of bytes to write to dst
+
+ Returns the number of bytes written to maxbuf or -1 on an error.
+*/
+signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
+{
+ iconv_t cd;
+ size_t res;
+ size_t inbytesleft, outbytesleft;
+ char *inbuf = src;
+ char *outbuf = dst;
+
+ cd = iconv_open( to_cs, from_cs );
+ if( cd == (iconv_t) -1 )
+ return( -1 );
+
+ inbytesleft = size ? size : strlen( src );
+ outbytesleft = maxbuf - 1;
+ res = iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+ *outbuf = '\0';
+ iconv_close( cd );
+
+ if( res == (size_t) -1 )
+ return( -1 );
+ else
+ return( outbuf - dst );
+}
+
+char *set_eval_charset( irc_t *irc, set_t *set, char *value )
+{
+ iconv_t cd;
+
+ if ( g_strncasecmp( value, "none", 4 ) == 0 )
+ return( value );
+
+ cd = iconv_open( "UTF-8", value );
+ if( cd == (iconv_t) -1 )
+ return( NULL );
+
+ iconv_close( cd );
+ return( value );
+}
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
new file mode 100644
index 00000000..4e10330a
--- /dev/null
+++ b/protocols/nogaim.h
@@ -0,0 +1,347 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * nogaim
+ *
+ * Gaim without gaim - for BitlBee
+ *
+ * This file contains functions called by the Gaim IM-modules. It contains
+ * some struct and type definitions from Gaim.
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * (and possibly other members of the Gaim team)
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _NOGAIM_H
+#define _NOGAIM_H
+
+#include "bitlbee.h"
+#include "proxy.h"
+#include "md5.h"
+#include "sha.h"
+
+
+#define BUF_LEN MSG_LEN
+#define BUF_LONG ( BUF_LEN * 2 )
+#define MSG_LEN 2048
+#define BUF_LEN MSG_LEN
+
+#define SELF_ALIAS_LEN 400
+#define BUDDY_ALIAS_MAXLEN 388 /* because MSN names can be 387 characters */
+
+#define PERMIT_ALL 1
+#define PERMIT_NONE 2
+#define PERMIT_SOME 3
+#define DENY_SOME 4
+
+#define WEBSITE "http://www.bitlee.org/"
+#define IM_FLAG_AWAY 0x0020
+#define OPT_CONN_HTML 0x00000001
+#define OPT_LOGGED_IN 0x00010000
+#define GAIM_AWAY_CUSTOM "Custom"
+
+#define GAIM_LOGO 0
+#define GAIM_ERROR 1
+#define GAIM_WARNING 2
+#define GAIM_INFO 3
+
+/* ok. now the fun begins. first we create a connection structure */
+struct gaim_connection {
+ /* we need to do either oscar or TOC */
+ /* we make this as an int in case if we want to add more protocols later */
+ int protocol;
+ struct prpl *prpl;
+ guint32 flags;
+
+ /* all connections need an input watcher */
+ int inpa;
+
+ /* buddy list stuff. there is still a global groups for the buddy list, but
+ * we need to maintain our own set of buddies, and our own permit/deny lists */
+ GSList *permit;
+ GSList *deny;
+ int permdeny;
+
+ /* all connections need a list of chats, even if they don't have chat */
+ GSList *buddy_chats;
+
+ /* each connection then can have its own protocol-specific data */
+ void *proto_data;
+
+ struct aim_user *user;
+
+ char username[64];
+ char displayname[128];
+ char password[32];
+ guint keepalive;
+ /* stuff needed for per-connection idle times */
+ guint idle_timer;
+ time_t login_time;
+ time_t lastsent;
+ int is_idle;
+
+ char *away;
+ int is_auto_away;
+
+ int evil;
+ gboolean wants_to_die; /* defaults to FALSE */
+
+ /* BitlBee */
+ irc_t *irc;
+ int lstitems; /* added for msnP8 */
+
+ struct conversation *conversations;
+};
+
+/* struct buddy_chat went away and got merged with this. */
+struct conversation {
+ struct gaim_connection *gc;
+
+ /* stuff used just for chat */
+ GList *in_room;
+ GList *ignored;
+ int id;
+
+ /* BitlBee */
+ struct conversation *next;
+ char *channel;
+ char *title;
+ char joined;
+ void *data;
+};
+
+struct buddy {
+ char name[80];
+ char show[BUDDY_ALIAS_MAXLEN];
+ int present;
+ int evil;
+ time_t signon;
+ time_t idle;
+ int uc;
+ guint caps; /* woohoo! */
+ void *proto_data; /* what a hack */
+ struct gaim_connection *gc; /* the connection it belongs to */
+};
+
+struct aim_user {
+ char username[64];
+ char alias[SELF_ALIAS_LEN];
+ char password[32];
+ char user_info[2048];
+ int options;
+ int protocol;
+ /* prpls can use this to save information about the user,
+ * like which server to connect to, etc */
+ char proto_opt[7][256];
+
+ struct gaim_connection *gc;
+ irc_t *irc;
+};
+
+struct prpl {
+ int protocol;
+ int options;
+ char *(* name)();
+
+ /* for ICQ and Yahoo, who have off/on per-conversation options */
+ /* char *checkbox; this should be per-connection */
+
+ GList *(* away_states)(struct gaim_connection *gc);
+ GList *(* actions)();
+ void (* do_action)(struct gaim_connection *, char *);
+ /* user_opts returns a GList* of g_malloc'd struct proto_user_opts */
+ GList *(* user_opts)();
+ GList *(* chat_info)(struct gaim_connection *);
+
+ /* all the server-related functions */
+
+ /* a lot of these (like get_dir) are protocol-dependent and should be removed. ones like
+ * set_dir (which is also protocol-dependent) can stay though because there's a dialog
+ * (i.e. the prpl says you can set your dir info, the ui shows a dialog and needs to call
+ * set_dir in order to set it) */
+
+ void (* login) (struct aim_user *);
+ void (* close) (struct gaim_connection *);
+ int (* send_im) (struct gaim_connection *, char *who, char *message, int len, int away);
+ int (* send_typing) (struct gaim_connection *, char *who, int typing);
+ void (* set_info) (struct gaim_connection *, char *info);
+ void (* get_info) (struct gaim_connection *, char *who);
+ void (* set_away) (struct gaim_connection *, char *state, char *message);
+ void (* get_away) (struct gaim_connection *, char *who);
+ void (* set_idle) (struct gaim_connection *, int idletime);
+ void (* add_buddy) (struct gaim_connection *, char *name);
+ void (* remove_buddy) (struct gaim_connection *, char *name, char *group);
+ void (* add_permit) (struct gaim_connection *, char *name);
+ void (* add_deny) (struct gaim_connection *, char *name);
+ void (* rem_permit) (struct gaim_connection *, char *name);
+ void (* rem_deny) (struct gaim_connection *, char *name);
+ void (* set_permit_deny)(struct gaim_connection *);
+ void (* join_chat) (struct gaim_connection *, GList *data);
+ void (* chat_invite) (struct gaim_connection *, int id, char *who, char *message);
+ void (* chat_leave) (struct gaim_connection *, int id);
+ void (* chat_whisper) (struct gaim_connection *, int id, char *who, char *message);
+ int (* chat_send) (struct gaim_connection *, int id, char *message);
+ int (* chat_open) (struct gaim_connection *, char *who);
+ void (* keepalive) (struct gaim_connection *);
+
+ /* get "chat buddy" info and away message */
+ void (* get_cb_info) (struct gaim_connection *, int, char *who);
+ void (* get_cb_away) (struct gaim_connection *, int, char *who);
+
+ /* save/store buddy's alias on server list/roster */
+ void (* alias_buddy) (struct gaim_connection *, char *who);
+
+ /* change a buddy's group on a server list/roster */
+ void (* group_buddy) (struct gaim_connection *, char *who, char *old_group, char *new_group);
+
+ void (* buddy_free) (struct buddy *);
+
+ char *(* get_status_string) (struct gaim_connection *gc, int stat);
+};
+
+#define PROTO_TOC 0
+#define PROTO_OSCAR 1
+#define PROTO_YAHOO 2
+#define PROTO_ICQ 3
+#define PROTO_MSN 4
+#define PROTO_IRC 5
+#define PROTO_FTP 6
+#define PROTO_VGATE 7
+#define PROTO_JABBER 8
+#define PROTO_NAPSTER 9
+#define PROTO_ZEPHYR 10
+#define PROTO_GADUGADU 11
+#define PROTO_MAX 16
+
+extern char proto_name[PROTO_MAX][8];
+
+#define UC_UNAVAILABLE 1
+
+/* JABBER */
+#define UC_AWAY (0x02 | UC_UNAVAILABLE)
+#define UC_CHAT 0x04
+#define UC_XA (0x08 | UC_UNAVAILABLE)
+#define UC_DND (0x10 | UC_UNAVAILABLE)
+
+G_MODULE_EXPORT GSList *get_connections();
+extern struct prpl *proto_prpl[16];
+
+/* nogaim.c */
+int serv_send_im(irc_t *irc, user_t *u, char *msg, int flags);
+int serv_send_chat(irc_t *irc, struct gaim_connection *gc, int id, char *msg );
+
+G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf );
+char *set_eval_charset( irc_t *irc, set_t *set, char *value );
+
+void nogaim_init();
+int proto_away( struct gaim_connection *gc, char *away );
+char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value );
+int handle_cmp( char *a, char *b, int protocol );
+
+gboolean auto_reconnect( gpointer data );
+void cancel_auto_reconnect( struct account *a );
+
+/* multi.c */
+G_MODULE_EXPORT struct gaim_connection *new_gaim_conn( struct aim_user *user );
+G_MODULE_EXPORT void destroy_gaim_conn( struct gaim_connection *gc );
+G_MODULE_EXPORT void set_login_progress( struct gaim_connection *gc, int step, char *msg );
+G_MODULE_EXPORT void hide_login_progress( struct gaim_connection *gc, char *msg );
+G_MODULE_EXPORT void hide_login_progress_error( struct gaim_connection *gc, char *msg );
+G_MODULE_EXPORT void serv_got_crap( struct gaim_connection *gc, char *format, ... );
+G_MODULE_EXPORT void account_online( struct gaim_connection *gc );
+G_MODULE_EXPORT void account_offline( struct gaim_connection *gc );
+G_MODULE_EXPORT void signoff( struct gaim_connection *gc );
+
+/* dialogs.c */
+G_MODULE_EXPORT void do_error_dialog( struct gaim_connection *gc, char *msg, char *title );
+G_MODULE_EXPORT void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont );
+
+/* list.c */
+G_MODULE_EXPORT int bud_list_cache_exists( struct gaim_connection *gc );
+G_MODULE_EXPORT void do_import( struct gaim_connection *gc, void *null );
+G_MODULE_EXPORT void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname );
+G_MODULE_EXPORT struct buddy *find_buddy( struct gaim_connection *gc, char *handle );
+G_MODULE_EXPORT void do_export( struct gaim_connection *gc );
+G_MODULE_EXPORT void signoff_blocked( struct gaim_connection *gc );
+
+G_MODULE_EXPORT void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname );
+
+/* buddy_chat.c */
+G_MODULE_EXPORT void add_chat_buddy( struct conversation *b, char *handle );
+G_MODULE_EXPORT void remove_chat_buddy( struct conversation *b, char *handle, char *reason );
+
+/* prpl.c */
+G_MODULE_EXPORT void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg );
+
+/* server.c */
+G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps );
+G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len );
+G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout );
+G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data );
+G_MODULE_EXPORT struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle );
+G_MODULE_EXPORT void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime );
+G_MODULE_EXPORT void serv_got_chat_left( struct gaim_connection *gc, int id );
+/* void serv_finish_login( struct gaim_connection *gc ); */
+
+/* util.c */
+G_MODULE_EXPORT char *utf8_to_str( const char *in );
+G_MODULE_EXPORT char *str_to_utf8( const char *in );
+G_MODULE_EXPORT void strip_linefeed( gchar *text );
+G_MODULE_EXPORT char *add_cr( char *text );
+G_MODULE_EXPORT char *tobase64( const char *text );
+G_MODULE_EXPORT char *normalize( const char *s );
+G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
+G_MODULE_EXPORT void strip_html( char *msg );
+G_MODULE_EXPORT char * escape_html(const char *html);
+G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value);
+
+#ifdef WITH_MSN
+/* msn.c */
+G_MODULE_EXPORT void msn_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_OSCAR
+/* oscar.c */
+G_MODULE_EXPORT void oscar_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_JABBER
+/* jabber.c */
+G_MODULE_EXPORT void jabber_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_YAHOO
+/* yahoo.c */
+G_MODULE_EXPORT void byahoo_init( struct prpl *ret );
+#endif
+
+/* prefs.c */
+G_MODULE_EXPORT void build_block_list();
+G_MODULE_EXPORT void build_allow_list();
+
+struct conversation *conv_findchannel( char *channel );
+
+
+#endif
diff --git a/protocols/oscar/AUTHORS b/protocols/oscar/AUTHORS
new file mode 100644
index 00000000..5ca13988
--- /dev/null
+++ b/protocols/oscar/AUTHORS
@@ -0,0 +1,31 @@
+
+Anyone else want to be in here?
+
+---
+
+N: Adam Fritzler
+H: mid
+E: mid@auk.cx
+W: http://www.auk.cx/~mid,http://www.auk.cx/faim
+D: Wrote most of the wap of crap that you see before you.
+
+N: Josh Myer
+E: josh@joshisanerd.com
+D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings.
+
+N: Daniel Reed
+H: n, linuxkitty
+E: n@ml.org
+W: http://users.n.ml.org/n/
+D: Fixed aim_snac.c
+
+N: Eric Warmenhoven
+E: warmenhoven@linux.com
+D: Some OFT info, author of the faim interface for gaim
+
+N: Brock Wilcox
+H: awwaiid
+E: awwaiid@auk.cx
+D: Figured out original password roasting
+
+
diff --git a/protocols/oscar/COPYING b/protocols/oscar/COPYING
new file mode 100644
index 00000000..223ede7d
--- /dev/null
+++ b/protocols/oscar/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile
new file mode 100644
index 00000000..acf4794a
--- /dev/null
+++ b/protocols/oscar/Makefile
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o ft.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: oscarr.o
+
+.PHONY: all clean distclean
+
+clean:
+ rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+oscarr.o: $(objects)
+ @echo '*' Linking oscarr.o
+ @$(LD) $(LFLAGS) $(objects) -o oscarr.o
diff --git a/protocols/oscar/admin.c b/protocols/oscar/admin.c
new file mode 100644
index 00000000..09082c5b
--- /dev/null
+++ b/protocols/oscar/admin.c
@@ -0,0 +1,196 @@
+#include <aim.h>
+#include "admin.h"
+
+/* called for both reply and change-reply */
+static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ /*
+ * struct {
+ * guint16 perms;
+ * guint16 tlvcount;
+ * aim_tlv_t tlvs[tlvcount];
+ * } admin_info[n];
+ */
+ while (aim_bstream_empty(bs)) {
+ guint16 perms, tlvcount;
+
+ perms = aimbs_get16(bs);
+ tlvcount = aimbs_get16(bs);
+
+ while (tlvcount && aim_bstream_empty(bs)) {
+ aim_rxcallback_t userfunc;
+ guint16 type, len;
+ guint8 *val;
+ int str = 0;
+
+ type = aimbs_get16(bs);
+ len = aimbs_get16(bs);
+
+ if ((type == 0x0011) || (type == 0x0004))
+ str = 1;
+
+ if (str)
+ val = (guint8 *)aimbs_getstr(bs, len);
+ else
+ val = aimbs_getraw(bs, len);
+
+ /* XXX fix so its only called once for the entire packet */
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, type, len, val, str);
+
+ g_free(val);
+
+ tlvcount--;
+ }
+ }
+
+ return 1;
+}
+
+static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ guint16 status;
+
+ status = aimbs_get16(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, status);
+
+ return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005))
+ return infochange(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0007)
+ return accountconfirm(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int admin_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = AIM_CB_FAM_ADM;
+ mod->version = 0x0001;
+ mod->toolid = AIM_TOOL_NEWWIN;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "admin", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
+{
+ aim_frame_t *tx;
+ aim_tlvlist_t *tl = NULL;
+ aim_snacid_t snacid;
+
+ if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+ aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+ /* new password TLV t(0002) */
+ aim_addtlvtochain_raw(&tl, 0x0002, strlen(newpw), (guint8 *)newpw);
+
+ /* current password TLV t(0012) */
+ aim_addtlvtochain_raw(&tl, 0x0012, strlen(curpw), (guint8 *)curpw);
+
+ aim_writetlvchain(&tx->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, tx);
+
+ return 0;
+}
+
+/*
+ * Request account confirmation.
+ *
+ * This will cause an email to be sent to the address associated with
+ * the account. By following the instructions in the mail, you can
+ * get the TRIAL flag removed from your account.
+ *
+ */
+int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
+}
+
+/*
+ * Request a bit of account info.
+ *
+ * The only known valid tag is 0x0011 (email address).
+ *
+ */
+int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info)
+{
+ aim_frame_t *tx;
+ aim_snacid_t snacid;
+
+ if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&tx->data, 0x0007, 0x0002, 0x0000, snacid);
+
+ aimbs_put16(&tx->data, info);
+ aimbs_put16(&tx->data, 0x0000);
+
+ aim_tx_enqueue(sess, tx);
+
+ return 0;
+}
+
+int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
+{
+ aim_frame_t *tx;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+ aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+ aim_addtlvtochain_raw(&tl, 0x0011, strlen(newemail), (guint8 *)newemail);
+
+ aim_writetlvchain(&tx->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, tx);
+
+ return 0;
+}
+
+int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick)
+{
+ aim_frame_t *tx;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+ aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(newnick), (guint8 *)newnick);
+
+ aim_writetlvchain(&tx->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, tx);
+
+
+ return 0;
+}
diff --git a/protocols/oscar/admin.h b/protocols/oscar/admin.h
new file mode 100644
index 00000000..f9d74cae
--- /dev/null
+++ b/protocols/oscar/admin.h
@@ -0,0 +1,13 @@
+#ifndef __OSCAR_ADMIN_H__
+#define __OSCAR_ADMIN_H__
+
+#define AIM_CB_FAM_ADM 0x0007
+
+/*
+ * SNAC Family: Administrative Services.
+ */
+#define AIM_CB_ADM_ERROR 0x0001
+#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005
+#define AIM_CB_ADM_DEFAULT 0xffff
+
+#endif /* __OSCAR_ADMIN_H__ */
diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h
new file mode 100644
index 00000000..2ba29c8b
--- /dev/null
+++ b/protocols/oscar/aim.h
@@ -0,0 +1,921 @@
+/*
+ * Main libfaim header. Must be included in client for prototypes/macros.
+ *
+ * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
+ * -- Josh Meyer
+ *
+ */
+
+#ifndef __AIM_H__
+#define __AIM_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <gmodule.h>
+
+#include "bitlbee.h"
+
+/* XXX adjust these based on autoconf-detected platform */
+typedef guint32 aim_snacid_t;
+typedef guint16 flap_seqnum_t;
+
+/* Portability stuff (DMP) */
+
+#if defined(mach) && defined(__APPLE__)
+#define gethostbyname(x) gethostbyname2(x, AF_INET)
+#endif
+
+/*
+ * Current Maximum Length for Screen Names (not including NULL)
+ *
+ * Currently only names up to 16 characters can be registered
+ * however it is aparently legal for them to be larger.
+ */
+#define MAXSNLEN 32
+
+/*
+ * Current Maximum Length for Instant Messages
+ *
+ * This was found basically by experiment, but not wholly
+ * accurate experiment. It should not be regarded
+ * as completely correct. But its a decent approximation.
+ *
+ * Note that although we can send this much, its impossible
+ * for WinAIM clients (up through the latest (4.0.1957)) to
+ * send any more than 1kb. Amaze all your windows friends
+ * with utterly oversized instant messages!
+ *
+ * XXX: the real limit is the total SNAC size at 8192. Fix this.
+ *
+ */
+#define MAXMSGLEN 7987
+
+/*
+ * Maximum size of a Buddy Icon.
+ */
+#define MAXICONLEN 7168
+#define AIM_ICONIDENT "AVT1picture.id"
+
+/*
+ * Current Maximum Length for Chat Room Messages
+ *
+ * This is actually defined by the protocol to be
+ * dynamic, but I have yet to see due cause to
+ * define it dynamically here. Maybe later.
+ *
+ */
+#define MAXCHATMSGLEN 512
+
+/*
+ * Standard size of an AIM authorization cookie
+ */
+#define AIM_COOKIELEN 0x100
+
+#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
+
+/*
+ * Default Authorizer server name and TCP port for the OSCAR farm.
+ *
+ * You shouldn't need to change this unless you're writing
+ * your own server.
+ *
+ * Note that only one server is needed to start the whole
+ * AIM process. The later server addresses come from
+ * the authorizer service.
+ *
+ * This is only here for convenience. Its still up to
+ * the client to connect to it.
+ *
+ */
+#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com"
+#define AIM_LOGIN_PORT 5190
+
+/*
+ * Size of the SNAC caching hash.
+ *
+ * Default: 16
+ *
+ */
+#define AIM_SNAC_HASH_SIZE 16
+
+/*
+ * Client info. Filled in by the client and passed in to
+ * aim_send_login(). The information ends up getting passed to OSCAR
+ * through the initial login command.
+ *
+ */
+struct client_info_s {
+ const char *clientstring;
+ guint16 clientid;
+ int major;
+ int minor;
+ int point;
+ int build;
+ const char *country; /* two-letter abbrev */
+ const char *lang; /* two-letter abbrev */
+};
+
+#define AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 { \
+ "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
+ 0x0004, \
+ 0x0003, \
+ 0x0005, \
+ 0x0000, \
+ 0x0686, \
+ "us", \
+ "en", \
+}
+
+#define AIM_CLIENTINFO_KNOWNGOOD_4_1_2010 { \
+ "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
+ 0x0004, \
+ 0x0004, \
+ 0x0001, \
+ 0x0000, \
+ 0x07da, \
+ "us", \
+ "en", \
+}
+
+/*
+ * I would make 4.1.2010 the default, but they seem to have found
+ * an alternate way of breaking that one.
+ *
+ * 3.5.1670 should work fine, however, you will be subjected to the
+ * memory test, which may require you to have a WinAIM binary laying
+ * around. (see login.c::memrequest())
+ */
+#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/*
+ * These could be arbitrary, but its easier to use the actual AIM values
+ */
+#define AIM_CONN_TYPE_AUTH 0x0007
+#define AIM_CONN_TYPE_ADS 0x0005
+#define AIM_CONN_TYPE_BOS 0x0002
+#define AIM_CONN_TYPE_CHAT 0x000e
+#define AIM_CONN_TYPE_CHATNAV 0x000d
+
+/* they start getting arbitrary in rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS 0x0101 /* these do not speak FLAP! */
+#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
+
+/*
+ * Subtypes, we need these for OFT stuff.
+ */
+#define AIM_CONN_SUBTYPE_OFT_DIRECTIM 0x0001
+#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002
+#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003
+#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004
+#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005
+
+/*
+ * Status values returned from aim_conn_new(). ORed together.
+ */
+#define AIM_CONN_STATUS_READY 0x0001
+#define AIM_CONN_STATUS_INTERNALERR 0x0002
+#define AIM_CONN_STATUS_RESOLVERR 0x0040
+#define AIM_CONN_STATUS_CONNERR 0x0080
+#define AIM_CONN_STATUS_INPROGRESS 0x0100
+
+#define AIM_FRAMETYPE_FLAP 0x0000
+#define AIM_FRAMETYPE_OFT 0x0001
+
+/*
+ * message type flags
+ */
+#define AIM_MTYPE_PLAIN 0x01
+#define AIM_MTYPE_CHAT 0x02
+#define AIM_MTYPE_FILEREQ 0x03
+#define AIM_MTYPE_URL 0x04
+#define AIM_MTYPE_AUTHREQ 0x06
+#define AIM_MTYPE_AUTHDENY 0x07
+#define AIM_MTYPE_AUTHOK 0x08
+#define AIM_MTYPE_SERVER 0x09
+#define AIM_MTYPE_ADDED 0x0C
+#define AIM_MTYPE_WWP 0x0D
+#define AIM_MTYPE_EEXPRESS 0x0E
+#define AIM_MTYPE_CONTACTS 0x13
+#define AIM_MTYPE_PLUGIN 0x1A
+#define AIM_MTYPE_AUTOAWAY 0xE8
+#define AIM_MTYPE_AUTOBUSY 0xE9
+#define AIM_MTYPE_AUTONA 0xEA
+#define AIM_MTYPE_AUTODND 0xEB
+#define AIM_MTYPE_AUTOFFC 0xEC
+
+typedef struct aim_conn_s {
+ int fd;
+ guint16 type;
+ guint16 subtype;
+ flap_seqnum_t seqnum;
+ guint32 status;
+ void *priv; /* misc data the client may want to store */
+ void *internal; /* internal conn-specific libfaim data */
+ time_t lastactivity; /* time of last transmit */
+ int forcedlatency;
+ void *handlerlist;
+ void *sessv; /* pointer to parent session */
+ void *inside; /* only accessible from inside libfaim */
+ struct aim_conn_s *next;
+} aim_conn_t;
+
+/*
+ * Byte Stream type. Sort of.
+ *
+ * Use of this type serves a couple purposes:
+ * - Buffer/buflen pairs are passed all around everywhere. This turns
+ * that into one value, as well as abstracting it slightly.
+ * - Through the abstraction, it is possible to enable bounds checking
+ * for robustness at the cost of performance. But a clean failure on
+ * weird packets is much better than a segfault.
+ * - I like having variables named "bs".
+ *
+ * Don't touch the insides of this struct. Or I'll have to kill you.
+ *
+ */
+typedef struct aim_bstream_s {
+ guint8 *data;
+ guint32 len;
+ guint32 offset;
+} aim_bstream_t;
+
+typedef struct aim_frame_s {
+ guint8 hdrtype; /* defines which piece of the union to use */
+ union {
+ struct {
+ guint8 type;
+ flap_seqnum_t seqnum;
+ } flap;
+ struct {
+ guint16 type;
+ guint8 magic[4]; /* ODC2 OFT2 */
+ guint16 hdr2len;
+ guint8 *hdr2; /* rest of bloated header */
+ } oft;
+ } hdr;
+ aim_bstream_t data; /* payload stream */
+ guint8 handled; /* 0 = new, !0 = been handled */
+ guint8 nofree; /* 0 = free data on purge, 1 = only unlink */
+ aim_conn_t *conn; /* the connection it came in on... */
+ struct aim_frame_s *next;
+} aim_frame_t;
+
+typedef struct aim_msgcookie_s {
+ unsigned char cookie[8];
+ int type;
+ void *data;
+ time_t addtime;
+ struct aim_msgcookie_s *next;
+} aim_msgcookie_t;
+
+/*
+ * AIM Session: The main client-data interface.
+ *
+ */
+typedef struct aim_session_s {
+
+ /* ---- Client Accessible ------------------------ */
+
+ /* Our screen name. */
+ char sn[MAXSNLEN+1];
+
+ /*
+ * Pointer to anything the client wants to
+ * explicitly associate with this session.
+ *
+ * This is for use in the callbacks mainly. In any
+ * callback, you can access this with sess->aux_data.
+ *
+ */
+ void *aux_data;
+
+ /* ---- Internal Use Only ------------------------ */
+
+ /* Server-stored information (ssi) */
+ struct {
+ int received_data;
+ guint16 revision;
+ struct aim_ssi_item *items;
+ time_t timestamp;
+ int waiting_for_ack;
+ aim_frame_t *holding_queue;
+ } ssi;
+
+ /* Connection information */
+ aim_conn_t *connlist;
+
+ /*
+ * Transmit/receive queues.
+ *
+ * These are only used when you don't use your own lowlevel
+ * I/O. I don't suggest that you use libfaim's internal I/O.
+ * Its really bad and the API/event model is quirky at best.
+ *
+ */
+ aim_frame_t *queue_outgoing;
+ aim_frame_t *queue_incoming;
+
+ /*
+ * Tx Enqueuing function.
+ *
+ * This is how you override the transmit direction of libfaim's
+ * internal I/O. This function will be called whenever it needs
+ * to send something.
+ *
+ */
+ int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *);
+
+ /*
+ * Outstanding snac handling
+ *
+ * XXX: Should these be per-connection? -mid
+ */
+ void *snac_hash[AIM_SNAC_HASH_SIZE];
+ aim_snacid_t snacid_next;
+
+ struct aim_icq_info *icq_info;
+ struct aim_oft_info *oft_info;
+ struct aim_authresp_info *authinfo;
+ struct aim_emailinfo *emailinfo;
+
+ struct {
+ struct aim_userinfo_s *userinfo;
+ struct userinfo_node *torequest;
+ struct userinfo_node *requested;
+ int waiting_for_response;
+ } locate;
+
+ guint32 flags; /* AIM_SESS_FLAGS_ */
+
+ aim_msgcookie_t *msgcookies;
+
+ void *modlistv;
+
+ guint8 aim_icq_state; /* ICQ representation of away state */
+} aim_session_t;
+
+/* Values for sess->flags */
+#define AIM_SESS_FLAGS_SNACLOGIN 0x00000001
+#define AIM_SESS_FLAGS_XORLOGIN 0x00000002
+#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004
+#define AIM_SESS_FLAGS_DONTTIMEOUTONICBM 0x00000008
+
+/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
+#define AIM_ICQ_STATE_NORMAL 0x00000000
+#define AIM_ICQ_STATE_AWAY 0x00000001
+#define AIM_ICQ_STATE_DND 0x00000002
+#define AIM_ICQ_STATE_OUT 0x00000004
+#define AIM_ICQ_STATE_BUSY 0x00000010
+#define AIM_ICQ_STATE_CHAT 0x00000020
+#define AIM_ICQ_STATE_INVISIBLE 0x00000100
+#define AIM_ICQ_STATE_WEBAWARE 0x00010000
+#define AIM_ICQ_STATE_HIDEIP 0x00020000
+#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
+#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000
+#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
+#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
+#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000
+
+/*
+ * AIM User Info, Standard Form.
+ */
+typedef struct {
+ char sn[MAXSNLEN+1];
+ guint16 warnlevel;
+ guint16 idletime;
+ guint16 flags;
+ guint32 membersince;
+ guint32 onlinesince;
+ guint32 sessionlen;
+ guint32 capabilities;
+ struct {
+ guint32 status;
+ guint32 ipaddr;
+ guint8 crap[0x25]; /* until we figure it out... */
+ } icqinfo;
+ guint32 present;
+} aim_userinfo_t;
+
+#define AIM_USERINFO_PRESENT_FLAGS 0x00000001
+#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002
+#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004
+#define AIM_USERINFO_PRESENT_IDLE 0x00000008
+#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
+#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020
+#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040
+#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
+#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100
+
+const char *aim_userinfo_sn(aim_userinfo_t *ui);
+guint16 aim_userinfo_flags(aim_userinfo_t *ui);
+guint16 aim_userinfo_idle(aim_userinfo_t *ui);
+float aim_userinfo_warnlevel(aim_userinfo_t *ui);
+time_t aim_userinfo_membersince(aim_userinfo_t *ui);
+time_t aim_userinfo_onlinesince(aim_userinfo_t *ui);
+guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui);
+int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap);
+
+#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */
+#define AIM_FLAG_ADMINISTRATOR 0x0002
+#define AIM_FLAG_AOL 0x0004
+#define AIM_FLAG_OSCAR_PAY 0x0008
+#define AIM_FLAG_FREE 0x0010
+#define AIM_FLAG_AWAY 0x0020
+#define AIM_FLAG_ICQ 0x0040
+#define AIM_FLAG_WIRELESS 0x0080
+#define AIM_FLAG_UNKNOWN100 0x0100
+#define AIM_FLAG_UNKNOWN200 0x0200
+#define AIM_FLAG_ACTIVEBUDDY 0x0400
+#define AIM_FLAG_UNKNOWN800 0x0800
+#define AIM_FLAG_ABINTERNAL 0x1000
+
+#define AIM_FLAG_ALLUSERS 0x001f
+
+/*
+ * TLV handling
+ */
+
+/* Generic TLV structure. */
+typedef struct aim_tlv_s {
+ guint16 type;
+ guint16 length;
+ guint8 *value;
+} aim_tlv_t;
+
+/* List of above. */
+typedef struct aim_tlvlist_s {
+ aim_tlv_t *tlv;
+ struct aim_tlvlist_s *next;
+} aim_tlvlist_t;
+
+/* TLV-handling functions */
+
+#if 0
+/* Very, very raw TLV handling. */
+int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v);
+int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v);
+int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v);
+int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v);
+#endif
+
+/* TLV list handling. */
+aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs);
+void aim_freetlvchain(aim_tlvlist_t **list);
+aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, guint16 t, const int n);
+char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n);
+guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 type, const int num);
+guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n);
+guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n);
+int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
+int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v);
+int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v);
+int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 type, const guint32 v);
+int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 type, const char *msg);
+int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v);
+int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps);
+int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type);
+int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui);
+int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl);
+int aim_counttlvchain(aim_tlvlist_t **list);
+int aim_sizetlvchain(aim_tlvlist_t **list);
+
+
+/*
+ * Get command from connections
+ *
+ * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction.
+ * XXX Make this easily overridable.
+ *
+ */
+int aim_get_command(aim_session_t *, aim_conn_t *);
+
+/*
+ * Dispatch commands that are in the rx queue.
+ */
+void aim_rxdispatch(aim_session_t *);
+
+int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn);
+
+typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...);
+
+struct aim_clientrelease {
+ char *name;
+ guint32 build;
+ char *url;
+ char *info;
+};
+
+struct aim_authresp_info {
+ char *sn;
+ guint16 errorcode;
+ char *errorurl;
+ guint16 regstatus;
+ char *email;
+ char *bosip;
+ guint8 *cookie;
+ struct aim_clientrelease latestrelease;
+ struct aim_clientrelease latestbeta;
+};
+
+/* Callback data for redirect. */
+struct aim_redirect_data {
+ guint16 group;
+ const char *ip;
+ const guint8 *cookie;
+ struct { /* group == AIM_CONN_TYPE_CHAT */
+ guint16 exchange;
+ const char *room;
+ guint16 instance;
+ } chat;
+};
+
+int aim_clientready(aim_session_t *sess, aim_conn_t *conn);
+int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn);
+int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key);
+int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest);
+void aim_purge_rxqueue(aim_session_t *);
+
+#define AIM_TX_QUEUED 0 /* default */
+#define AIM_TX_IMMEDIATE 1
+#define AIM_TX_USER 2
+int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *));
+
+int aim_tx_flushqueue(aim_session_t *);
+void aim_tx_purgequeue(aim_session_t *);
+
+int aim_conn_setlatency(aim_conn_t *conn, int newval);
+
+void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
+
+int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, u_short family, u_short type, aim_rxcallback_t newhandler, u_short flags);
+int aim_clearhandlers(aim_conn_t *conn);
+
+aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group);
+aim_session_t *aim_conn_getsess(aim_conn_t *conn);
+void aim_conn_close(aim_conn_t *deadconn);
+aim_conn_t *aim_newconn(aim_session_t *, int type, const char *dest);
+int aim_conngetmaxfd(aim_session_t *);
+aim_conn_t *aim_select(aim_session_t *, struct timeval *, int *);
+int aim_conn_isready(aim_conn_t *);
+int aim_conn_setstatus(aim_conn_t *, int);
+int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn);
+int aim_conn_isconnecting(aim_conn_t *conn);
+
+typedef void (*faim_debugging_callback_t)(aim_session_t *sess, int level, const char *format, va_list va);
+int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t);
+void aim_session_init(aim_session_t *, guint32 flags, int debuglevel);
+void aim_session_kill(aim_session_t *);
+void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password);
+aim_conn_t *aim_getconn_type(aim_session_t *, int type);
+aim_conn_t *aim_getconn_type_all(aim_session_t *, int type);
+aim_conn_t *aim_getconn_fd(aim_session_t *, int fd);
+
+/* aim_misc.c */
+
+
+struct aim_chat_roominfo {
+ unsigned short exchange;
+ char *name;
+ unsigned short instance;
+};
+
+
+#define AIM_VISIBILITYCHANGE_PERMITADD 0x05
+#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
+#define AIM_VISIBILITYCHANGE_DENYADD 0x07
+#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08
+
+#define AIM_PRIVFLAGS_ALLOWIDLE 0x01
+#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02
+
+#define AIM_WARN_ANON 0x01
+
+int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn);
+int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags);
+int aim_nop(aim_session_t *, aim_conn_t *);
+int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn);
+int aim_bos_setidle(aim_session_t *, aim_conn_t *, guint32);
+int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *);
+int aim_bos_setbuddylist(aim_session_t *, aim_conn_t *, const char *);
+int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps);
+int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, guint32 mask);
+int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, guint32);
+int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *);
+int aim_reqservice(aim_session_t *, aim_conn_t *, guint16);
+int aim_bos_reqrights(aim_session_t *, aim_conn_t *);
+int aim_bos_reqbuddyrights(aim_session_t *, aim_conn_t *);
+int aim_bos_reqlocaterights(aim_session_t *, aim_conn_t *);
+int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy);
+int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy);
+int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status);
+
+struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *);
+
+#define AIM_CLIENTTYPE_UNKNOWN 0x0000
+#define AIM_CLIENTTYPE_MC 0x0001
+#define AIM_CLIENTTYPE_WINAIM 0x0002
+#define AIM_CLIENTTYPE_WINAIM41 0x0003
+#define AIM_CLIENTTYPE_AOL_TOC 0x0004
+unsigned short aim_fingerprintclient(unsigned char *msghdr, int len);
+
+#define AIM_RATE_CODE_CHANGE 0x0001
+#define AIM_RATE_CODE_WARNING 0x0002
+#define AIM_RATE_CODE_LIMIT 0x0003
+#define AIM_RATE_CODE_CLEARLIMIT 0x0004
+int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
+
+/* aim_im.c */
+
+aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize);
+
+aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn);
+int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size);
+int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn);
+int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn);
+
+#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0
+#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1
+
+int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag);
+
+#define AIM_GETINFO_GENERALINFO 0x00001
+#define AIM_GETINFO_AWAYMESSAGE 0x00003
+#define AIM_GETINFO_CAPABILITIES 0x0004
+
+struct aim_invite_priv {
+ char *sn;
+ char *roomname;
+ guint16 exchange;
+ guint16 instance;
+};
+
+#define AIM_COOKIETYPE_UNKNOWN 0x00
+#define AIM_COOKIETYPE_ICBM 0x01
+#define AIM_COOKIETYPE_ADS 0x02
+#define AIM_COOKIETYPE_BOS 0x03
+#define AIM_COOKIETYPE_IM 0x04
+#define AIM_COOKIETYPE_CHAT 0x05
+#define AIM_COOKIETYPE_CHATNAV 0x06
+#define AIM_COOKIETYPE_INVITE 0x07
+/* we'll move OFT up a bit to give breathing room. not like it really
+ * matters. */
+#define AIM_COOKIETYPE_OFTIM 0x10
+#define AIM_COOKIETYPE_OFTGET 0x11
+#define AIM_COOKIETYPE_OFTSEND 0x12
+#define AIM_COOKIETYPE_OFTVOICE 0x13
+#define AIM_COOKIETYPE_OFTIMAGE 0x14
+#define AIM_COOKIETYPE_OFTICON 0x15
+
+int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
+
+#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
+#define AIM_TRANSFER_DENY_DECLINE 0x0001
+#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
+int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, unsigned short code);
+aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, const guint8 *cookie, const guint8 *ip, guint16 listingfiles, guint16 listingtotsize, guint16 listingsize, guint32 listingchecksum, guint16 rendid);
+
+int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, unsigned short);
+int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info);
+int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+
+#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001
+#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002
+
+/* This is what the server will give you if you don't set them yourself. */
+#define AIM_IMPARAM_DEFAULTS { \
+ 0, \
+ AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+ 512, /* !! Note how small this is. */ \
+ (99.9)*10, (99.9)*10, \
+ 1000 /* !! And how large this is. */ \
+}
+
+/* This is what most AIM versions use. */
+#define AIM_IMPARAM_REASONABLE { \
+ 0, \
+ AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+ 8000, \
+ (99.9)*10, (99.9)*10, \
+ 0 \
+}
+
+
+struct aim_icbmparameters {
+ guint16 maxchan;
+ guint32 flags; /* AIM_IMPARAM_FLAG_ */
+ guint16 maxmsglen; /* message size that you will accept */
+ guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
+ guint16 maxrecverwarn;
+ guint32 minmsginterval; /* in milliseconds? */
+};
+
+int aim_reqicbmparams(aim_session_t *sess);
+int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params);
+
+/* auth.c */
+int aim_sendcookie(aim_session_t *, aim_conn_t *, const guint8 *);
+
+int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw);
+int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn);
+int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info);
+int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
+int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick);
+
+/* These apply to exchanges as well. */
+#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
+#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
+#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
+#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
+
+struct aim_chat_exchangeinfo {
+ guint16 number;
+ guint16 flags;
+ char *name;
+ char *charset1;
+ char *lang1;
+ char *charset2;
+ char *lang2;
+};
+
+#define AIM_CHATFLAGS_NOREFLECT 0x0001
+#define AIM_CHATFLAGS_AWAY 0x0002
+int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen);
+int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+char *aim_chat_getname(aim_conn_t *conn);
+aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name);
+
+int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn);
+
+int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
+
+int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange);
+int aim_chat_leaveroom(aim_session_t *sess, const char *name);
+
+/* aim_util.c */
+/*
+ * These are really ugly. You'd think this was LISP. I wish it was.
+ *
+ * XXX With the advent of bstream's, these should be removed to enforce
+ * their use.
+ *
+ */
+#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1)
+#define aimutil_get8(buf) ((*(buf))&0xff)
+#define aimutil_put16(buf, data) ( \
+ (*(buf) = (u_char)((data)>>8)&0xff), \
+ (*((buf)+1) = (u_char)(data)&0xff), \
+ 2)
+#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define aimutil_put32(buf, data) ( \
+ (*((buf)) = (u_char)((data)>>24)&0xff), \
+ (*((buf)+1) = (u_char)((data)>>16)&0xff), \
+ (*((buf)+2) = (u_char)((data)>>8)&0xff), \
+ (*((buf)+3) = (u_char)(data)&0xff), \
+ 4)
+#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+ (((*((buf)+1))<<16)&0x00ff0000) + \
+ (((*((buf)+2))<< 8)&0x0000ff00) + \
+ (((*((buf)+3) )&0x000000ff)))
+
+/* Little-endian versions (damn ICQ) */
+#define aimutil_putle8(buf, data) ( \
+ (*(buf) = (unsigned char)(data) & 0xff), \
+ 1)
+#define aimutil_getle8(buf) ( \
+ (*(buf)) & 0xff \
+ )
+#define aimutil_putle16(buf, data) ( \
+ (*((buf)+0) = (unsigned char)((data) >> 0) & 0xff), \
+ (*((buf)+1) = (unsigned char)((data) >> 8) & 0xff), \
+ 2)
+#define aimutil_getle16(buf) ( \
+ (((*((buf)+0)) << 0) & 0x00ff) + \
+ (((*((buf)+1)) << 8) & 0xff00) \
+ )
+#define aimutil_putle32(buf, data) ( \
+ (*((buf)+0) = (unsigned char)((data) >> 0) & 0xff), \
+ (*((buf)+1) = (unsigned char)((data) >> 8) & 0xff), \
+ (*((buf)+2) = (unsigned char)((data) >> 16) & 0xff), \
+ (*((buf)+3) = (unsigned char)((data) >> 24) & 0xff), \
+ 4)
+#define aimutil_getle32(buf) ( \
+ (((*((buf)+0)) << 0) & 0x000000ff) + \
+ (((*((buf)+1)) << 8) & 0x0000ff00) + \
+ (((*((buf)+2)) << 16) & 0x00ff0000) + \
+ (((*((buf)+3)) << 24) & 0xff000000))
+
+
+int aimutil_putstr(u_char *, const char *, int);
+int aimutil_tokslen(char *toSearch, int index, char dl);
+int aimutil_itemcnt(char *toSearch, char dl);
+char *aimutil_itemidx(char *toSearch, int index, char dl);
+int aim_sncmp(const char *a, const char *b);
+
+#include <aim_internal.h>
+
+/*
+ * SNAC Families.
+ */
+#define AIM_CB_FAM_ACK 0x0000
+#define AIM_CB_FAM_GEN 0x0001
+#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
+
+/*
+ * SNAC Family: Ack.
+ *
+ * Not really a family, but treating it as one really
+ * helps it fit into the libfaim callback structure better.
+ *
+ */
+#define AIM_CB_ACK_ACK 0x0001
+
+/*
+ * SNAC Family: General.
+ */
+#define AIM_CB_GEN_ERROR 0x0001
+#define AIM_CB_GEN_CLIENTREADY 0x0002
+#define AIM_CB_GEN_SERVERREADY 0x0003
+#define AIM_CB_GEN_SERVICEREQ 0x0004
+#define AIM_CB_GEN_REDIRECT 0x0005
+#define AIM_CB_GEN_RATEINFOREQ 0x0006
+#define AIM_CB_GEN_RATEINFO 0x0007
+#define AIM_CB_GEN_RATEINFOACK 0x0008
+#define AIM_CB_GEN_RATECHANGE 0x000a
+#define AIM_CB_GEN_SERVERPAUSE 0x000b
+#define AIM_CB_GEN_SERVERRESUME 0x000d
+#define AIM_CB_GEN_REQSELFINFO 0x000e
+#define AIM_CB_GEN_SELFINFO 0x000f
+#define AIM_CB_GEN_EVIL 0x0010
+#define AIM_CB_GEN_SETIDLE 0x0011
+#define AIM_CB_GEN_MIGRATIONREQ 0x0012
+#define AIM_CB_GEN_MOTD 0x0013
+#define AIM_CB_GEN_SETPRIVFLAGS 0x0014
+#define AIM_CB_GEN_WELLKNOWNURL 0x0015
+#define AIM_CB_GEN_NOP 0x0016
+#define AIM_CB_GEN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Advertisement Services
+ */
+#define AIM_CB_ADS_ERROR 0x0001
+#define AIM_CB_ADS_DEFAULT 0xffff
+
+/*
+ * OFT Services
+ *
+ * See non-SNAC note below.
+ */
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
+#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+
+#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */
+#define AIM_CB_OFT_GETFILEFILEREQ 0x0008 /* received file request */
+#define AIM_CB_OFT_GETFILEFILESEND 0x0009 /* received file request confirm -- send data */
+#define AIM_CB_OFT_GETFILECOMPLETE 0x000a /* received file send complete*/
+#define AIM_CB_OFT_GETFILEINITIATE 0x000b /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c /* OFT connection disconnected.*/
+#define AIM_CB_OFT_GETFILELISTING 0x000d /* OFT listing.txt received.*/
+#define AIM_CB_OFT_GETFILERECEIVE 0x000e /* OFT file incoming.*/
+#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f
+#define AIM_CB_OFT_GETFILESTATE4 0x0010
+
+#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020 /* OFT connection disconnected.*/
+
+
+
+/*
+ * SNAC Family: Internal Messages
+ *
+ * This isn't truely a SNAC family either, but using
+ * these, we can integrated non-SNAC services into
+ * the SNAC-centered libfaim callback structure.
+ *
+ */
+#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
+#define AIM_CB_SPECIAL_AUTHOTHER 0x0002
+#define AIM_CB_SPECIAL_CONNERR 0x0003
+#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
+#define AIM_CB_SPECIAL_FLAPVER 0x0005
+#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
+#define AIM_CB_SPECIAL_IMAGETRANSFER 0x007
+#define AIM_CB_SPECIAL_UNKNOWN 0xffff
+#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
+
+#endif /* __AIM_H__ */
diff --git a/protocols/oscar/aim_cbtypes.h b/protocols/oscar/aim_cbtypes.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/oscar/aim_cbtypes.h
diff --git a/protocols/oscar/aim_internal.h b/protocols/oscar/aim_internal.h
new file mode 100644
index 00000000..2e36c961
--- /dev/null
+++ b/protocols/oscar/aim_internal.h
@@ -0,0 +1,216 @@
+/*
+ * aim_internal.h -- prototypes/structs for the guts of libfaim
+ *
+ */
+
+#ifndef __AIM_INTERNAL_H__
+#define __AIM_INTERNAL_H__ 1
+
+typedef struct {
+ guint16 family;
+ guint16 subtype;
+ guint16 flags;
+ guint32 id;
+} aim_modsnac_t;
+
+#define AIM_MODULENAME_MAXLEN 16
+#define AIM_MODFLAG_MULTIFAMILY 0x0001
+typedef struct aim_module_s {
+ guint16 family;
+ guint16 version;
+ guint16 toolid;
+ guint16 toolversion;
+ guint16 flags;
+ char name[AIM_MODULENAME_MAXLEN+1];
+ int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs);
+ void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod);
+ void *priv;
+ struct aim_module_s *next;
+} aim_module_t;
+
+int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *));
+void aim__shutdownmodules(aim_session_t *sess);
+aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group);
+
+int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod);
+int admin_modfirst(aim_session_t *sess, aim_module_t *mod);
+int bos_modfirst(aim_session_t *sess, aim_module_t *mod);
+int search_modfirst(aim_session_t *sess, aim_module_t *mod);
+int stats_modfirst(aim_session_t *sess, aim_module_t *mod);
+int auth_modfirst(aim_session_t *sess, aim_module_t *mod);
+int msg_modfirst(aim_session_t *sess, aim_module_t *mod);
+int misc_modfirst(aim_session_t *sess, aim_module_t *mod);
+int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod);
+int chat_modfirst(aim_session_t *sess, aim_module_t *mod);
+int locate_modfirst(aim_session_t *sess, aim_module_t *mod);
+int general_modfirst(aim_session_t *sess, aim_module_t *mod);
+int ssi_modfirst(aim_session_t *sess, aim_module_t *mod);
+int icq_modfirst(aim_session_t *sess, aim_module_t *mod);
+
+int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *);
+int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *);
+
+#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset)
+
+void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
+int aim_recv(int fd, void *buf, size_t count);
+int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len);
+int aim_bstream_empty(aim_bstream_t *bs);
+int aim_bstream_curpos(aim_bstream_t *bs);
+int aim_bstream_setpos(aim_bstream_t *bs, int off);
+void aim_bstream_rewind(aim_bstream_t *bs);
+int aim_bstream_advance(aim_bstream_t *bs, int n);
+guint8 aimbs_get8(aim_bstream_t *bs);
+guint16 aimbs_get16(aim_bstream_t *bs);
+guint32 aimbs_get32(aim_bstream_t *bs);
+guint8 aimbs_getle8(aim_bstream_t *bs);
+guint16 aimbs_getle16(aim_bstream_t *bs);
+guint32 aimbs_getle32(aim_bstream_t *bs);
+int aimbs_put8(aim_bstream_t *bs, guint8 v);
+int aimbs_put16(aim_bstream_t *bs, guint16 v);
+int aimbs_put32(aim_bstream_t *bs, guint32 v);
+int aimbs_putle8(aim_bstream_t *bs, guint8 v);
+int aimbs_putle16(aim_bstream_t *bs, guint16 v);
+int aimbs_putle32(aim_bstream_t *bs, guint32 v);
+int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len);
+guint8 *aimbs_getraw(aim_bstream_t *bs, int len);
+char *aimbs_getstr(aim_bstream_t *bs, int len);
+int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len);
+int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len);
+
+int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn);
+
+int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
+flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *);
+aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen);
+void aim_frame_destroy(aim_frame_t *);
+int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
+int aim_tx_printqueue(aim_session_t *);
+void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);
+
+aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, u_short family, u_short type);
+
+/*
+ * Generic SNAC structure. Rarely if ever used.
+ */
+typedef struct aim_snac_s {
+ aim_snacid_t id;
+ guint16 family;
+ guint16 type;
+ guint16 flags;
+ void *data;
+ time_t issuetime;
+ struct aim_snac_s *next;
+} aim_snac_t;
+
+void aim_initsnachash(aim_session_t *sess);
+aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac);
+aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
+aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id);
+void aim_cleansnacs(aim_session_t *, int maxage);
+int aim_putsnac(aim_bstream_t *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id);
+
+aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
+void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
+
+int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *);
+
+int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
+
+/* Stored in ->priv of the service request SNAC for chats. */
+struct chatsnacinfo {
+ guint16 exchange;
+ char name[128];
+ guint16 instance;
+};
+
+/* these are used by aim_*_clientready */
+#define AIM_TOOL_JAVA 0x0001
+#define AIM_TOOL_MAC 0x0002
+#define AIM_TOOL_WIN16 0x0003
+#define AIM_TOOL_WIN32 0x0004
+#define AIM_TOOL_MAC68K 0x0005
+#define AIM_TOOL_MACPPC 0x0006
+#define AIM_TOOL_NEWWIN 0x0010
+struct aim_tool_version {
+ guint16 group;
+ guint16 version;
+ guint16 tool;
+ guint16 toolversion;
+};
+
+/*
+ * In SNACland, the terms 'family' and 'group' are synonymous -- the former
+ * is my term, the latter is AOL's.
+ */
+struct snacgroup {
+ guint16 group;
+ struct snacgroup *next;
+};
+
+struct snacpair {
+ guint16 group;
+ guint16 subtype;
+ struct snacpair *next;
+};
+
+struct rateclass {
+ guint16 classid;
+ guint32 windowsize;
+ guint32 clear;
+ guint32 alert;
+ guint32 limit;
+ guint32 disconnect;
+ guint32 current;
+ guint32 max;
+ guint8 unknown[5]; /* only present in versions >= 3 */
+ struct snacpair *members;
+ struct rateclass *next;
+};
+
+/*
+ * This is inside every connection. But it is a void * to anything
+ * outside of libfaim. It should remain that way. It's called data
+ * abstraction. Maybe you've heard of it. (Probably not if you're a
+ * libfaim user.)
+ *
+ */
+typedef struct aim_conn_inside_s {
+ struct snacgroup *groups;
+ struct rateclass *rates;
+} aim_conn_inside_t;
+
+void aim_conn_addgroup(aim_conn_t *conn, guint16 group);
+
+guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len);
+int aim_putcap(aim_bstream_t *bs, guint32 caps);
+
+int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type);
+aim_msgcookie_t *aim_mkcookie(guint8 *, int, void *);
+aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const unsigned char *, const int);
+int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+int aim_msgcookie_gettype(int reqclass);
+int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie);
+
+int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *);
+int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info);
+
+int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo);
+
+int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret);
+int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret);
+void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn);
+void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn);
+
+void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
+
+/* These are all handled internally now. */
+int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
+int aim_reqrates(aim_session_t *, aim_conn_t *);
+int aim_rates_addparam(aim_session_t *, aim_conn_t *);
+int aim_rates_delparam(aim_session_t *, aim_conn_t *);
+
+#endif /* __AIM_INTERNAL_H__ */
diff --git a/protocols/oscar/auth.c b/protocols/oscar/auth.c
new file mode 100644
index 00000000..c25a4604
--- /dev/null
+++ b/protocols/oscar/auth.c
@@ -0,0 +1,543 @@
+/*
+ * Deals with the authorizer (group 0x0017=23, and old-style non-SNAC login).
+ *
+ */
+
+#include <aim.h>
+
+#include "md5.h"
+
+static int aim_encode_password(const char *password, unsigned char *encoded);
+
+/*
+ * This just pushes the passed cookie onto the passed connection, without
+ * the SNAC header or any of that.
+ *
+ * Very commonly used, as every connection except auth will require this to
+ * be the first thing you send.
+ *
+ */
+int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const guint8 *chipsahoy)
+{
+ aim_frame_t *fr;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0001, 4+2+2+AIM_COOKIELEN)))
+ return -ENOMEM;
+
+ aimbs_put32(&fr->data, 0x00000001);
+ aim_addtlvtochain_raw(&tl, 0x0006, AIM_COOKIELEN, chipsahoy);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Normally the FLAP version is sent as the first few bytes of the cookie,
+ * meaning you generally never call this.
+ *
+ * But there are times when something might want it seperate. Specifically,
+ * libfaim sends this internally when doing SNAC login.
+ *
+ */
+int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *fr;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4)))
+ return -ENOMEM;
+
+ aimbs_put32(&fr->data, 0x00000001);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * This is a bit confusing.
+ *
+ * Normal SNAC login goes like this:
+ * - connect
+ * - server sends flap version
+ * - client sends flap version
+ * - client sends screen name (17/6)
+ * - server sends hash key (17/7)
+ * - client sends auth request (17/2 -- aim_send_login)
+ * - server yells
+ *
+ * XOR login (for ICQ) goes like this:
+ * - connect
+ * - server sends flap version
+ * - client sends auth request which contains flap version (aim_send_login)
+ * - server yells
+ *
+ * For the client API, we make them implement the most complicated version,
+ * and for the simpler version, we fake it and make it look like the more
+ * complicated process.
+ *
+ * This is done by giving the client a faked key, just so we can convince
+ * them to call aim_send_login right away, which will detect the session
+ * flag that says this is XOR login and ignore the key, sending an ICQ
+ * login request instead of the normal SNAC one.
+ *
+ * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
+ *
+ * XXX This may cause problems if the client relies on callbacks only
+ * being called from the context of aim_rxdispatch()...
+ *
+ */
+static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t fr;
+ aim_rxcallback_t userfunc;
+
+ sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
+ sess->flags |= AIM_SESS_FLAGS_XORLOGIN;
+
+ fr.conn = conn;
+
+ if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007)))
+ userfunc(sess, &fr, "");
+
+ return 0;
+}
+
+/*
+ * In AIM 3.5 protocol, the first stage of login is to request login from the
+ * Authorizer, passing it the screen name for verification. If the name is
+ * invalid, a 0017/0003 is spit back, with the standard error contents. If
+ * valid, a 0017/0007 comes back, which is the signal to send it the main
+ * login command (0017/0002).
+ *
+ */
+int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!sess || !conn || !sn)
+ return -EINVAL;
+
+ if ((sn[0] >= '0') && (sn[0] <= '9'))
+ return goddamnicq(sess, conn, sn);
+
+ sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+
+ aim_sendflapver(sess, conn);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid);
+
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Part two of the ICQ hack. Note the ignoring of the key and clientinfo.
+ */
+static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password)
+{
+ static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2001b.5.17.1.3642.85"};
+ static const char lang[] = {"en"};
+ static const char country[] = {"us"};
+ aim_frame_t *fr;
+ aim_tlvlist_t *tl = NULL;
+ guint8 *password_encoded;
+
+ if (!(password_encoded = (guint8 *) g_malloc(strlen(password))))
+ return -ENOMEM;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) {
+ g_free(password_encoded);
+ return -ENOMEM;
+ }
+
+ aim_encode_password(password, password_encoded);
+
+ aimbs_put32(&fr->data, 0x00000001);
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+ aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded);
+ aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), (guint8 *)clientstr);
+ aim_addtlvtochain16(&tl, 0x0016, 0x010a); /* cliend ID */
+ aim_addtlvtochain16(&tl, 0x0017, 0x0005); /* major version */
+ aim_addtlvtochain16(&tl, 0x0018, 0x0011); /* minor version */
+ aim_addtlvtochain16(&tl, 0x0019, 0x0001); /* point version */
+ aim_addtlvtochain16(&tl, 0x001a, 0x0e3a); /* build */
+ aim_addtlvtochain32(&tl, 0x0014, 0x00000055); /* distribution chan */
+ aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), (guint8 *)lang);
+ aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), (guint8 *)country);
+
+ aim_writetlvchain(&fr->data, &tl);
+
+ g_free(password_encoded);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * send_login(int socket, char *sn, char *password)
+ *
+ * This is the initial login request packet.
+ *
+ * NOTE!! If you want/need to make use of the aim_sendmemblock() function,
+ * then the client information you send here must exactly match the
+ * executable that you're pulling the data from.
+ *
+ * WinAIM 4.8.2540
+ * clientstring = "AOL Instant Messenger (SM), version 4.8.2540/WIN32"
+ * clientid = 0x0109
+ * major = 0x0004
+ * minor = 0x0008
+ * point = 0x0000
+ * build = 0x09ec
+ * t(0x0014) = 0x000000af
+ * t(0x004a) = 0x01
+ *
+ * WinAIM 4.3.2188:
+ * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32"
+ * clientid = 0x0109
+ * major = 0x0400
+ * minor = 0x0003
+ * point = 0x0000
+ * build = 0x088c
+ * unknown = 0x00000086
+ * lang = "en"
+ * country = "us"
+ * unknown4a = 0x01
+ *
+ * Latest WinAIM that libfaim can emulate without server-side buddylists:
+ * clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32"
+ * clientid = 0x0004
+ * major = 0x0004
+ * minor = 0x0001
+ * point = 0x0000
+ * build = 0x07da
+ * unknown= 0x0000004b
+ *
+ * WinAIM 3.5.1670:
+ * clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32"
+ * clientid = 0x0004
+ * major = 0x0003
+ * minor = 0x0005
+ * point = 0x0000
+ * build = 0x0686
+ * unknown =0x0000002a
+ *
+ * Java AIM 1.1.19:
+ * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
+ * clientid = 0x0001
+ * major = 0x0001
+ * minor = 0x0001
+ * point = (not sent)
+ * build = 0x0013
+ * unknown= (not sent)
+ *
+ * AIM for Linux 1.1.112:
+ * clientstring = "AOL Instant Messenger (SM)"
+ * clientid = 0x1d09
+ * major = 0x0001
+ * minor = 0x0001
+ * point = 0x0001
+ * build = 0x0070
+ * unknown= 0x0000008b
+ * serverstore = 0x01
+ *
+ */
+int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key)
+{
+ aim_frame_t *fr;
+ aim_tlvlist_t *tl = NULL;
+ guint8 digest[16];
+ aim_snacid_t snacid;
+
+ if (!ci || !sn || !password)
+ return -EINVAL;
+
+ /*
+ * What the XORLOGIN flag _really_ means is that its an ICQ login,
+ * which is really stupid and painful, so its not done here.
+ *
+ */
+ if (sess->flags & AIM_SESS_FLAGS_XORLOGIN)
+ return goddamnicq2(sess, conn, sn, password);
+
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
+
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+
+ aim_encode_password_md5(password, key, digest);
+ aim_addtlvtochain_raw(&tl, 0x0025, 16, digest);
+
+ /*
+ * Newer versions of winaim have an empty type x004c TLV here.
+ */
+
+ if (ci->clientstring)
+ aim_addtlvtochain_raw(&tl, 0x0003, strlen(ci->clientstring), (guint8 *)ci->clientstring);
+ aim_addtlvtochain16(&tl, 0x0016, (guint16)ci->clientid);
+ aim_addtlvtochain16(&tl, 0x0017, (guint16)ci->major);
+ aim_addtlvtochain16(&tl, 0x0018, (guint16)ci->minor);
+ aim_addtlvtochain16(&tl, 0x0019, (guint16)ci->point);
+ aim_addtlvtochain16(&tl, 0x001a, (guint16)ci->build);
+ aim_addtlvtochain_raw(&tl, 0x000e, strlen(ci->country), (guint8 *)ci->country);
+ aim_addtlvtochain_raw(&tl, 0x000f, strlen(ci->lang), (guint8 *)ci->lang);
+
+ /*
+ * If set, old-fashioned buddy lists will not work. You will need
+ * to use SSI.
+ */
+ aim_addtlvtochain8(&tl, 0x004a, 0x01);
+
+ aim_writetlvchain(&fr->data, &tl);
+
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_encode_password_md5(const char *password, const char *key, guint8 *digest)
+{
+ md5_state_t state;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)key, strlen(key));
+ md5_append(&state, (const md5_byte_t *)password, strlen(password));
+ md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+ md5_finish(&state, (md5_byte_t *)digest);
+
+ return 0;
+}
+
+/**
+ * aim_encode_password - Encode a password using old XOR method
+ * @password: incoming password
+ * @encoded: buffer to put encoded password
+ *
+ * This takes a const pointer to a (null terminated) string
+ * containing the unencoded password. It also gets passed
+ * an already allocated buffer to store the encoded password.
+ * This buffer should be the exact length of the password without
+ * the null. The encoded password buffer /is not %NULL terminated/.
+ *
+ * The encoding_table seems to be a fixed set of values. We'll
+ * hope it doesn't change over time!
+ *
+ * This is only used for the XOR method, not the better MD5 method.
+ *
+ */
+static int aim_encode_password(const char *password, guint8 *encoded)
+{
+ guint8 encoding_table[] = {
+#if 0 /* old v1 table */
+ 0xf3, 0xb3, 0x6c, 0x99,
+ 0x95, 0x3f, 0xac, 0xb6,
+ 0xc5, 0xfa, 0x6b, 0x63,
+ 0x69, 0x6c, 0xc3, 0x9f
+#else /* v2.1 table, also works for ICQ */
+ 0xf3, 0x26, 0x81, 0xc4,
+ 0x39, 0x86, 0xdb, 0x92,
+ 0x71, 0xa3, 0xb9, 0xe6,
+ 0x53, 0x7a, 0x95, 0x7c
+#endif
+ };
+ int i;
+
+ for (i = 0; i < strlen(password); i++)
+ encoded[i] = (password[i] ^ encoding_table[i]);
+
+ return 0;
+}
+
+/*
+ * This is sent back as a general response to the login command.
+ * It can be either an error or a success, depending on the
+ * precense of certain TLVs.
+ *
+ * The client should check the value passed as errorcode. If
+ * its nonzero, there was an error.
+ *
+ */
+static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_tlvlist_t *tlvlist;
+ aim_rxcallback_t userfunc;
+ struct aim_authresp_info info;
+ int ret = 0;
+
+ memset(&info, 0, sizeof(info));
+
+ /*
+ * Read block of TLVs. All further data is derived
+ * from what is parsed here.
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * No matter what, we should have a screen name.
+ */
+ memset(sess->sn, 0, sizeof(sess->sn));
+ if (aim_gettlv(tlvlist, 0x0001, 1)) {
+ info.sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+ strncpy(sess->sn, info.sn, sizeof(sess->sn));
+ }
+
+ /*
+ * Check for an error code. If so, we should also
+ * have an error url.
+ */
+ if (aim_gettlv(tlvlist, 0x0008, 1))
+ info.errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
+ if (aim_gettlv(tlvlist, 0x0004, 1))
+ info.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+
+ /*
+ * BOS server address.
+ */
+ if (aim_gettlv(tlvlist, 0x0005, 1))
+ info.bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
+
+ /*
+ * Authorization cookie.
+ */
+ if (aim_gettlv(tlvlist, 0x0006, 1)) {
+ aim_tlv_t *tmptlv;
+
+ tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+
+ info.cookie = tmptlv->value;
+ }
+
+ /*
+ * The email address attached to this account
+ * Not available for ICQ logins.
+ */
+ if (aim_gettlv(tlvlist, 0x0011, 1))
+ info.email = aim_gettlv_str(tlvlist, 0x0011, 1);
+
+ /*
+ * The registration status. (Not real sure what it means.)
+ * Not available for ICQ logins.
+ *
+ * 1 = No disclosure
+ * 2 = Limited disclosure
+ * 3 = Full disclosure
+ *
+ * This has to do with whether your email address is available
+ * to other users or not. AFAIK, this feature is no longer used.
+ *
+ */
+ if (aim_gettlv(tlvlist, 0x0013, 1))
+ info.regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
+
+ if (aim_gettlv(tlvlist, 0x0040, 1))
+ info.latestbeta.build = aim_gettlv32(tlvlist, 0x0040, 1);
+ if (aim_gettlv(tlvlist, 0x0041, 1))
+ info.latestbeta.url = aim_gettlv_str(tlvlist, 0x0041, 1);
+ if (aim_gettlv(tlvlist, 0x0042, 1))
+ info.latestbeta.info = aim_gettlv_str(tlvlist, 0x0042, 1);
+ if (aim_gettlv(tlvlist, 0x0043, 1))
+ info.latestbeta.name = aim_gettlv_str(tlvlist, 0x0043, 1);
+ if (aim_gettlv(tlvlist, 0x0048, 1))
+ ; /* no idea what this is */
+
+ if (aim_gettlv(tlvlist, 0x0044, 1))
+ info.latestrelease.build = aim_gettlv32(tlvlist, 0x0044, 1);
+ if (aim_gettlv(tlvlist, 0x0045, 1))
+ info.latestrelease.url = aim_gettlv_str(tlvlist, 0x0045, 1);
+ if (aim_gettlv(tlvlist, 0x0046, 1))
+ info.latestrelease.info = aim_gettlv_str(tlvlist, 0x0046, 1);
+ if (aim_gettlv(tlvlist, 0x0047, 1))
+ info.latestrelease.name = aim_gettlv_str(tlvlist, 0x0047, 1);
+ if (aim_gettlv(tlvlist, 0x0049, 1))
+ ; /* no idea what this is */
+
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
+ ret = userfunc(sess, rx, &info);
+
+ g_free(info.sn);
+ g_free(info.bosip);
+ g_free(info.errorurl);
+ g_free(info.email);
+ g_free(info.latestrelease.name);
+ g_free(info.latestrelease.url);
+ g_free(info.latestrelease.info);
+ g_free(info.latestbeta.name);
+ g_free(info.latestbeta.url);
+ g_free(info.latestbeta.info);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed
+ * by only its length in a two byte word.
+ *
+ * Calls the client, which should then use the value to call aim_send_login.
+ *
+ */
+static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int keylen, ret = 1;
+ aim_rxcallback_t userfunc;
+ char *keystr;
+
+ keylen = aimbs_get16(bs);
+ keystr = aimbs_getstr(bs, keylen);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, keystr);
+
+ g_free(keystr);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return parse(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0007)
+ return keyparse(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int auth_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0017;
+ mod->version = 0x0000;
+ mod->flags = 0;
+ strncpy(mod->name, "auth", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
diff --git a/protocols/oscar/bos.c b/protocols/oscar/bos.c
new file mode 100644
index 00000000..e7f12f79
--- /dev/null
+++ b/protocols/oscar/bos.c
@@ -0,0 +1,161 @@
+#include <aim.h>
+#include "bos.h"
+
+/* Request BOS rights (group 9, type 2) */
+int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
+}
+
+/* BOS Rights (group 9, type 3) */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ aim_tlvlist_t *tlvlist;
+ guint16 maxpermits = 0, maxdenies = 0;
+ int ret = 0;
+
+ /*
+ * TLVs follow
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * TLV type 0x0001: Maximum number of buddies on permit list.
+ */
+ if (aim_gettlv(tlvlist, 0x0001, 1))
+ maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
+
+ /*
+ * TLV type 0x0002: Maximum number of buddies on deny list.
+ */
+ if (aim_gettlv(tlvlist, 0x0002, 1))
+ maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, maxpermits, maxdenies);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Set group permisson mask (group 9, type 4)
+ *
+ * Normally 0x1f (all classes).
+ *
+ * The group permission mask allows you to keep users of a certain
+ * class or classes from talking to you. The mask should be
+ * a bitwise OR of all the user classes you want to see you.
+ *
+ */
+int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, guint32 mask)
+{
+ return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
+}
+
+/*
+ * Modify permit/deny lists (group 9, types 5, 6, 7, and 8)
+ *
+ * Changes your visibility depending on changetype:
+ *
+ * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
+ * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
+ * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
+ * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
+ *
+ * list should be a list of
+ * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
+ *
+ * Equivelents to options in WinAIM:
+ * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
+ * with only your name on it.
+ * - Allow only users on my Buddy List: Send an
+ * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
+ * buddy list
+ * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD
+ * with everyone listed that you want to see you.
+ * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only
+ * yourself in the list
+ * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
+ * the list of users to be blocked
+ *
+ * XXX ye gods.
+ */
+int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist)
+{
+ aim_frame_t *fr;
+ int packlen = 0;
+ guint16 subtype;
+ char *localcpy = NULL, *tmpptr = NULL;
+ int i;
+ int listcount;
+ aim_snacid_t snacid;
+
+ if (!denylist)
+ return -EINVAL;
+
+ if (changetype == AIM_VISIBILITYCHANGE_PERMITADD)
+ subtype = 0x05;
+ else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE)
+ subtype = 0x06;
+ else if (changetype == AIM_VISIBILITYCHANGE_DENYADD)
+ subtype = 0x07;
+ else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE)
+ subtype = 0x08;
+ else
+ return -EINVAL;
+
+ localcpy = g_strdup(denylist);
+
+ listcount = aimutil_itemcnt(localcpy, '&');
+ packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) {
+ g_free(localcpy);
+ return -ENOMEM;
+ }
+
+ snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid);
+
+ for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
+ tmpptr = aimutil_itemidx(localcpy, i, '&');
+
+ aimbs_put8(&fr->data, strlen(tmpptr));
+ aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr));
+
+ g_free(tmpptr);
+ }
+ g_free(localcpy);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return rights(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int bos_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0009;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "bos", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+
diff --git a/protocols/oscar/bos.h b/protocols/oscar/bos.h
new file mode 100644
index 00000000..e7c2cbcd
--- /dev/null
+++ b/protocols/oscar/bos.h
@@ -0,0 +1,14 @@
+#ifndef __OSCAR_BOS_H__
+#define __OSCAR_BOS_H__
+
+#define AIM_CB_FAM_BOS 0x0009
+
+/*
+ * SNAC Family: Misc BOS Services.
+ */
+#define AIM_CB_BOS_ERROR 0x0001
+#define AIM_CB_BOS_RIGHTSQUERY 0x0002
+#define AIM_CB_BOS_RIGHTS 0x0003
+#define AIM_CB_BOS_DEFAULT 0xffff
+
+#endif /* __OSCAR_BOS_H__ */
diff --git a/protocols/oscar/buddylist.c b/protocols/oscar/buddylist.c
new file mode 100644
index 00000000..a7b461f2
--- /dev/null
+++ b/protocols/oscar/buddylist.c
@@ -0,0 +1,150 @@
+#include <aim.h>
+#include "buddylist.h"
+
+/*
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure. Its close enough to run
+ * through aim_extractuserinfo() however.
+ *
+ * Although the offgoing notification contains no information,
+ * it is still in a format parsable by extractuserinfo.
+ *
+ */
+static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_userinfo_t userinfo;
+ aim_rxcallback_t userfunc;
+
+ aim_extractuserinfo(sess, bs, &userinfo);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, &userinfo);
+
+ return 0;
+}
+
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ aim_tlvlist_t *tlvlist;
+ guint16 maxbuddies = 0, maxwatchers = 0;
+ int ret = 0;
+
+ /*
+ * TLVs follow
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * TLV type 0x0001: Maximum number of buddies.
+ */
+ if (aim_gettlv(tlvlist, 0x0001, 1))
+ maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1);
+
+ /*
+ * TLV type 0x0002: Maximum number of watchers.
+ *
+ * Watchers are other users who have you on their buddy
+ * list. (This is called the "reverse list" by a certain
+ * other IM protocol.)
+ *
+ */
+ if (aim_gettlv(tlvlist, 0x0002, 1))
+ maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
+
+ /*
+ * TLV type 0x0003: Unknown.
+ *
+ * ICQ only?
+ */
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, maxbuddies, maxwatchers);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return rights(sess, mod, rx, snac, bs);
+ else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
+ return buddychange(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0003;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "buddylist", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+/*
+ * aim_add_buddy()
+ *
+ * Adds a single buddy to your buddy list after login.
+ *
+ * XXX this should just be an extension of setbuddylist()
+ *
+ */
+int aim_add_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sn || !strlen(sn))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
+ aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * XXX generalise to support removing multiple buddies (basically, its
+ * the same as setbuddylist() but with a different snac subtype).
+ *
+ */
+int aim_remove_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sn || !strlen(sn))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
+ aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid);
+
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
diff --git a/protocols/oscar/buddylist.h b/protocols/oscar/buddylist.h
new file mode 100644
index 00000000..9a325279
--- /dev/null
+++ b/protocols/oscar/buddylist.h
@@ -0,0 +1,23 @@
+#ifndef __OSCAR_BUDDYLIST_H__
+#define __OSCAR_BUDDYLIST_H__
+
+#define AIM_CB_FAM_BUD 0x0003
+
+/*
+ * SNAC Family: Buddy List Management Services.
+ */
+#define AIM_CB_BUD_ERROR 0x0001
+#define AIM_CB_BUD_REQRIGHTS 0x0002
+#define AIM_CB_BUD_RIGHTSINFO 0x0003
+#define AIM_CB_BUD_ADDBUDDY 0x0004
+#define AIM_CB_BUD_REMBUDDY 0x0005
+#define AIM_CB_BUD_REJECT 0x000a
+#define AIM_CB_BUD_ONCOMING 0x000b
+#define AIM_CB_BUD_OFFGOING 0x000c
+#define AIM_CB_BUD_DEFAULT 0xffff
+
+/* aim_buddylist.c */
+int aim_add_buddy(aim_session_t *, aim_conn_t *, const char *);
+int aim_remove_buddy(aim_session_t *, aim_conn_t *, const char *);
+
+#endif /* __OSCAR_BUDDYLIST_H__ */
diff --git a/protocols/oscar/chat.c b/protocols/oscar/chat.c
new file mode 100644
index 00000000..60aabc79
--- /dev/null
+++ b/protocols/oscar/chat.c
@@ -0,0 +1,713 @@
+/*
+ * aim_chat.c
+ *
+ * Routines for the Chat service.
+ *
+ */
+
+#include <aim.h>
+#include <glib.h>
+#include "info.h"
+
+/* Stored in the ->priv of chat connections */
+struct chatconnpriv {
+ guint16 exchange;
+ char *name;
+ guint16 instance;
+};
+
+void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
+{
+ struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv;
+
+ if (ccp)
+ g_free(ccp->name);
+ g_free(ccp);
+
+ return;
+}
+
+char *aim_chat_getname(aim_conn_t *conn)
+{
+ struct chatconnpriv *ccp;
+
+ if (!conn)
+ return NULL;
+
+ if (conn->type != AIM_CONN_TYPE_CHAT)
+ return NULL;
+
+ ccp = (struct chatconnpriv *)conn->priv;
+
+ return ccp->name;
+}
+
+/* XXX get this into conn.c -- evil!! */
+aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
+{
+ aim_conn_t *cur;
+
+ for (cur = sess->connlist; cur; cur = cur->next) {
+ struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv;
+
+ if (cur->type != AIM_CONN_TYPE_CHAT)
+ continue;
+ if (!cur->priv) {
+ do_error_dialog(sess->aux_data, "chat connection with no name!", "Gaim");
+ continue;
+ }
+
+ if (strcmp(ccp->name, name) == 0)
+ break;
+ }
+
+ return cur;
+}
+
+int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+ struct chatconnpriv *ccp;
+
+ if (!conn || !roomname)
+ return -EINVAL;
+
+ if (conn->priv)
+ g_free(conn->priv);
+
+ if (!(ccp = g_malloc(sizeof(struct chatconnpriv))))
+ return -ENOMEM;
+
+ ccp->exchange = exchange;
+ ccp->name = g_strdup(roomname);
+ ccp->instance = instance;
+
+ conn->priv = (void *)ccp;
+
+ return 0;
+}
+
+/*
+ * Send a Chat Message.
+ *
+ * Possible flags:
+ * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages
+ * should be sent to their sender.
+ * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse
+ * (Note that WinAIM does not honor this,
+ * and displays the message as normal.)
+ *
+ * XXX convert this to use tlvchains
+ */
+int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen)
+{
+ int i;
+ aim_frame_t *fr;
+ aim_msgcookie_t *cookie;
+ aim_snacid_t snacid;
+ guint8 ckstr[8];
+ aim_tlvlist_t *otl = NULL, *itl = NULL;
+
+ if (!sess || !conn || !msg || (msglen <= 0))
+ return 0;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
+
+
+ /*
+ * Generate a random message cookie.
+ *
+ * XXX mkcookie should generate the cookie and cache it in one
+ * operation to preserve uniqueness.
+ *
+ */
+ for (i = 0; i < sizeof(ckstr); i++)
+ aimutil_put8(ckstr+i, (guint8) rand());
+
+ cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
+ cookie->data = NULL; /* XXX store something useful here */
+
+ aim_cachecookie(sess, cookie);
+
+ for (i = 0; i < sizeof(ckstr); i++)
+ aimbs_put8(&fr->data, ckstr[i]);
+
+
+ /*
+ * Channel ID.
+ */
+ aimbs_put16(&fr->data, 0x0003);
+
+
+ /*
+ * Type 1: Flag meaning this message is destined to the room.
+ */
+ aim_addtlvtochain_noval(&otl, 0x0001);
+
+ /*
+ * Type 6: Reflect
+ */
+ if (!(flags & AIM_CHATFLAGS_NOREFLECT))
+ aim_addtlvtochain_noval(&otl, 0x0006);
+
+ /*
+ * Type 7: Autoresponse
+ */
+ if (flags & AIM_CHATFLAGS_AWAY)
+ aim_addtlvtochain_noval(&otl, 0x0007);
+
+ /*
+ * SubTLV: Type 1: Message
+ */
+ aim_addtlvtochain_raw(&itl, 0x0001, strlen(msg), (guint8 *)msg);
+
+ /*
+ * Type 5: Message block. Contains more TLVs.
+ *
+ * This could include other information... We just
+ * put in a message TLV however.
+ *
+ */
+ aim_addtlvtochain_frozentlvlist(&otl, 0x0005, &itl);
+
+ aim_writetlvchain(&fr->data, &otl);
+
+ aim_freetlvchain(&itl);
+ aim_freetlvchain(&otl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
+{
+ guint8 *buf;
+ int buflen;
+ aim_bstream_t bs;
+
+ buflen = 2 + 1 + strlen(roomname) + 2;
+
+ if (!(buf = g_malloc(buflen)))
+ return 0;
+
+ aim_bstream_init(&bs, buf, buflen);
+
+ aimbs_put16(&bs, exchange);
+ aimbs_put8(&bs, strlen(roomname));
+ aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname));
+ aimbs_put16(&bs, instance);
+
+ aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+ g_free(buf);
+
+ return 0;
+}
+
+/*
+ * Join a room of name roomname. This is the first step to joining an
+ * already created room. It's basically a Service Request for
+ * family 0x000e, with a little added on to specify the exchange and room
+ * name.
+ */
+int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+ struct chatsnacinfo csi;
+
+ if (!sess || !conn || !roomname || !strlen(roomname))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+ return -ENOMEM;
+
+ memset(&csi, 0, sizeof(csi));
+ csi.exchange = exchange;
+ strncpy(csi.name, roomname, sizeof(csi.name));
+ csi.instance = instance;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
+ aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
+
+ /*
+ * Requesting service chat (0x000e)
+ */
+ aimbs_put16(&fr->data, 0x000e);
+
+ aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
+{
+ int namelen;
+
+ if (!bs || !outinfo)
+ return 0;
+
+ outinfo->exchange = aimbs_get16(bs);
+ namelen = aimbs_get8(bs);
+ outinfo->name = aimbs_getstr(bs, namelen);
+ outinfo->instance = aimbs_get16(bs);
+
+ return 0;
+}
+
+int aim_chat_leaveroom(aim_session_t *sess, const char *name)
+{
+ aim_conn_t *conn;
+
+ if (!(conn = aim_chat_getconn(sess, name)))
+ return -ENOENT;
+
+ aim_conn_close(conn);
+
+ return 0;
+}
+
+/*
+ * conn must be a BOS connection!
+ */
+int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
+{
+ int i;
+ aim_frame_t *fr;
+ aim_msgcookie_t *cookie;
+ struct aim_invite_priv *priv;
+ guint8 ckstr[8];
+ aim_snacid_t snacid;
+ aim_tlvlist_t *otl = NULL, *itl = NULL;
+ guint8 *hdr;
+ int hdrlen;
+ aim_bstream_t hdrbs;
+
+ if (!sess || !conn || !sn || !msg || !roomname)
+ return -EINVAL;
+
+ if (conn->type != AIM_CONN_TYPE_BOS)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+
+ /*
+ * Cookie
+ */
+ for (i = 0; i < sizeof(ckstr); i++)
+ aimutil_put8(ckstr, (guint8) rand());
+
+ /* XXX should be uncached by an unwritten 'invite accept' handler */
+ if ((priv = g_malloc(sizeof(struct aim_invite_priv)))) {
+ priv->sn = g_strdup(sn);
+ priv->roomname = g_strdup(roomname);
+ priv->exchange = exchange;
+ priv->instance = instance;
+ }
+
+ if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv)))
+ aim_cachecookie(sess, cookie);
+ else
+ g_free(priv);
+
+ for (i = 0; i < sizeof(ckstr); i++)
+ aimbs_put8(&fr->data, ckstr[i]);
+
+
+ /*
+ * Channel (2)
+ */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /*
+ * Dest sn
+ */
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ /*
+ * TLV t(0005)
+ *
+ * Everything else is inside this TLV.
+ *
+ * Sigh. AOL was rather inconsistent right here. So we have
+ * to play some minor tricks. Right inside the type 5 is some
+ * raw data, followed by a series of TLVs.
+ *
+ */
+ hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2;
+ hdr = g_malloc(hdrlen);
+ aim_bstream_init(&hdrbs, hdr, hdrlen);
+
+ aimbs_put16(&hdrbs, 0x0000); /* Unknown! */
+ aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */
+ aim_putcap(&hdrbs, AIM_CAPS_CHAT);
+
+ aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+ aim_addtlvtochain_noval(&itl, 0x000f);
+ aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), (guint8 *)msg);
+ aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance);
+ aim_writetlvchain(&hdrbs, &itl);
+
+ aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+ aim_writetlvchain(&fr->data, &otl);
+
+ g_free(hdr);
+ aim_freetlvchain(&itl);
+ aim_freetlvchain(&otl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * General room information. Lots of stuff.
+ *
+ * Values I know are in here but I havent attached
+ * them to any of the 'Unknown's:
+ * - Language (English)
+ *
+ * SNAC 000e/0002
+ */
+static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_userinfo_t *userinfo = NULL;
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ int usercount = 0;
+ guint8 detaillevel = 0;
+ char *roomname = NULL;
+ struct aim_chat_roominfo roominfo;
+ guint16 tlvcount = 0;
+ aim_tlvlist_t *tlvlist;
+ char *roomdesc = NULL;
+ guint16 flags = 0;
+ guint32 creationtime = 0;
+ guint16 maxmsglen = 0, maxvisiblemsglen = 0;
+ guint16 unknown_d2 = 0, unknown_d5 = 0;
+
+ aim_chat_readroominfo(bs, &roominfo);
+
+ detaillevel = aimbs_get8(bs);
+
+ if (detaillevel != 0x02) {
+ do_error_dialog(sess->aux_data, "Only detaillevel 0x2 is support at the moment", "Gaim");
+ return 1;
+ }
+
+ tlvcount = aimbs_get16(bs);
+
+ /*
+ * Everything else are TLVs.
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * TLV type 0x006a is the room name in Human Readable Form.
+ */
+ if (aim_gettlv(tlvlist, 0x006a, 1))
+ roomname = aim_gettlv_str(tlvlist, 0x006a, 1);
+
+ /*
+ * Type 0x006f: Number of occupants.
+ */
+ if (aim_gettlv(tlvlist, 0x006f, 1))
+ usercount = aim_gettlv16(tlvlist, 0x006f, 1);
+
+ /*
+ * Type 0x0073: Occupant list.
+ */
+ if (aim_gettlv(tlvlist, 0x0073, 1)) {
+ int curoccupant = 0;
+ aim_tlv_t *tmptlv;
+ aim_bstream_t occbs;
+
+ tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
+
+ /* Allocate enough userinfo structs for all occupants */
+ userinfo = g_new0(aim_userinfo_t, usercount);
+
+ aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
+
+ while (curoccupant < usercount)
+ aim_extractuserinfo(sess, &occbs, &userinfo[curoccupant++]);
+ }
+
+ /*
+ * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
+ */
+ if (aim_gettlv(tlvlist, 0x00c9, 1))
+ flags = aim_gettlv16(tlvlist, 0x00c9, 1);
+
+ /*
+ * Type 0x00ca: Creation time (4 bytes)
+ */
+ if (aim_gettlv(tlvlist, 0x00ca, 1))
+ creationtime = aim_gettlv32(tlvlist, 0x00ca, 1);
+
+ /*
+ * Type 0x00d1: Maximum Message Length
+ */
+ if (aim_gettlv(tlvlist, 0x00d1, 1))
+ maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1);
+
+ /*
+ * Type 0x00d2: Unknown. (2 bytes)
+ */
+ if (aim_gettlv(tlvlist, 0x00d2, 1))
+ unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1);
+
+ /*
+ * Type 0x00d3: Room Description
+ */
+ if (aim_gettlv(tlvlist, 0x00d3, 1))
+ roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
+
+ /*
+ * Type 0x000d4: Unknown (flag only)
+ */
+ if (aim_gettlv(tlvlist, 0x000d4, 1))
+ ;
+
+ /*
+ * Type 0x00d5: Unknown. (1 byte)
+ */
+ if (aim_gettlv(tlvlist, 0x00d5, 1))
+ unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1);
+
+
+ /*
+ * Type 0x00d6: Encoding 1 ("us-ascii")
+ */
+ if (aim_gettlv(tlvlist, 0x000d6, 1))
+ ;
+
+ /*
+ * Type 0x00d7: Language 1 ("en")
+ */
+ if (aim_gettlv(tlvlist, 0x000d7, 1))
+ ;
+
+ /*
+ * Type 0x00d8: Encoding 2 ("us-ascii")
+ */
+ if (aim_gettlv(tlvlist, 0x000d8, 1))
+ ;
+
+ /*
+ * Type 0x00d9: Language 2 ("en")
+ */
+ if (aim_gettlv(tlvlist, 0x000d9, 1))
+ ;
+
+ /*
+ * Type 0x00da: Maximum visible message length
+ */
+ if (aim_gettlv(tlvlist, 0x000da, 1))
+ maxvisiblemsglen = aim_gettlv16(tlvlist, 0x00da, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+ ret = userfunc(sess,
+ rx,
+ &roominfo,
+ roomname,
+ usercount,
+ userinfo,
+ roomdesc,
+ flags,
+ creationtime,
+ maxmsglen,
+ unknown_d2,
+ unknown_d5,
+ maxvisiblemsglen);
+ }
+
+ g_free(roominfo.name);
+ g_free(userinfo);
+ g_free(roomname);
+ g_free(roomdesc);
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_userinfo_t *userinfo = NULL;
+ aim_rxcallback_t userfunc;
+ int curcount = 0, ret = 0;
+
+ while (aim_bstream_empty(bs)) {
+ curcount++;
+ userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t));
+ aim_extractuserinfo(sess, bs, &userinfo[curcount-1]);
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, curcount, userinfo);
+
+ g_free(userinfo);
+
+ return ret;
+}
+
+/*
+ * We could probably include this in the normal ICBM parsing
+ * code as channel 0x0003, however, since only the start
+ * would be the same, we might as well do it here.
+ *
+ * General outline of this SNAC:
+ * snac
+ * cookie
+ * channel id
+ * tlvlist
+ * unknown
+ * source user info
+ * name
+ * evility
+ * userinfo tlvs
+ * online time
+ * etc
+ * message metatlv
+ * message tlv
+ * message string
+ * possibly others
+ *
+ */
+static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_userinfo_t userinfo;
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ guint8 *cookie;
+ guint16 channel;
+ aim_tlvlist_t *otl;
+ char *msg = NULL;
+ aim_msgcookie_t *ck;
+
+ memset(&userinfo, 0, sizeof(aim_userinfo_t));
+
+ /*
+ * ICBM Cookie. Uncache it.
+ */
+ cookie = aimbs_getraw(bs, 8);
+
+ if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+ g_free(ck->data);
+ g_free(ck);
+ }
+
+ /*
+ * Channel ID
+ *
+ * Channels 1 and 2 are implemented in the normal ICBM
+ * parser.
+ *
+ * We only do channel 3 here.
+ *
+ */
+ channel = aimbs_get16(bs);
+
+ if (channel != 0x0003) {
+ do_error_dialog(sess->aux_data, "unknown channel!", "Gaim");
+ return 0;
+ }
+
+ /*
+ * Start parsing TLVs right away.
+ */
+ otl = aim_readtlvchain(bs);
+
+ /*
+ * Type 0x0003: Source User Information
+ */
+ if (aim_gettlv(otl, 0x0003, 1)) {
+ aim_tlv_t *userinfotlv;
+ aim_bstream_t tbs;
+
+ userinfotlv = aim_gettlv(otl, 0x0003, 1);
+
+ aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
+ aim_extractuserinfo(sess, &tbs, &userinfo);
+ }
+
+ /*
+ * Type 0x0001: If present, it means it was a message to the
+ * room (as opposed to a whisper).
+ */
+ if (aim_gettlv(otl, 0x0001, 1))
+ ;
+
+ /*
+ * Type 0x0005: Message Block. Conains more TLVs.
+ */
+ if (aim_gettlv(otl, 0x0005, 1)) {
+ aim_tlvlist_t *itl;
+ aim_tlv_t *msgblock;
+ aim_bstream_t tbs;
+
+ msgblock = aim_gettlv(otl, 0x0005, 1);
+ aim_bstream_init(&tbs, msgblock->value, msgblock->length);
+ itl = aim_readtlvchain(&tbs);
+
+ /*
+ * Type 0x0001: Message.
+ */
+ if (aim_gettlv(itl, 0x0001, 1))
+ msg = aim_gettlv_str(itl, 0x0001, 1);
+
+ aim_freetlvchain(&itl);
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, &userinfo, msg);
+
+ g_free(cookie);
+ g_free(msg);
+ aim_freetlvchain(&otl);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0002)
+ return infoupdate(sess, mod, rx, snac, bs);
+ else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
+ return userlistchange(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0006)
+ return incomingmsg(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x000e;
+ mod->version = 0x0001;
+ mod->toolid = 0x0010;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "chat", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
diff --git a/protocols/oscar/chat.h b/protocols/oscar/chat.h
new file mode 100644
index 00000000..6b360326
--- /dev/null
+++ b/protocols/oscar/chat.h
@@ -0,0 +1,17 @@
+#ifndef __OSCAR_CHAT_H__
+#define __OSCAR_CHAT_H__
+
+#define AIM_CB_FAM_CHT 0x000e /* Chat */
+
+/*
+ * SNAC Family: Chat Services
+ */
+#define AIM_CB_CHT_ERROR 0x0001
+#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002
+#define AIM_CB_CHT_USERJOIN 0x0003
+#define AIM_CB_CHT_USERLEAVE 0x0004
+#define AIM_CB_CHT_OUTGOINGMSG 0x0005
+#define AIM_CB_CHT_INCOMINGMSG 0x0006
+#define AIM_CB_CHT_DEFAULT 0xffff
+
+#endif /* __OSCAR_CHAT_H__ */
diff --git a/protocols/oscar/chatnav.c b/protocols/oscar/chatnav.c
new file mode 100644
index 00000000..c7e11765
--- /dev/null
+++ b/protocols/oscar/chatnav.c
@@ -0,0 +1,421 @@
+/*
+ * Handle ChatNav.
+ *
+ * [The ChatNav(igation) service does various things to keep chat
+ * alive. It provides room information, room searching and creating,
+ * as well as giving users the right ("permission") to use chat.]
+ *
+ */
+
+#include <aim.h>
+#include "chatnav.h"
+
+/*
+ * conn must be a chatnav connection!
+ */
+int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
+}
+
+int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange)
+{
+ static const char ck[] = {"create"};
+ static const char lang[] = {"en"};
+ static const char charset[] = {"us-ascii"};
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
+
+ /* exchange */
+ aimbs_put16(&fr->data, exchange);
+
+ /*
+ * This looks to be a big hack. You'll note that this entire
+ * SNAC is just a room info structure, but the hard room name,
+ * here, is set to "create".
+ *
+ * Either this goes on the "list of questions concerning
+ * why-the-hell-did-you-do-that", or this value is completly
+ * ignored. Without experimental evidence, but a good knowledge of
+ * AOL style, I'm going to guess that it is the latter, and that
+ * the value of the room name in create requests is ignored.
+ */
+ aimbs_put8(&fr->data, strlen(ck));
+ aimbs_putraw(&fr->data, (guint8 *)ck, strlen(ck));
+
+ /*
+ * instance
+ *
+ * Setting this to 0xffff apparently assigns the last instance.
+ *
+ */
+ aimbs_put16(&fr->data, 0xffff);
+
+ /* detail level */
+ aimbs_put8(&fr->data, 0x01);
+
+ aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), (guint8 *)name);
+ aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), (guint8 *)charset);
+ aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), (guint8 *)lang);
+
+ /* tlvcount */
+ aimbs_put16(&fr->data, aim_counttlvchain(&tl));
+ aim_writetlvchain(&fr->data, &tl);
+
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ struct aim_chat_exchangeinfo *exchanges = NULL;
+ int curexchange;
+ aim_tlv_t *exchangetlv;
+ guint8 maxrooms = 0;
+ aim_tlvlist_t *tlvlist, *innerlist;
+
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * Type 0x0002: Maximum concurrent rooms.
+ */
+ if (aim_gettlv(tlvlist, 0x0002, 1))
+ maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
+
+ /*
+ * Type 0x0003: Exchange information
+ *
+ * There can be any number of these, each one
+ * representing another exchange.
+ *
+ */
+ for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
+ aim_bstream_t tbs;
+
+ aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
+
+ curexchange++;
+
+ exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
+
+ /* exchange number */
+ exchanges[curexchange-1].number = aimbs_get16(&tbs);
+ innerlist = aim_readtlvchain(&tbs);
+
+ /*
+ * Type 0x000a: Unknown.
+ *
+ * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
+ *
+ */
+ if (aim_gettlv(innerlist, 0x000a, 1))
+ ;
+
+ /*
+ * Type 0x000d: Unknown.
+ */
+ if (aim_gettlv(innerlist, 0x000d, 1))
+ ;
+
+ /*
+ * Type 0x0004: Unknown
+ */
+ if (aim_gettlv(innerlist, 0x0004, 1))
+ ;
+
+ /*
+ * Type 0x0002: Unknown
+ */
+ if (aim_gettlv(innerlist, 0x0002, 1)) {
+ guint16 classperms;
+
+ classperms = aim_gettlv16(innerlist, 0x0002, 1);
+
+ }
+
+ /*
+ * Type 0x00c9: Flags
+ *
+ * 1 Evilable
+ * 2 Nav Only
+ * 4 Instancing Allowed
+ * 8 Occupant Peek Allowed
+ *
+ */
+ if (aim_gettlv(innerlist, 0x00c9, 1))
+ exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1);
+
+ /*
+ * Type 0x00ca: Creation Date
+ */
+ if (aim_gettlv(innerlist, 0x00ca, 1))
+ ;
+
+ /*
+ * Type 0x00d0: Mandatory Channels?
+ */
+ if (aim_gettlv(innerlist, 0x00d0, 1))
+ ;
+
+ /*
+ * Type 0x00d1: Maximum Message length
+ */
+ if (aim_gettlv(innerlist, 0x00d1, 1))
+ ;
+
+ /*
+ * Type 0x00d2: Maximum Occupancy?
+ */
+ if (aim_gettlv(innerlist, 0x00d2, 1))
+ ;
+
+ /*
+ * Type 0x00d3: Exchange Description
+ */
+ if (aim_gettlv(innerlist, 0x00d3, 1))
+ exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
+ else
+ exchanges[curexchange-1].name = NULL;
+
+ /*
+ * Type 0x00d4: Exchange Description URL
+ */
+ if (aim_gettlv(innerlist, 0x00d4, 1))
+ ;
+
+ /*
+ * Type 0x00d5: Creation Permissions
+ *
+ * 0 Creation not allowed
+ * 1 Room creation allowed
+ * 2 Exchange creation allowed
+ *
+ */
+ if (aim_gettlv(innerlist, 0x00d5, 1)) {
+ guint8 createperms;
+
+ createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+ }
+
+ /*
+ * Type 0x00d6: Character Set (First Time)
+ */
+ if (aim_gettlv(innerlist, 0x00d6, 1))
+ exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
+ else
+ exchanges[curexchange-1].charset1 = NULL;
+
+ /*
+ * Type 0x00d7: Language (First Time)
+ */
+ if (aim_gettlv(innerlist, 0x00d7, 1))
+ exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
+ else
+ exchanges[curexchange-1].lang1 = NULL;
+
+ /*
+ * Type 0x00d8: Character Set (Second Time)
+ */
+ if (aim_gettlv(innerlist, 0x00d8, 1))
+ exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
+ else
+ exchanges[curexchange-1].charset2 = NULL;
+
+ /*
+ * Type 0x00d9: Language (Second Time)
+ */
+ if (aim_gettlv(innerlist, 0x00d9, 1))
+ exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
+ else
+ exchanges[curexchange-1].lang2 = NULL;
+
+ /*
+ * Type 0x00da: Unknown
+ */
+ if (aim_gettlv(innerlist, 0x00da, 1))
+ ;
+
+ aim_freetlvchain(&innerlist);
+ }
+
+ /*
+ * Call client.
+ */
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
+
+ for (curexchange--; curexchange >= 0; curexchange--) {
+ g_free(exchanges[curexchange].name);
+ g_free(exchanges[curexchange].charset1);
+ g_free(exchanges[curexchange].lang1);
+ g_free(exchanges[curexchange].charset2);
+ g_free(exchanges[curexchange].lang2);
+ }
+ g_free(exchanges);
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+ aim_rxcallback_t userfunc;
+ aim_tlvlist_t *tlvlist, *innerlist;
+ char *ck = NULL, *fqcn = NULL, *name = NULL;
+ guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
+ guint32 createtime = 0;
+ guint8 createperms = 0, detaillevel;
+ int cklen;
+ aim_tlv_t *bigblock;
+ int ret = 0;
+ aim_bstream_t bbbs;
+
+ tlvlist = aim_readtlvchain(bs);
+
+ if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
+ do_error_dialog(sess->aux_data, "no bigblock in top tlv in create room response", "Gaim");
+
+ aim_freetlvchain(&tlvlist);
+ return 0;
+ }
+
+ aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
+
+ exchange = aimbs_get16(&bbbs);
+ cklen = aimbs_get8(&bbbs);
+ ck = aimbs_getstr(&bbbs, cklen);
+ instance = aimbs_get16(&bbbs);
+ detaillevel = aimbs_get8(&bbbs);
+
+ if (detaillevel != 0x02) {
+ do_error_dialog(sess->aux_data, "unknown detaillevel in create room response", "Gaim");
+ aim_freetlvchain(&tlvlist);
+ g_free(ck);
+ return 0;
+ }
+
+ unknown = aimbs_get16(&bbbs);
+
+ innerlist = aim_readtlvchain(&bbbs);
+
+ if (aim_gettlv(innerlist, 0x006a, 1))
+ fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
+
+ if (aim_gettlv(innerlist, 0x00c9, 1))
+ flags = aim_gettlv16(innerlist, 0x00c9, 1);
+
+ if (aim_gettlv(innerlist, 0x00ca, 1))
+ createtime = aim_gettlv32(innerlist, 0x00ca, 1);
+
+ if (aim_gettlv(innerlist, 0x00d1, 1))
+ maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
+
+ if (aim_gettlv(innerlist, 0x00d2, 1))
+ maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
+
+ if (aim_gettlv(innerlist, 0x00d3, 1))
+ name = aim_gettlv_str(innerlist, 0x00d3, 1);
+
+ if (aim_gettlv(innerlist, 0x00d5, 1))
+ createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+ ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
+ }
+
+ g_free(ck);
+ g_free(name);
+ g_free(fqcn);
+ aim_freetlvchain(&innerlist);
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Since multiple things can trigger this callback, we must lookup the
+ * snacid to determine the original snac subtype that was called.
+ *
+ * XXX This isn't really how this works. But this is: Every d/9 response
+ * has a 16bit value at the beginning. That matches to:
+ * Short Desc = 1
+ * Full Desc = 2
+ * Instance Info = 4
+ * Nav Short Desc = 8
+ * Nav Instance Info = 16
+ * And then everything is really asynchronous. There is no specific
+ * attachment of a response to a create room request, for example. Creating
+ * the room yields no different a response than requesting the room's info.
+ *
+ */
+static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_snac_t *snac2;
+ int ret = 0;
+
+ if (!(snac2 = aim_remsnac(sess, snac->id))) {
+ do_error_dialog(sess->aux_data, "received response to unknown request!", "Gaim");
+ return 0;
+ }
+
+ if (snac2->family != 0x000d) {
+ do_error_dialog(sess->aux_data, "recieved response that maps to corrupt request!", "Gaim");
+ return 0;
+ }
+
+ /*
+ * We now know what the original SNAC subtype was.
+ */
+ if (snac2->type == 0x0002) /* request chat rights */
+ ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2);
+ else if (snac2->type == 0x0003) {} /* request exchange info */
+ else if (snac2->type == 0x0004) {} /* request room info */
+ else if (snac2->type == 0x0005) {} /* request more room info */
+ else if (snac2->type == 0x0006) {} /* request occupant list */
+ else if (snac2->type == 0x0007) {} /* search for a room */
+ else if (snac2->type == 0x0008) /* create room */
+ ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
+ else
+ do_error_dialog(sess->aux_data, "unknown request subtype", "Gaim");
+
+ if (snac2)
+ g_free(snac2->data);
+ g_free(snac2);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0009)
+ return parseinfo(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x000d;
+ mod->version = 0x0003;
+ mod->toolid = 0x0010;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "chatnav", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
diff --git a/protocols/oscar/chatnav.h b/protocols/oscar/chatnav.h
new file mode 100644
index 00000000..285decad
--- /dev/null
+++ b/protocols/oscar/chatnav.h
@@ -0,0 +1,14 @@
+#ifndef __OSCAR_CHATNAV_H__
+#define __OSCAR_CHATNAV_H__
+
+#define AIM_CB_FAM_CTN 0x000d /* ChatNav */
+
+/*
+ * SNAC Family: Chat Navigation Services
+ */
+#define AIM_CB_CTN_ERROR 0x0001
+#define AIM_CB_CTN_CREATE 0x0008
+#define AIM_CB_CTN_INFO 0x0009
+#define AIM_CB_CTN_DEFAULT 0xffff
+
+#endif /* __OSCAR_CHATNAV_H__ */
diff --git a/protocols/oscar/conn.c b/protocols/oscar/conn.c
new file mode 100644
index 00000000..711e3458
--- /dev/null
+++ b/protocols/oscar/conn.c
@@ -0,0 +1,690 @@
+
+/*
+ * conn.c
+ *
+ * Does all this gloriously nifty connection handling stuff...
+ *
+ */
+
+#include <aim.h>
+#include "sock.h"
+
+static int aim_logoff(aim_session_t *sess);
+
+/*
+ * In OSCAR, every connection has a set of SNAC groups associated
+ * with it. These are the groups that you can send over this connection
+ * without being guarenteed a "Not supported" SNAC error.
+ *
+ * The grand theory of things says that these associations transcend
+ * what libfaim calls "connection types" (conn->type). You can probably
+ * see the elegance here, but since I want to revel in it for a bit, you
+ * get to hear it all spelled out.
+ *
+ * So let us say that you have your core BOS connection running. One
+ * of your modules has just given you a SNAC of the group 0x0004 to send
+ * you. Maybe an IM destined for some twit in Greenland. So you start
+ * at the top of your connection list, looking for a connection that
+ * claims to support group 0x0004. You find one. Why, that neat BOS
+ * connection of yours can do that. So you send it on its way.
+ *
+ * Now, say, that fellow from Greenland has friends and they all want to
+ * meet up with you in a lame chat room. This has landed you a SNAC
+ * in the family 0x000e and you have to admit you're a bit lost. You've
+ * searched your connection list for someone who wants to make your life
+ * easy and deliver this SNAC for you, but there isn't one there.
+ *
+ * Here comes the good bit. Without even letting anyone know, particularly
+ * the module that decided to send this SNAC, and definitly not that twit
+ * in Greenland, you send out a service request. In this request, you have
+ * marked the need for a connection supporting group 0x000e. A few seconds
+ * later, you receive a service redirect with an IP address and a cookie in
+ * it. Great, you say. Now I have something to do. Off you go, making
+ * that connection. One of the first things you get from this new server
+ * is a message saying that indeed it does support the group you were looking
+ * for. So you continue and send rate confirmation and all that.
+ *
+ * Then you remember you had that SNAC to send, and now you have a means to
+ * do it, and you do, and everyone is happy. Except the Greenlander, who is
+ * still stuck in the bitter cold.
+ *
+ * Oh, and this is useful for building the Migration SNACs, too. In the
+ * future, this may help convince me to implement rate limit mitigation
+ * for real. We'll see.
+ *
+ * Just to make me look better, I'll say that I've known about this great
+ * scheme for quite some time now. But I still haven't convinced myself
+ * to make libfaim work that way. It would take a fair amount of effort,
+ * and probably some client API changes as well. (Whenever I don't want
+ * to do something, I just say it would change the client API. Then I
+ * instantly have a couple of supporters of not doing it.)
+ *
+ * Generally, addgroup is only called by the internal handling of the
+ * server ready SNAC. So if you want to do something before that, you'll
+ * have to be more creative. That is done rather early, though, so I don't
+ * think you have to worry about it. Unless you're me. I care deeply
+ * about such inane things.
+ *
+ */
+void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ struct snacgroup *sg;
+
+ if (!(sg = g_malloc(sizeof(struct snacgroup))))
+ return;
+
+ sg->group = group;
+
+ sg->next = ins->groups;
+ ins->groups = sg;
+
+ return;
+}
+
+aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
+{
+ aim_conn_t *cur;
+
+ for (cur = sess->connlist; cur; cur = cur->next) {
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside;
+ struct snacgroup *sg;
+
+ for (sg = ins->groups; sg; sg = sg->next) {
+ if (sg->group == group)
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+static void connkill_snacgroups(struct snacgroup **head)
+{
+ struct snacgroup *sg;
+
+ for (sg = *head; sg; ) {
+ struct snacgroup *tmp;
+
+ tmp = sg->next;
+ g_free(sg);
+ sg = tmp;
+ }
+
+ *head = NULL;
+
+ return;
+}
+
+static void connkill_rates(struct rateclass **head)
+{
+ struct rateclass *rc;
+
+ for (rc = *head; rc; ) {
+ struct rateclass *tmp;
+ struct snacpair *sp;
+
+ tmp = rc->next;
+
+ for (sp = rc->members; sp; ) {
+ struct snacpair *tmpsp;
+
+ tmpsp = sp->next;
+ g_free(sp);
+ sp = tmpsp;
+ }
+ g_free(rc);
+
+ rc = tmp;
+ }
+
+ *head = NULL;
+
+ return;
+}
+
+static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
+{
+
+ aim_rxqueue_cleanbyconn(sess, *deadconn);
+ aim_tx_cleanqueue(sess, *deadconn);
+
+ if ((*deadconn)->fd != -1)
+ aim_conn_close(*deadconn);
+
+ /*
+ * XXX ->priv should never be touched by the library. I know
+ * it used to be, but I'm getting rid of all that. Use
+ * ->internal instead.
+ */
+ if ((*deadconn)->priv)
+ g_free((*deadconn)->priv);
+
+ /*
+ * This will free ->internal if it necessary...
+ */
+ if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
+ aim_conn_kill_rend(sess, *deadconn);
+ else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
+ aim_conn_kill_chat(sess, *deadconn);
+
+ if ((*deadconn)->inside) {
+ aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;
+
+ connkill_snacgroups(&inside->groups);
+ connkill_rates(&inside->rates);
+
+ g_free(inside);
+ }
+
+ g_free(*deadconn);
+ *deadconn = NULL;
+
+ return;
+}
+
+/**
+ * aim_connrst - Clears out connection list, killing remaining connections.
+ * @sess: Session to be cleared
+ *
+ * Clears out the connection list and kills any connections left.
+ *
+ */
+static void aim_connrst(aim_session_t *sess)
+{
+
+ if (sess->connlist) {
+ aim_conn_t *cur = sess->connlist, *tmp;
+
+ while (cur) {
+ tmp = cur->next;
+ aim_conn_close(cur);
+ connkill_real(sess, &cur);
+ cur = tmp;
+ }
+ }
+
+ sess->connlist = NULL;
+
+ return;
+}
+
+/**
+ * aim_conn_init - Reset a connection to default values.
+ * @deadconn: Connection to be reset
+ *
+ * Initializes and/or resets a connection structure.
+ *
+ */
+static void aim_conn_init(aim_conn_t *deadconn)
+{
+
+ if (!deadconn)
+ return;
+
+ deadconn->fd = -1;
+ deadconn->subtype = -1;
+ deadconn->type = -1;
+ deadconn->seqnum = 0;
+ deadconn->lastactivity = 0;
+ deadconn->forcedlatency = 0;
+ deadconn->handlerlist = NULL;
+ deadconn->priv = NULL;
+ memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));
+
+ return;
+}
+
+/**
+ * aim_conn_getnext - Gets a new connection structure.
+ * @sess: Session
+ *
+ * Allocate a new empty connection structure.
+ *
+ */
+static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
+{
+ aim_conn_t *newconn;
+
+ if (!(newconn = g_new0(aim_conn_t,1)))
+ return NULL;
+
+ if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) {
+ g_free(newconn);
+ return NULL;
+ }
+
+ aim_conn_init(newconn);
+
+ newconn->next = sess->connlist;
+ sess->connlist = newconn;
+
+ return newconn;
+}
+
+/**
+ * aim_conn_kill - Close and free a connection.
+ * @sess: Session for the connection
+ * @deadconn: Connection to be freed
+ *
+ * Close, clear, and free a connection structure. Should never be
+ * called from within libfaim.
+ *
+ */
+void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
+{
+ aim_conn_t *cur, **prev;
+
+ if (!deadconn || !*deadconn)
+ return;
+
+ for (prev = &sess->connlist; (cur = *prev); ) {
+ if (cur == *deadconn) {
+ *prev = cur->next;
+ break;
+ }
+ prev = &cur->next;
+ }
+
+ if (!cur)
+ return; /* oops */
+
+ connkill_real(sess, &cur);
+
+ return;
+}
+
+/**
+ * aim_conn_close - Close a connection
+ * @deadconn: Connection to close
+ *
+ * Close (but not free) a connection.
+ *
+ * This leaves everything untouched except for clearing the
+ * handler list and setting the fd to -1 (used to recognize
+ * dead connections). It will also remove cookies if necessary.
+ *
+ */
+void aim_conn_close(aim_conn_t *deadconn)
+{
+
+ if (deadconn->fd >= 3)
+ closesocket(deadconn->fd);
+ deadconn->fd = -1;
+ if (deadconn->handlerlist)
+ aim_clearhandlers(deadconn);
+ if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS)
+ aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn);
+
+ return;
+}
+
+/**
+ * aim_getconn_type - Find a connection of a specific type
+ * @sess: Session to search
+ * @type: Type of connection to look for
+ *
+ * Searches for a connection of the specified type in the
+ * specified session. Returns the first connection of that
+ * type found.
+ *
+ * XXX except for RENDEZVOUS, all uses of this should be removed and
+ * use aim_conn_findbygroup() instead.
+ */
+aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
+{
+ aim_conn_t *cur;
+
+ for (cur = sess->connlist; cur; cur = cur->next) {
+ if ((cur->type == type) &&
+ !(cur->status & AIM_CONN_STATUS_INPROGRESS))
+ break;
+ }
+
+ return cur;
+}
+
+aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
+{
+ aim_conn_t *cur;
+
+ for (cur = sess->connlist; cur; cur = cur->next) {
+ if (cur->type == type)
+ break;
+ }
+
+ return cur;
+}
+
+/**
+ * aim_cloneconn - clone an aim_conn_t
+ * @sess: session containing parent
+ * @src: connection to clone
+ *
+ * A new connection is allocated, and the values are filled in
+ * appropriately. Note that this function sets the new connnection's
+ * ->priv pointer to be equal to that of its parent: only the pointer
+ * is copied, not the data it points to.
+ *
+ * This function returns a pointer to the new aim_conn_t, or %NULL on
+ * error
+ */
+aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
+{
+ aim_conn_t *conn;
+
+ if (!(conn = aim_conn_getnext(sess)))
+ return NULL;
+
+ conn->fd = src->fd;
+ conn->type = src->type;
+ conn->subtype = src->subtype;
+ conn->seqnum = src->seqnum;
+ conn->priv = src->priv;
+ conn->internal = src->internal;
+ conn->lastactivity = src->lastactivity;
+ conn->forcedlatency = src->forcedlatency;
+ conn->sessv = src->sessv;
+ aim_clonehandlers(sess, conn, src);
+
+ if (src->inside) {
+ /*
+ * XXX should clone this section as well, but since currently
+ * this function only gets called for some of that rendezvous
+ * crap, and not on SNAC connections, its probably okay for
+ * now.
+ *
+ */
+ }
+
+ return conn;
+}
+
+/**
+ * aim_newconn - Open a new connection
+ * @sess: Session to create connection in
+ * @type: Type of connection to create
+ * @dest: Host to connect to (in "host:port" syntax)
+ *
+ * Opens a new connection to the specified dest host of specified
+ * type, using the proxy settings if available. If @host is %NULL,
+ * the connection is allocated and returned, but no connection
+ * is made.
+ *
+ * FIXME: Return errors in a more sane way.
+ *
+ */
+aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
+{
+ aim_conn_t *connstruct;
+ guint16 port = AIM_LOGIN_PORT;
+ char *host;
+ int i;
+
+ if (!(connstruct = aim_conn_getnext(sess)))
+ return NULL;
+
+ connstruct->sessv = (void *)sess;
+ connstruct->type = type;
+
+ if (!dest) { /* just allocate a struct */
+ connstruct->fd = -1;
+ connstruct->status = 0;
+ return connstruct;
+ }
+
+ /*
+ * As of 23 Jul 1999, AOL now sends the port number, preceded by a
+ * colon, in the BOS redirect. This fatally breaks all previous
+ * libfaims. Bad, bad AOL.
+ *
+ * We put this here to catch every case.
+ *
+ */
+
+ for(i = 0; i < (int)strlen(dest); i++) {
+ if (dest[i] == ':') {
+ port = atoi(&(dest[i+1]));
+ break;
+ }
+ }
+
+ host = (char *)g_malloc(i+1);
+ strncpy(host, dest, i);
+ host[i] = '\0';
+
+ connstruct->fd = proxy_connect(host, port, NULL, NULL);
+
+ g_free(host);
+
+ return connstruct;
+}
+
+/**
+ * aim_conn_setlatency - Set a forced latency value for connection
+ * @conn: Conn to set latency for
+ * @newval: Number of seconds to force between transmits
+ *
+ * Causes @newval seconds to be spent between transmits on a connection.
+ *
+ * This is my lame attempt at overcoming not understanding the rate
+ * limiting.
+ *
+ * XXX: This should really be replaced with something that scales and
+ * backs off like the real rate limiting does.
+ *
+ */
+int aim_conn_setlatency(aim_conn_t *conn, int newval)
+{
+
+ if (!conn)
+ return -1;
+
+ conn->forcedlatency = newval;
+ conn->lastactivity = 0; /* reset this just to make sure */
+
+ return 0;
+}
+
+/**
+ * aim_session_init - Initializes a session structure
+ * @sess: Session to initialize
+ * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
+ * @debuglevel: Level of debugging output (zero is least)
+ *
+ * Sets up the initial values for a session.
+ *
+ */
+void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
+{
+
+ if (!sess)
+ return;
+
+ memset(sess, 0, sizeof(aim_session_t));
+ aim_connrst(sess);
+ sess->queue_outgoing = NULL;
+ sess->queue_incoming = NULL;
+ aim_initsnachash(sess);
+ sess->msgcookies = NULL;
+ sess->snacid_next = 0x00000001;
+
+ sess->flags = 0;
+
+ sess->modlistv = NULL;
+
+ sess->ssi.received_data = 0;
+ sess->ssi.waiting_for_ack = 0;
+ sess->ssi.holding_queue = NULL;
+ sess->ssi.revision = 0;
+ sess->ssi.items = NULL;
+ sess->ssi.timestamp = (time_t)0;
+
+ sess->locate.userinfo = NULL;
+ sess->locate.torequest = NULL;
+ sess->locate.requested = NULL;
+ sess->locate.waiting_for_response = FALSE;
+
+ sess->icq_info = NULL;
+ sess->authinfo = NULL;
+ sess->emailinfo = NULL;
+ sess->oft_info = NULL;
+
+
+ /*
+ * Default to SNAC login unless XORLOGIN is explicitly set.
+ */
+ if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
+ sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+ sess->flags |= flags;
+
+ /*
+ * This must always be set. Default to the queue-based
+ * version for back-compatibility.
+ */
+ aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
+
+
+ /*
+ * Register all the modules for this session...
+ */
+ aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
+ aim__registermodule(sess, general_modfirst);
+ aim__registermodule(sess, locate_modfirst);
+ aim__registermodule(sess, buddylist_modfirst);
+ aim__registermodule(sess, msg_modfirst);
+ aim__registermodule(sess, admin_modfirst);
+ aim__registermodule(sess, bos_modfirst);
+ aim__registermodule(sess, search_modfirst);
+ aim__registermodule(sess, stats_modfirst);
+ aim__registermodule(sess, chatnav_modfirst);
+ aim__registermodule(sess, chat_modfirst);
+ /* missing 0x0f - 0x12 */
+ aim__registermodule(sess, ssi_modfirst);
+ /* missing 0x14 */
+ aim__registermodule(sess, icq_modfirst);
+ /* missing 0x16 */
+ aim__registermodule(sess, auth_modfirst);
+
+ return;
+}
+
+/**
+ * aim_session_kill - Deallocate a session
+ * @sess: Session to kill
+ *
+ */
+void aim_session_kill(aim_session_t *sess)
+{
+ aim_cleansnacs(sess, -1);
+
+ aim_logoff(sess);
+
+ aim__shutdownmodules(sess);
+
+ return;
+}
+
+/*
+ * XXX this is nearly as ugly as proxyconnect().
+ */
+int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
+{
+ fd_set fds, wfds;
+ struct timeval tv;
+ int res, error = ETIMEDOUT;
+ aim_rxcallback_t userfunc;
+
+ if (!conn || (conn->fd == -1))
+ return -1;
+
+ if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
+ return -1;
+
+ FD_ZERO(&fds);
+ FD_SET(conn->fd, &fds);
+ FD_ZERO(&wfds);
+ FD_SET(conn->fd, &wfds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
+ error = errno;
+ aim_conn_close(conn);
+ errno = error;
+ return -1;
+ } else if (res == 0) {
+ return 0; /* hasn't really completed yet... */
+ }
+
+ if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
+ unsigned int len = sizeof(error);
+
+ if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ error = errno;
+ }
+
+ if (error) {
+ aim_conn_close(conn);
+ errno = error;
+ return -1;
+ }
+
+#ifndef _WIN32
+ fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
+#endif
+
+ conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
+
+ if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
+ userfunc(sess, NULL, conn);
+
+ /* Flush out the queues if there was something waiting for this conn */
+ aim_tx_flushqueue(sess);
+
+ return 0;
+}
+
+aim_session_t *aim_conn_getsess(aim_conn_t *conn)
+{
+
+ if (!conn)
+ return NULL;
+
+ return (aim_session_t *)conn->sessv;
+}
+
+/*
+ * aim_logoff()
+ *
+ * Closes -ALL- open connections.
+ *
+ */
+static int aim_logoff(aim_session_t *sess)
+{
+
+ aim_connrst(sess); /* in case we want to connect again */
+
+ return 0;
+
+}
+
+/*
+ * aim_flap_nop()
+ *
+ * No-op. WinAIM 4.x sends these _every minute_ to keep
+ * the connection alive.
+ */
+int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *fr;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
+ return -ENOMEM;
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+
diff --git a/protocols/oscar/faimconfig.h b/protocols/oscar/faimconfig.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/protocols/oscar/faimconfig.h
diff --git a/protocols/oscar/ft.c b/protocols/oscar/ft.c
new file mode 100644
index 00000000..ddb64e7a
--- /dev/null
+++ b/protocols/oscar/ft.c
@@ -0,0 +1,2005 @@
+/*
+ * File transfer (OFT) and DirectIM (ODC).
+ * (OSCAR File Transfer, Oscar Direct Connect(ion?)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <aim.h>
+#include <glib.h>
+#include "ft.h"
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/utsname.h> /* for aim_directim_initiate */
+#include <arpa/inet.h> /* for inet_ntoa */
+#endif
+
+/* TODO:
+ o look for memory leaks.. there's going to be shitloads, i'm sure.
+*/
+
+struct aim_directim_intdata {
+ guint8 cookie[8];
+ char sn[MAXSNLEN+1];
+ char ip[22];
+};
+
+static int listenestablish(guint16 portnum);
+
+/**
+ * aim_handlerendconnect - call this to accept OFT connections and set up the required structures
+ * @sess: the session
+ * @cur: the conn the incoming connection is on
+ *
+ * call this when you get an outstanding read on a conn with subtype
+ * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current
+ * &aim_conn_t and tweak things as appropriate. the new conn and the
+ * listener conn are both returned to the client in the
+ * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback.
+ */
+int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
+{
+ int acceptfd = 0;
+ struct sockaddr cliaddr;
+ unsigned int clilen = sizeof(cliaddr);
+ int ret = 0;
+ aim_conn_t *newconn;
+
+ if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
+ return 0; /* not an error */
+
+ if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
+ closesocket(acceptfd);
+ aim_conn_close(cur);
+ return -1;
+ }
+
+ if (!(newconn = aim_cloneconn(sess, cur))) {
+ closesocket(acceptfd);
+ aim_conn_close(cur);
+ return -1;
+ }
+
+ newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
+ newconn->fd = acceptfd;
+
+ if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {
+ struct aim_directim_intdata *priv;
+ aim_rxcallback_t userfunc;
+
+ priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal);
+ cur->internal = NULL;
+
+ g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u",
+ inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr),
+ ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+ if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
+ ret = userfunc(sess, NULL, newconn, cur);
+
+ } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+#if 0
+ struct aim_filetransfer_priv *priv;
+ aim_rxcallback_t userfunc;
+
+
+ newconn->priv = cur->priv;
+ cur->priv = NULL;
+ priv = (struct aim_filetransfer_priv *)newconn->priv;
+
+ g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+ if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
+ ret = userfunc(sess, NULL, newconn, cur);
+#endif
+ } else {
+ do_error_dialog(sess->aux_data, "Got a Connection on a listener that's not Rendezvous Closing conn.", "Gaim");
+ aim_conn_close(newconn);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/**
+ * aim_send_typing - send client-to-client typing notification over established connection
+ * @sess: session to conn
+ * @conn: directim connection
+ * @typing: If true, notify user has started typing; if false, notify user has stopped.
+ *
+ * The connection must have been previously established.
+ */
+int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
+{
+
+struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+ aim_frame_t *fr;
+ aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */
+
+ if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
+ return -ENOMEM;
+
+ memcpy(fr->hdr.oft.magic, "ODC2", 4);
+
+ fr->hdr.oft.hdr2len = 0x44;
+
+ if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) {
+ aim_frame_destroy(fr);
+ return -ENOMEM;
+ }
+ memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len);
+
+ aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+ aimbs_put16(&hdrbs, 0x0006);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_putraw(&hdrbs, intdata->cookie, 8);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put32(&hdrbs, 0x00000000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+
+ /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */
+ aimbs_put16(&hdrbs, ( typing ? 0x000e : 0x0002));
+
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn));
+
+ aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
+
+ aimbs_put8(&hdrbs, 0x00);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+
+ /* end of hdr2 */
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/**
+ * aim_send_im_direct - send IM client-to-client over established connection
+ * @sess: session to conn
+ * @conn: directim connection
+ * @msg: null-terminated string to send.
+ * len: The length of the message to send, including binary data.
+ *
+ * Call this just like you would aim_send_im, to send a directim. You
+ * _must_ have previously established the directim connection.
+ */
+int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len)
+{
+ struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+ aim_frame_t *fr;
+ aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */
+
+ if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len)))
+ return -ENOMEM;
+
+ memcpy(fr->hdr.oft.magic, "ODC2", 4);
+
+ fr->hdr.oft.hdr2len = 0x44;
+
+ if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) {
+ aim_frame_destroy(fr);
+ return -ENOMEM;
+ }
+ memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len);
+
+ aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+ aimbs_put16(&hdrbs, 0x0006);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_putraw(&hdrbs, intdata->cookie, 8);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put32(&hdrbs, len);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+
+ /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */
+ aimbs_put16(&hdrbs, 0x0000);
+
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn));
+
+ aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
+
+ aimbs_put8(&hdrbs, 0x00);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_put16(&hdrbs, 0x0000);
+
+ /* end of hdr2 */
+
+#if 0 /* XXX this is how you send buddy icon info... */
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
+ i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
+#endif
+ aimbs_putraw(&fr->data, (guint8 *)msg, len);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int getlocalip(guint8 *ip)
+{
+ struct hostent *hptr;
+ char localhost[129];
+
+ /* XXX if available, use getaddrinfo() */
+ /* XXX allow client to specify which IP to use for multihomed boxes */
+
+ if (gethostname(localhost, 128) < 0)
+ return -1;
+
+ if (!(hptr = gethostbyname(localhost)))
+ return -1;
+
+ memcpy(ip, hptr->h_addr_list[0], 4);
+
+ return 0;
+}
+
+/**
+ * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one)
+ * @destsn: the SN to connect to.
+ *
+ */
+aim_conn_t *aim_directim_initiate(aim_session_t *sess, const char *destsn)
+{
+ aim_conn_t *newconn;
+ aim_msgcookie_t *cookie;
+ struct aim_directim_intdata *priv;
+ int listenfd;
+ guint16 port = 4443;
+ guint8 localip[4];
+ guint8 ck[8];
+
+ if (getlocalip(localip) == -1)
+ return NULL;
+
+ if ((listenfd = listenestablish(port)) == -1)
+ return NULL;
+
+ aim_request_directim(sess, destsn, localip, port, ck);
+
+ cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1);
+ memcpy(cookie->cookie, ck, 8);
+ cookie->type = AIM_COOKIETYPE_OFTIM;
+
+ /* this one is for the cookie */
+ priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+ memcpy(priv->cookie, ck, 8);
+ strncpy(priv->sn, destsn, sizeof(priv->sn));
+ cookie->data = priv;
+ aim_cachecookie(sess, cookie);
+
+ /* XXX switch to aim_cloneconn()? */
+ if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
+ closesocket(listenfd);
+ return NULL;
+ }
+
+ /* this one is for the conn */
+ priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata, 1);
+
+ memcpy(priv->cookie, ck, 8);
+ strncpy(priv->sn, destsn, sizeof(priv->sn));
+
+ newconn->fd = listenfd;
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+ newconn->internal = priv;
+ newconn->lastactivity = time(NULL);
+
+ return newconn;
+}
+
+/**
+ * aim_sendfile_intitiate - For those times when we want to send the file ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn: the SN to connect to.
+ * @filename: the name of the files you want to send
+ *
+ */
+aim_conn_t *aim_sendfile_initiate(aim_session_t *sess, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize)
+{
+ aim_conn_t *newconn;
+ aim_msgcookie_t *cookie;
+ struct aim_directim_intdata *priv;
+ int listenfd;
+ guint16 port = 4443;
+ guint8 localip[4];
+ guint8 ck[8];
+
+ if (getlocalip(localip) == -1)
+ return NULL;
+
+ if ((listenfd = listenestablish(port)) == -1)
+ return NULL;
+
+ aim_request_sendfile(sess, destsn, filename, numfiles, totsize, localip, port, ck);
+
+ cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1);
+ memcpy(cookie->cookie, ck, 8);
+ cookie->type = AIM_COOKIETYPE_OFTIM;
+
+ /* this one is for the cookie */
+ priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+ memcpy(priv->cookie, ck, 8);
+ strncpy(priv->sn, destsn, sizeof(priv->sn));
+ cookie->data = priv;
+ aim_cachecookie(sess, cookie);
+
+ /* XXX switch to aim_cloneconn()? */
+ if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
+ closesocket(listenfd);
+ return NULL;
+ }
+
+ /* this one is for the conn */
+ priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+ memcpy(priv->cookie, ck, 8);
+ strncpy(priv->sn, destsn, sizeof(priv->sn));
+
+ newconn->fd = listenfd;
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+ newconn->internal = priv;
+ newconn->lastactivity = time(NULL);
+
+ return newconn;
+}
+
+#if 0
+/**
+ * unsigned int aim_oft_listener_clean - close up old listeners
+ * @sess: session to clean up in
+ * @age: maximum age in seconds
+ *
+ * returns number closed, -1 on error.
+ */
+unsigned int aim_oft_listener_clean(struct aim_session_t *sess, time_t age)
+{
+ struct aim_conn_t *cur;
+ time_t now;
+ unsigned int hit = 0;
+
+ if (!sess)
+ return -1;
+ now = time(NULL);
+ faim_mutex_lock(&sess->connlistlock);
+ for(cur = sess->connlist;cur; cur = cur->next)
+ if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+ faim_mutex_lock(&cur->active);
+ if (cur->lastactivity < (now - age) ) {
+ faim_mutex_unlock(&cur->active);
+ aim_conn_close(cur);
+ hit++;
+ } else
+ faim_mutex_unlock(&cur->active);
+ }
+ faim_mutex_unlock(&sess->connlistlock);
+ return hit;
+}
+#endif
+
+const char *aim_directim_getsn(aim_conn_t *conn)
+{
+ struct aim_directim_intdata *intdata;
+
+ if (!conn)
+ return NULL;
+
+ if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) ||
+ (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+ return NULL;
+
+ if (!conn->internal)
+ return NULL;
+
+ intdata = (struct aim_directim_intdata *)conn->internal;
+
+ return intdata->sn;
+}
+
+/**
+ * aim_directim_connect - connect to buddy for directim
+ * @sess: the session to append the conn to,
+ * @sn: the SN we're connecting to
+ * @addr: address to connect to
+ *
+ * This is a wrapper for aim_newconn.
+ *
+ * If addr is NULL, the socket is not created, but the connection is
+ * allocated and setup to connect.
+ *
+ */
+aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const guint8 *cookie)
+{
+ aim_conn_t *newconn;
+ struct aim_directim_intdata *intdata;
+
+ if (!sess || !sn)
+ return NULL;
+
+ if (!(intdata = g_new0(struct aim_directim_intdata, 1)))
+ return NULL;
+
+ memcpy(intdata->cookie, cookie, 8);
+ strncpy(intdata->sn, sn, sizeof(intdata->sn));
+ if (addr)
+ strncpy(intdata->ip, addr, sizeof(intdata->ip));
+
+ /* XXX verify that non-blocking connects actually work */
+ if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
+ g_free(intdata);
+ return NULL;
+ }
+
+ if (!newconn) {
+ g_free(intdata);
+ return newconn;
+ }
+
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+ newconn->internal = intdata;
+
+ return newconn;
+}
+
+/**
+ * aim_directim_getconn - find a directim conn for buddy name
+ * @sess: your session,
+ * @name: the name to get,
+ *
+ * returns conn for directim with name, %NULL if none found.
+ *
+ */
+aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name)
+{
+ aim_conn_t *cur;
+
+ if (!sess || !name || !strlen(name))
+ return NULL;
+
+ for (cur = sess->connlist; cur; cur = cur->next) {
+ struct aim_directim_intdata *intdata;
+
+ if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+ continue;
+
+ intdata = cur->internal;
+
+ if (aim_sncmp(intdata->sn, name) == 0)
+ break;
+ }
+
+ return cur;
+}
+
+/**
+ * aim_accepttransfer - accept a file transfer request
+ * @sess: the session,
+ * @conn: the BOS conn for the CAP reply
+ * @sn: the screenname to send it to,
+ * @cookie: the cookie used
+ * @ip: the ip to connect to
+ * @listingfiles: number of files to share
+ * @listingtotsize: total size of shared files
+ * @listingsize: length of the listing file(buffer)
+ * @listingchecksum: checksum of the listing
+ * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)
+ *
+ * Returns new connection or %NULL on error.
+ *
+ * XXX this should take a struct.
+ */
+aim_conn_t *aim_accepttransfer(aim_session_t *sess,
+ aim_conn_t *conn,
+ const char *sn, const guint8 *cookie,
+ const guint8 *ip,
+ guint16 listingfiles,
+ guint16 listingtotsize,
+ guint16 listingsize,
+ guint32 listingchecksum,
+ guint16 rendid)
+{
+ return NULL;
+#if 0
+ struct command_tx_struct *newpacket, *newoft;
+ struct aim_conn_t *newconn;
+ struct aim_fileheader_t *fh;
+ struct aim_filetransfer_priv *priv;
+ struct aim_msgcookie_t *cachedcook;
+ int curbyte, i;
+
+ if (!sess || !conn || !sn || !cookie || !ip) {
+ return NULL;
+ }
+
+ newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
+
+ if (!newconn || (newconn->fd == -1)) {
+ perror("aim_newconn");
+ faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
+ return newconn;
+ } else {
+ priv = (struct aim_filetransfer_priv *)g_new0(struct aim_filetransfer_priv,1);
+
+ memcpy(priv->cookie, cookie, 8);
+ priv->state = 0;
+ strncpy(priv->sn, sn, MAXSNLEN);
+ strncpy(priv->ip, ip, sizeof(priv->ip));
+ newconn->priv = (void *)priv;
+
+ faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+ }
+
+ if (rendid == AIM_CAPS_GETFILE) {
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+
+ faimdprintf(sess, 2, "faim: getfile request accept\n");
+
+ if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) {
+ faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ /* XXX: conn leak here */
+ return NULL;
+ }
+
+ newoft->lock = 1;
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(fh = (struct aim_fileheader_t*)g_new0(struct aim_fileheader_t,1))) {
+ /* XXX: conn leak here */
+ perror("calloc");
+ return NULL;
+ }
+
+ fh->encrypt = 0x0000;
+ fh->compress = 0x0000;
+ fh->totfiles = listingfiles;
+ fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/
+ fh->totparts = 0x0001;
+ fh->partsleft = 0x0001;
+ fh->totsize = listingtotsize;
+ fh->size = listingsize; /* ls -l listing.txt */
+ fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+ fh->checksum = listingchecksum;
+ fh->rfcsum = 0x00000000;
+ fh->rfsize = 0x00000000;
+ fh->cretime = 0x00000000;
+ fh->rfcsum = 0x00000000;
+ fh->nrecvd = 0x00000000;
+ fh->recvcsum = 0x00000000;
+ memset(fh->idstring, 0, sizeof(fh->idstring));
+ memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+ fh->flags = 0x02;
+ fh->lnameoffset = 0x1a;
+ fh->lsizeoffset = 0x10;
+ memset(fh->dummy, 0, sizeof(fh->dummy));
+ memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+ /* we need to figure out these encodings for filenames */
+ fh->nencode = 0x0000;
+ fh->nlanguage = 0x0000;
+ memset(fh->name, 0, sizeof(fh->name));
+ memcpy(fh->name, "listing.txt", sizeof(fh->name));
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ /* XXX: conn leak */
+ perror("calloc (1)");
+ return NULL;
+ }
+
+ memcpy(fh->bcookie, cookie, 8);
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
+ faimdprintf(sess, 1, "eek, bh fail!\n");
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+
+ if (!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) {
+ faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
+ /* XXX: more cleanup, conn leak */
+ perror("calloc (2)");
+ return NULL;
+ }
+
+ memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t));
+ memcpy(cachedcook->cookie, cookie, 8);
+
+ cachedcook->type = AIM_COOKIETYPE_OFTGET;
+ cachedcook->data = (void *)priv;
+
+ if (aim_cachecookie(sess, cachedcook) == -1)
+ faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
+
+ g_free(fh);
+
+ /* OSCAR CAP accept packet */
+
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+
+ newpacket->lock = 1;
+ curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+ for (i = 0; i < 8; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+ curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
+ curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept*/);
+
+ for (i = 0;i < 8; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+ curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid);
+ newpacket->lock = 0;
+ aim_tx_enqueue(sess, newpacket);
+
+ return newconn;
+#endif
+}
+
+/**
+ * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE*
+ * @file is an opened listing file
+ *
+ * returns a pointer to the filled-in fileheader_t
+ *
+ * Currently omits checksum. we'll fix this when AOL breaks us, i
+ * guess.
+ *
+ */
+struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file)
+{
+ return NULL;
+#if 0
+ struct aim_fileheader_t *fh;
+ u_long totsize = 0, size = 0, checksum = 0xffff0000;
+ short totfiles = 0;
+ char *linebuf, sizebuf[9];
+
+ int linelength = 1024;
+
+ /* XXX: if we have a line longer than 1024chars, God help us. */
+ if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
+ faimdprintf(sess, 2, "linebuf calloc failed\n");
+ return NULL;
+ }
+
+ if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
+ perror("getlisting END1 fseek:");
+ faimdprintf(sess, 2, "getlising fseek END1 error\n");
+ }
+
+ if ((size = ftell(file)) == -1) {
+ perror("getlisting END1 getpos:");
+ faimdprintf(sess, 2, "getlising getpos END1 error\n");
+ }
+
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ perror("getlesting fseek(SET):");
+ faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n");
+ }
+
+ memset(linebuf, 0, linelength);
+
+ size = 0;
+
+ while(fgets(linebuf, linelength, file)) {
+ totfiles++;
+ memset(sizebuf, 0, 9);
+
+ size += strlen(linebuf);
+
+ if (strlen(linebuf) < 23) {
+ faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf);
+ continue;
+ }
+ if (linebuf[strlen(linebuf)-1] != '\n') {
+ faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
+ }
+
+ memcpy(sizebuf, linebuf+17, 8);
+
+ totsize += strtol(sizebuf, NULL, 10);
+ memset(linebuf, 0, linelength);
+ }
+
+ if (fseek(file, 0, SEEK_SET) == -1) {
+ perror("getlisting END2 fseek:");
+ faimdprintf(sess, 2, "getlising fseek END2 error\n");
+ }
+
+ g_free(linebuf);
+
+ /* we're going to ignore checksumming the data for now -- that
+ * requires walking the whole listing.txt. it should probably be
+ * done at register time and cached, but, eh. */
+
+ if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
+ return NULL;
+
+ fh->encrypt = 0x0000;
+ fh->compress = 0x0000;
+ fh->totfiles = totfiles;
+ fh->filesleft = totfiles; /* is this right ?*/
+ fh->totparts = 0x0001;
+ fh->partsleft = 0x0001;
+ fh->totsize = totsize;
+ fh->size = size; /* ls -l listing.txt */
+ fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+ fh->checksum = checksum; /* XXX: checksum ! */
+ fh->rfcsum = 0x00000000;
+ fh->rfsize = 0x00000000;
+ fh->cretime = 0x00000000;
+ fh->rfcsum = 0x00000000;
+ fh->nrecvd = 0x00000000;
+ fh->recvcsum = 0x00000000;
+
+ /* memset(fh->idstring, 0, sizeof(fh->idstring)); */
+ memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+ memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
+
+ fh->flags = 0x02;
+ fh->lnameoffset = 0x1a;
+ fh->lsizeoffset = 0x10;
+
+ /* memset(fh->dummy, 0, sizeof(fh->dummy)); */
+ memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+ fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */
+ fh->nlanguage = 0x0000;
+
+ /* memset(fh->name, 0, sizeof(fh->name)); */
+ memcpy(fh->name, "listing.txt", sizeof(fh->name));
+ memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
+
+ faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
+ return fh;
+#endif
+}
+
+/**
+ * aim_listenestablish - create a listening socket on a port.
+ * @portnum: the port number to bind to.
+ *
+ * you need to call accept() when it's connected. returns your fd
+ *
+ * XXX: give the client author the responsibility of setting up a
+ * listener, then we no longer have a libfaim problem with broken
+ * solaris *innocent smile* -jbm
+ */
+static int listenestablish(guint16 portnum)
+{
+#if HAVE_GETADDRINFO
+ int listenfd;
+ const int on = 1;
+ struct addrinfo hints, *res, *ressave;
+ char serv[5];
+
+ g_snprintf(serv, sizeof(serv), "%d", portnum);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
+ perror("getaddrinfo");
+ return -1;
+ }
+ ressave = res;
+ do {
+ listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (listenfd < 0)
+ continue;
+ setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+ /* success */
+ closesocket(listenfd);
+ } while ( (res = res->ai_next) );
+
+ if (!res)
+ return -1;
+
+ if (listen(listenfd, 1024)!=0) {
+ perror("listen");
+ return -1;
+ }
+
+ freeaddrinfo(ressave);
+ return listenfd;
+#else
+ int listenfd;
+ const int on = 1;
+ struct sockaddr_in sockin;
+
+ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket(listenfd)");
+ return -1;
+ }
+
+ if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) {
+ perror("setsockopt(listenfd)");
+ closesocket(listenfd);
+ return -1;
+ }
+
+ memset(&sockin, 0, sizeof(struct sockaddr_in));
+ sockin.sin_family = AF_INET;
+ sockin.sin_port = htons(portnum);
+
+ if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
+ perror("bind(listenfd)");
+ closesocket(listenfd);
+ return -1;
+ }
+ if (listen(listenfd, 4) != 0) {
+ perror("listen(listenfd)");
+ closesocket(listenfd);
+ return -1;
+ }
+ return listenfd;
+#endif
+}
+
+static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+#if 0
+ struct aim_filetransfer_priv *ft;
+ aim_rxcallback_t userfunc;
+
+ ft = conn->priv;
+ if (ft->state == 2) {
+ /* waiting on listing data */
+ int ret = 0;
+ char *listing;
+ struct command_tx_struct *newoft;
+
+ if (!(listing = g_malloc(ft->fh.size)))
+ return -1;
+
+ ft->state = 0;
+ if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)
+ faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
+ faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
+ faim_mutex_unlock(&conn->active);
+ g_free(listing);
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ /* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */
+
+ ft->fh.nrecvd = ft->fh.size;
+ ft->fh.recvcsum = ft->fh.checksum;
+ ft->fh.flags = 0;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ aim_frame_destroy(newoft);
+ g_free(listing);
+ return -1;
+ }
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
+ faimdprintf(sess, 2, "eek! bh fail listing\n");
+
+ /* send the 120b */
+ aim_tx_enqueue(sess, newoft);
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
+ ret = userfunc(sess, NULL, conn, ft, listing);
+
+ g_free(listing);
+ return ret;
+ }
+
+ if (ft->state == 3) {
+ /* waiting on file data */
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) )
+ return userfunc(sess, NULL, conn, ft);
+ return 0;
+ }
+
+ if (ft->state == 4) {
+ if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) )
+ return userfunc(sess, NULL, conn);
+ aim_conn_close(conn);
+ return 0;
+ }
+
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_msgcookie_t *cook;
+ struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+ cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTSEND);
+ aim_cookie_free(sess, cook);
+
+ return;
+}
+
+static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+
+ g_free(conn->internal);
+
+ return;
+}
+
+static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_msgcookie_t *cook;
+ struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+ cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTGET);
+ aim_cookie_free(sess, cook);
+
+ return;
+}
+
+static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+
+ g_free(conn->internal);
+
+ return;
+}
+
+static void connclose_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+ struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+ aim_msgcookie_t *cook;
+
+ cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM);
+ aim_cookie_free(sess, cook);
+
+ return;
+}
+
+static void connkill_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+
+ g_free(conn->internal);
+
+ return;
+}
+
+void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+ if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+ return;
+
+ if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+ connclose_sendfile(sess, conn);
+ else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+ connclose_getfile(sess, conn);
+ else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+ connclose_directim(sess, conn);
+
+ return;
+}
+
+void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+ if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+ return;
+
+ if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+ connkill_sendfile(sess, conn);
+ else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+ connkill_getfile(sess, conn);
+ else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+ connkill_directim(sess, conn);
+
+ return;
+}
+
+static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+ aim_frame_t fr;
+ aim_rxcallback_t userfunc;
+ guint32 payloadlength;
+ guint16 flags;
+ char *snptr = NULL;
+
+ fr.conn = conn;
+
+ payloadlength = aimutil_get32(hdr+22);
+ flags = aimutil_get16(hdr+32);
+ snptr = (char *)hdr+38;
+
+ if (flags == 0x000e) {
+ int ret = 0;
+
+ if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+ ret = userfunc(sess, &fr, snptr, 1);
+
+ return ret;
+
+ } else if (flags == 0x0002) {
+ int ret = 0;
+
+ if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+ ret = userfunc(sess, &fr, snptr, 0);
+
+ return ret;
+
+ } else if ((flags == 0x0000) && payloadlength) {
+ char *msg, *msg2;
+ int ret = 0;
+ int recvd = 0;
+ int i;
+
+ if (!(msg = g_malloc(payloadlength+1)))
+ return -1;
+ memset(msg, 0, payloadlength+1);
+ msg2 = msg;
+
+ while (payloadlength - recvd) {
+ if (payloadlength - recvd >= 1024)
+ i = aim_recv(conn->fd, msg2, 1024);
+ else
+ i = aim_recv(conn->fd, msg2, payloadlength - recvd);
+ if (i <= 0) {
+ g_free(msg);
+ return -1;
+ }
+ recvd = recvd + i;
+ msg2 = msg2 + i;
+ if ((userfunc=aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
+ userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
+ }
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
+ ret = userfunc(sess, &fr, snptr, msg, payloadlength);
+
+ g_free(msg);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_filetransfer_priv *ft;
+ struct aim_fileheader_t *fh;
+ struct aim_msgcookie_t *cook;
+ struct command_tx_struct *newoft;
+ aim_rxcallback_t userfunc;
+
+ faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
+ fh = aim_oft_getfh(hdr);
+
+ faim_mutex_unlock(&conn->active);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+ g_free(fh);
+ return -1;
+ }
+
+ ft = cook->data;
+
+ /* we're waaaaiiiting.. for listing.txt */
+ ft->state = 2;
+
+ memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+ g_free(fh);
+
+ if(aim_cachecookie(sess, cook) == -1) {
+ faimdprintf(sess, 1, "error caching cookie\n");
+ return -1;
+ }
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+#endif
+ return -1;
+}
+
+static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_filetransfer_priv *ft;
+ struct aim_fileheader_t *fh;
+ struct aim_msgcookie_t *cook;
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
+ faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", AIM_COOKIETYPE_OFTGET, fh->bcookie);
+
+ ft = cook->data;
+
+ if (ft->fh.size != fh->size)
+ faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size);
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
+ ret = userfunc(sess, NULL, conn, fh);
+
+ faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
+
+ g_free(fh);
+
+ return ret;
+#else
+ return -1;
+#endif
+}
+
+static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_filetransfer_priv *ft;
+ struct aim_msgcookie_t *cook;
+ struct aim_fileheader_t *fh;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+ g_free(fh);
+ return -1;
+ }
+
+ g_free(fh);
+
+ ft = cook->data;
+
+ if (aim_cachecookie(sess, cook) == -1)
+ return -1;
+
+ if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
+ return userfunc(sess, NULL, conn);
+#endif
+ return -1;
+}
+
+static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_filetransfer_priv *ft;
+ struct aim_msgcookie_t *cook;
+ struct aim_fileheader_t *fh;
+ struct command_tx_struct *newoft;
+ int i = 0;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+ g_free(fh);
+ return -1;
+ }
+
+ ft = cook->data;
+ memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+ g_free(fh);
+
+ aim_cachecookie(sess, cook);
+
+ faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
+ i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
+
+ if (i < 0)
+ return i;
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
+ faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
+ ft->fh.nrecvd = 0;
+ ft->fh.recvcsum = 0;
+ ft->fh.flags = 0x20;
+
+ aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+
+ faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
+
+ return i;
+#else
+ return -1;
+#endif
+}
+
+static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_fileheader_t *fh;
+ struct aim_filetransfer_priv *ft;
+ struct aim_msgcookie_t *cook;
+ struct command_tx_struct *newoft;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+ g_free(fh);
+ return -1;
+ }
+
+ g_free(fh);
+
+ ft = cook->data;
+
+ ft->state = 3;
+
+ if (aim_cachecookie(sess, cook) == -1)
+ return -1;
+
+ faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+ faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+
+ faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
+ return 1;
+#else
+ return -1;
+#endif
+}
+
+static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_fileheader_t *fh;
+ struct aim_filetransfer_priv *ft;
+ struct aim_msgcookie_t *cook;
+ int ret = 1;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+ g_free(fh);
+ return -1;
+ }
+
+ ft = cook->data;
+
+ faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
+ ret = userfunc(sess, NULL, conn, fh);
+
+ g_free(fh);
+
+ return ret;
+#else
+ return -1;
+#endif
+}
+
+static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+ struct aim_fileheader_t *fh;
+ aim_rxcallback_t userfunc;
+
+ fh = aim_oft_getfh(hdr);
+
+ faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
+
+ if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
+ userfunc(sess, NULL, conn, fh);
+
+ g_free(fh);
+#endif
+
+ return -1;
+}
+
+/**
+ * aim_get_command_rendezvous - OFT equivalent of aim_get_command
+ * @sess: session to work on
+ * @conn: conn to pull data from
+ *
+ * this reads and handles data from conn->fd. currently a little rough
+ * around the edges
+ */
+int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn)
+{
+ guint8 hdrbuf1[6];
+ guint8 *hdr = NULL;
+ int hdrlen, hdrtype;
+ int ret = -1;
+
+ if (!sess || !conn)
+ return -1;
+
+ memset(hdrbuf1, 0, sizeof(hdrbuf1));
+
+ /* I guess? I didn't understand any of that mess... */
+ if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+ return getcommand_getfile(sess, conn);
+
+ /* XXX fix all the error cases here */
+ if (aim_recv(conn->fd, hdrbuf1, 6) < 6) {
+
+ do_error_dialog(sess->aux_data, "read error", "Gaim");
+
+ aim_conn_close(conn);
+
+ return -1;
+ }
+
+ hdrlen = aimutil_get16(hdrbuf1+4);
+ hdrlen -= 6;
+
+ hdr = g_malloc(hdrlen);
+
+ if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
+ do_error_dialog(sess->aux_data, "read error", "Gaim");
+ g_free(hdr);
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ hdrtype = aimutil_get16(hdr);
+
+ if (hdrtype == 0x0001)
+ ret = handlehdr_directim(sess, conn, hdr);
+ else if (hdrtype == 0x1108) /* getfile listing.txt incoming tx->rx */
+ ret = handlehdr_getfile_listing(sess, conn, hdr);
+ else if (hdrtype == 0x1209) /* get file listing ack rx->tx */
+ ret = handlehdr_getfile_listing2(sess, conn, hdr);
+ else if (hdrtype == 0x120b) /* get file listing rx confirm */
+ ret = handlehdr_getfile_listing3(sess, conn, hdr);
+ else if (hdrtype == 0x120c) /* getfile request */
+ ret = handlehdr_getfile_request(sess, conn, hdr);
+ else if (hdrtype == 0x0101) /* getfile sending data */
+ ret = handlehdr_getfile_sending(sess, conn, hdr);
+ else if (hdrtype == 0x0202) /* getfile recv data */
+ ret = handlehdr_getfile_recv(sess, conn, hdr);
+ else if (hdrtype == 0x0204) /* getfile finished */
+ ret = handlehdr_getfile_finish(sess, conn, hdr);
+ else {
+ ret = -1;
+ }
+
+ g_free(hdr);
+
+ if (ret == -1)
+ aim_conn_close(conn);
+
+ return ret;
+}
+
+#if 0
+/**
+ * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
+ * @hdr: buffer to extract header from
+ *
+ * returns pointer to new struct on success; %NULL on error.
+ *
+ */
+static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr)
+{
+ struct aim_fileheader_t *fh;
+ int i, j;
+ if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+ return NULL;
+
+ /* [0] and [1] are the type. we can ignore those here. */
+ i = 2;
+ for(j = 0; j < 8; j++, i++)
+ fh->bcookie[j] = hdr[i];
+ fh->encrypt = aimutil_get16(hdr+i);
+ i += 2;
+ fh->compress = aimutil_get16(hdr+i);
+ i += 2;
+ fh->totfiles = aimutil_get16(hdr+i);
+ i += 2;
+ fh->filesleft = aimutil_get16(hdr+i);
+ i += 2;
+ fh->totparts = aimutil_get16(hdr+i);
+ i += 2;
+ fh->partsleft = aimutil_get16(hdr+i);
+ i += 2;
+ fh->totsize = aimutil_get32(hdr+i);
+ i += 4;
+ fh->size = aimutil_get32(hdr+i);
+ i += 4;
+ fh->modtime = aimutil_get32(hdr+i);
+ i += 4;
+ fh->checksum = aimutil_get32(hdr+i);
+ i += 4;
+ fh->rfrcsum = aimutil_get32(hdr+i);
+ i += 4;
+ fh->rfsize = aimutil_get32(hdr+i);
+ i += 4;
+ fh->cretime = aimutil_get32(hdr+i);
+ i += 4;
+ fh->rfcsum = aimutil_get32(hdr+i);
+ i += 4;
+ fh->nrecvd = aimutil_get32(hdr+i);
+ i += 4;
+ fh->recvcsum = aimutil_get32(hdr+i);
+ i += 4;
+ memcpy(fh->idstring, hdr+i, 32);
+ i += 32;
+ fh->flags = aimutil_get8(hdr+i);
+ i += 1;
+ fh->lnameoffset = aimutil_get8(hdr+i);
+ i += 1;
+ fh->lsizeoffset = aimutil_get8(hdr+i);
+ i += 1;
+ memcpy(fh->dummy, hdr+i, 69);
+ i += 69;
+ memcpy(fh->macfileinfo, hdr+i, 16);
+ i += 16;
+ fh->nencode = aimutil_get16(hdr+i);
+ i += 2;
+ fh->nlanguage = aimutil_get16(hdr+i);
+ i += 2;
+ memcpy(fh->name, hdr+i, 64);
+ i += 64;
+ return fh;
+}
+#endif
+
+/**
+ * aim_oft_checksum - calculate oft checksum of buffer
+ * @buffer: buffer of data to checksum
+ * @bufsize: size of buffer
+ * @checksum: pointer to integer to place result in (pointer!)
+ *
+ *
+ * Note that checksum is a pointer. Checksum should be filled with
+ * 0xFFFF0000 for each new file; you can have this checksum chunks of
+ * files in series if you just call it repeatedly in a for(; ; ) loop
+ * and don't reset the checksum between each call. And you thought we
+ * didn't care about you and your pathetic client's meomry footprint
+ * ;^)
+ *
+ *
+ * Also, it's been said that this is incorrect as currently
+ * written. You were warned.
+ */
+guint32 aim_oft_checksum(aim_session_t *sess, const char *buffer, int bufsize, guint32 *checksum)
+{
+ return 0xdeadbeef;
+#if 0
+ guint16 check0, check1;
+ int i;
+
+ check0 = ((*checksum & 0xFF000000) >> 16);
+ check1 = ((*checksum & 0x00ff0000) >> 16);
+ for(i = 0; i < bufsize; i++) {
+ if (i % 2) { /* use check1 -- second byte */
+ if ( (short)buffer[i] > check1 ) { /* wrapping */
+ check1 += 0x100; /* this is a cheap way to wrap */
+
+ /* if we're wrapping, decrement the other one */
+ /* XXX: check this corner case */
+ if (check0 == 0)
+ check0 = 0x00ff;
+ else
+ check0--;
+ }
+ check1 -= buffer[i];
+ } else { /* use check0 -- first byte */
+ if ( (short)buffer[i] > check0 ) { /* wrapping */
+ check0 += 0x100; /* this is a cheap way to wrap */
+
+ /* if we're wrapping, decrement the other one */
+ /* XXX: check this corner case */
+ if (check1 == 0)
+ check1 = 0x00ff;
+ else
+ check1--;
+ }
+ check0 -= buffer[i];
+ }
+ }
+
+ if (check0 > 0xff || check1 > 0xff) {
+ /* they shouldn't be able to do this. error! */
+ faimdprintf(sess, 2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
+ return -1;
+ }
+
+ /* grab just the lowest byte; this should be clean, but just in
+ case */
+ check0 &= 0xff;
+ check1 &= 0xff;
+
+ *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
+ return *checksum;
+#endif
+}
+
+#if 0
+/**
+ * aim_oft_buildheader - fills a buffer with network-order fh data
+ * @dest: buffer to fill -- pre-alloced
+ * @fh: fh to get data from
+ *
+ * returns length written; -1 on error.
+ * DOES NOT DO BOUNDS CHECKING!
+ *
+ */
+static int oft_buildheader(unsigned char *dest, struct aim_fileheader_t *fh)
+{
+ int i, curbyte;
+ if (!dest || !fh)
+ return -1;
+ curbyte = 0;
+ for(i = 0; i < 8; i++)
+ curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]);
+ curbyte += aimutil_put16(dest+curbyte, fh->encrypt);
+ curbyte += aimutil_put16(dest+curbyte, fh->compress);
+ curbyte += aimutil_put16(dest+curbyte, fh->totfiles);
+ curbyte += aimutil_put16(dest+curbyte, fh->filesleft);
+ curbyte += aimutil_put16(dest+curbyte, fh->totparts);
+ curbyte += aimutil_put16(dest+curbyte, fh->partsleft);
+ curbyte += aimutil_put32(dest+curbyte, fh->totsize);
+ curbyte += aimutil_put32(dest+curbyte, fh->size);
+ curbyte += aimutil_put32(dest+curbyte, fh->modtime);
+ curbyte += aimutil_put32(dest+curbyte, fh->checksum);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfsize);
+ curbyte += aimutil_put32(dest+curbyte, fh->cretime);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfcsum);
+ curbyte += aimutil_put32(dest+curbyte, fh->nrecvd);
+ curbyte += aimutil_put32(dest+curbyte, fh->recvcsum);
+ memcpy(dest+curbyte, fh->idstring, 32);
+ curbyte += 32;
+ curbyte += aimutil_put8(dest+curbyte, fh->flags);
+ curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset);
+ curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset);
+ memcpy(dest+curbyte, fh->dummy, 69);
+ curbyte += 69;
+ memcpy(dest+curbyte, fh->macfileinfo, 16);
+ curbyte += 16;
+ curbyte += aimutil_put16(dest+curbyte, fh->nencode);
+ curbyte += aimutil_put16(dest+curbyte, fh->nlanguage);
+ memset(dest+curbyte, 0x00, 64);
+ memcpy(dest+curbyte, fh->name, 64);
+
+ /* XXX: Filenames longer than 64B */
+ curbyte += 64;
+ return curbyte;
+}
+#endif
+
+/**
+ * aim_getfile_intitiate - Request an OFT getfile session
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn is the SN to connect to.
+ *
+ * returns a new &aim_conn_t on success, %NULL on error
+ */
+aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn)
+{
+ return NULL;
+#if 0
+ struct command_tx_struct *newpacket;
+ struct aim_conn_t *newconn;
+ struct aim_filetransfer_priv *priv;
+ struct aim_msgcookie_t *cookie;
+ int curbyte, i, listenfd;
+ short port = 4443;
+ struct hostent *hptr;
+ struct utsname myname;
+ char cap[16];
+ char d[4];
+
+ /* Open our socket */
+
+ if ( (listenfd = aim_listenestablish(port)) == -1)
+ return NULL;
+
+ /* get our local IP */
+
+ if (uname(&myname) < 0)
+ return NULL;
+ if ( (hptr = gethostbyname(myname.nodename)) == NULL)
+ return NULL;
+ memcpy(&d, hptr->h_addr_list[0], 4);
+
+ aim_putcap(cap, 16, AIM_CAPS_GETFILE);
+
+ /* create the OSCAR packet */
+
+ if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42)))
+ return NULL;
+ newpacket->lock = 1;
+
+ /* lock struct */
+ curbyte = 0;
+ curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+ /* XXX: check the cookie before commiting to using it */
+
+ /* Generate a random message cookie
+ * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
+ for (i=0; i<7; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10));
+
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ /* grab all the data for cookie caching. */
+
+ if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t))))
+ return NULL;
+ memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+ cookie->type = AIM_COOKIETYPE_OFTGET;
+
+ if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv))))
+ return NULL;
+ memcpy(priv->cookie, cookie, 8);
+ memcpy(priv->sn, destsn, sizeof(priv->sn));
+ memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
+ priv->state = 1;
+
+ cookie->data = priv;
+
+ aim_cachecookie(sess, cookie);
+
+ /* Channel ID */
+ curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+
+ /* Destination SN (prepended with byte length) */
+ curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+ curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+ /* enTLV start */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042);
+
+ /* Flag data / ICBM Parameters? */
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ /* Cookie */
+ curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
+
+ /* Capability String */
+ curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+
+ /* 000a/0002 : 0001 */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+ /* 0003/0004: IP address */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+ for(i = 0; i < 4; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
+
+ /* already in network byte order */
+
+ /* 0005/0002: Port */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+ curbyte += aimutil_put16(newpacket->data+curbyte, port);
+
+ /* 000f/0000: ?? */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+ /* 2711/000c: ?? */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
+ curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
+
+ for(i = 0; i < 0x000c - 4; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ newpacket->commandlen = curbyte;
+ newpacket->lock = 0;
+ aim_tx_enqueue(sess, newpacket);
+
+ /* allocate and set up our connection */
+
+ i = fcntl(listenfd, F_GETFL, 0);
+ fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
+ newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
+
+ if (!newconn){
+ perror("aim_newconn");
+ return NULL;
+ }
+
+ newconn->fd = listenfd;
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+ newconn->priv = priv;
+
+ return newconn;
+#endif
+}
+
+/**
+ * aim_oft_getfile_request - request a particular file over an established getfile connection
+ * @sess: your session
+ * @conn: the established OFT getfile connection
+ * @name: filename to request
+ * @size: size of the file
+ *
+ *
+ * returns -1 on error, 0 on successful enqueuing
+ */
+int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size)
+{
+ return -EINVAL;
+#if 0
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+ if (!sess || !conn || !conn->priv || !name)
+ return -1;
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) {
+ faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+ ft->fh.filesleft = 1;
+ ft->fh.totfiles = 1;
+ ft->fh.totparts = 1;
+ ft->fh.partsleft = 1;
+ ft->fh.totsize = size;
+ ft->fh.size = size;
+ ft->fh.checksum = 0;
+ memcpy(ft->fh.name, name, strlen(name));
+ memset(ft->fh.name+strlen(name), 0, 1);
+
+ if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+#endif
+}
+
+/**
+ * aim_oft_getfile_ack - acknowledge a getfile download as complete
+ * @sess: your session
+ * @conn: the getfile conn to send the ack over
+ *
+ * Call this function after you have read all the data in a particular
+ * filetransfer. Returns -1 on error, 0 on apparent success
+ *
+ */
+int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn)
+{
+ return -EINVAL;
+#if 0
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+
+ if (!sess || !conn || !conn->priv)
+ return -1;
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+ faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100-8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+#endif
+}
+
+/**
+ * aim_oft_getfile_end - end a getfile.
+ * @sess: your session
+ * @conn: the getfile connection
+ *
+ * call this before you close the getfile connection if you're on the
+ * receiving/requesting end.
+ */
+int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn)
+{
+ return -EINVAL;
+#if 0
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+
+ if (!sess || !conn || !conn->priv)
+ return -1;
+
+ if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0204, 0))) {
+ faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+ ft->state = 4; /* no longer wanting data */
+ ft->fh.nrecvd = ft->fh.size;
+ ft->fh.recvcsum = ft->fh.checksum;
+ ft->fh.flags = 0x21;
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_frame_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+
+ return 0;
+#endif /* 0 */
+}
+
diff --git a/protocols/oscar/ft.h b/protocols/oscar/ft.h
new file mode 100644
index 00000000..7b1142eb
--- /dev/null
+++ b/protocols/oscar/ft.h
@@ -0,0 +1,78 @@
+#ifndef __OSCAR_FT_H__
+#define __OSCAR_FT_H__
+
+#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
+
+/*
+ * OFT Services
+ *
+ * See non-SNAC note below.
+ */
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
+#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+
+#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */
+#define AIM_CB_OFT_GETFILEFILEREQ 0x0008 /* received file request */
+#define AIM_CB_OFT_GETFILEFILESEND 0x0009 /* received file request confirm -- send data */
+#define AIM_CB_OFT_GETFILECOMPLETE 0x000a /* received file send complete*/
+#define AIM_CB_OFT_GETFILEINITIATE 0x000b /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c /* OFT connection disconnected.*/
+#define AIM_CB_OFT_GETFILELISTING 0x000d /* OFT listing.txt received.*/
+#define AIM_CB_OFT_GETFILERECEIVE 0x000e /* OFT file incoming.*/
+#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f
+#define AIM_CB_OFT_GETFILESTATE4 0x0010
+
+#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020 /* OFT connection disconnected.*/
+
+struct aim_fileheader_t {
+#if 0
+ char magic[4]; /* 0 */
+ short hdrlen; /* 4 */
+ short hdrtype; /* 6 */
+#endif
+ char bcookie[8]; /* 8 */
+ short encrypt; /* 16 */
+ short compress; /* 18 */
+ short totfiles; /* 20 */
+ short filesleft; /* 22 */
+ short totparts; /* 24 */
+ short partsleft; /* 26 */
+ long totsize; /* 28 */
+ long size; /* 32 */
+ long modtime; /* 36 */
+ long checksum; /* 40 */
+ long rfrcsum; /* 44 */
+ long rfsize; /* 48 */
+ long cretime; /* 52 */
+ long rfcsum; /* 56 */
+ long nrecvd; /* 60 */
+ long recvcsum; /* 64 */
+ char idstring[32]; /* 68 */
+ char flags; /* 100 */
+ char lnameoffset; /* 101 */
+ char lsizeoffset; /* 102 */
+ char dummy[69]; /* 103 */
+ char macfileinfo[16]; /* 172 */
+ short nencode; /* 188 */
+ short nlanguage; /* 190 */
+ char name[64]; /* 192 */
+ /* 256 */
+};
+
+
+
+struct aim_filetransfer_priv {
+ char sn[MAXSNLEN];
+ guint8 cookie[8];
+ char ip[30];
+ int state;
+ struct aim_fileheader_t fh;
+};
+
+#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
+
+#endif /* __OSCAR_FT_H__ */
diff --git a/protocols/oscar/icq.c b/protocols/oscar/icq.c
new file mode 100644
index 00000000..23959b75
--- /dev/null
+++ b/protocols/oscar/icq.c
@@ -0,0 +1,441 @@
+/*
+ * Encapsulated ICQ.
+ *
+ */
+
+#include <aim.h>
+#include "icq.h"
+
+int aim_icq_reqofflinemsgs(aim_session_t *sess)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int bslen;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+ return -EINVAL;
+
+ bslen = 2 + 4 + 2 + 2;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+ /* For simplicity, don't bother using a tlvlist */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, bslen);
+
+ aimbs_putle16(&fr->data, bslen - 2);
+ aimbs_putle32(&fr->data, atoi(sess->sn));
+ aimbs_putle16(&fr->data, 0x003c); /* I command thee. */
+ aimbs_putle16(&fr->data, snacid); /* eh. */
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_icq_ackofflinemsgs(aim_session_t *sess)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int bslen;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+ return -EINVAL;
+
+ bslen = 2 + 4 + 2 + 2;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+ /* For simplicity, don't bother using a tlvlist */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, bslen);
+
+ aimbs_putle16(&fr->data, bslen - 2);
+ aimbs_putle32(&fr->data, atoi(sess->sn));
+ aimbs_putle16(&fr->data, 0x003e); /* I command thee. */
+ aimbs_putle16(&fr->data, snacid); /* eh. */
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int bslen;
+
+ if (!xml || !strlen(xml))
+ return -EINVAL;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+ return -EINVAL;
+
+ bslen = 2 + 10 + 2 + strlen(xml) + 1;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+ /* For simplicity, don't bother using a tlvlist */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, bslen);
+
+ aimbs_putle16(&fr->data, bslen - 2);
+ aimbs_putle32(&fr->data, atoi(sess->sn));
+ aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+ aimbs_putle16(&fr->data, snacid); /* eh. */
+ aimbs_putle16(&fr->data, 0x0998); /* shrug. */
+ aimbs_putle16(&fr->data, strlen(xml) + 1);
+ aimbs_putraw(&fr->data, (guint8 *)xml, strlen(xml) + 1);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int bslen;
+ struct aim_icq_info *info;
+
+ if (!uin || uin[0] < '0' || uin[0] > '9')
+ return -EINVAL;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+ return -EINVAL;
+
+ bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+ /* For simplicity, don't bother using a tlvlist */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, bslen);
+
+ aimbs_putle16(&fr->data, bslen - 2);
+ aimbs_putle32(&fr->data, atoi(sess->sn));
+ aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+ aimbs_putle16(&fr->data, snacid); /* eh. */
+ aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
+ aimbs_putle32(&fr->data, atoi(uin));
+
+ aim_tx_enqueue(sess, fr);
+
+ /* Keep track of this request and the ICQ number and request ID */
+ info = g_new0(struct aim_icq_info, 1);
+ info->reqid = snacid;
+ info->uin = atoi(uin);
+ info->next = sess->icq_info;
+ sess->icq_info = info;
+
+ return 0;
+}
+
+int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int bslen;
+
+ if (!uin || uin[0] < '0' || uin[0] > '9')
+ return -EINVAL;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+ return -EINVAL;
+
+ bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+ /* For simplicity, don't bother using a tlvlist */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, bslen);
+
+ aimbs_putle16(&fr->data, bslen - 2);
+ aimbs_putle32(&fr->data, atoi(sess->sn));
+ aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+ aimbs_putle16(&fr->data, snacid); /* eh. */
+ aimbs_putle16(&fr->data, 0x051f); /* shrug. */
+ aimbs_putle32(&fr->data, atoi(uin));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static void aim_icq_freeinfo(struct aim_icq_info *info) {
+ int i;
+
+ if (!info)
+ return;
+ g_free(info->nick);
+ g_free(info->first);
+ g_free(info->last);
+ g_free(info->email);
+ g_free(info->homecity);
+ g_free(info->homestate);
+ g_free(info->homephone);
+ g_free(info->homefax);
+ g_free(info->homeaddr);
+ g_free(info->mobile);
+ g_free(info->homezip);
+ g_free(info->personalwebpage);
+ if (info->email2)
+ for (i = 0; i < info->numaddresses; i++)
+ g_free(info->email2[i]);
+ g_free(info->email2);
+ g_free(info->workcity);
+ g_free(info->workstate);
+ g_free(info->workphone);
+ g_free(info->workfax);
+ g_free(info->workaddr);
+ g_free(info->workzip);
+ g_free(info->workcompany);
+ g_free(info->workdivision);
+ g_free(info->workposition);
+ g_free(info->workwebpage);
+ g_free(info->info);
+ g_free(info);
+}
+
+/**
+ * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
+ */
+static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_tlvlist_t *tl;
+ aim_tlv_t *datatlv;
+ aim_bstream_t qbs;
+ guint32 ouruin;
+ guint16 cmdlen, cmd, reqid;
+
+ if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) {
+ aim_freetlvchain(&tl);
+ do_error_dialog(sess->aux_data, "corrupt ICQ response\n", "Gaim");
+ return 0;
+ }
+
+ aim_bstream_init(&qbs, datatlv->value, datatlv->length);
+
+ cmdlen = aimbs_getle16(&qbs);
+ ouruin = aimbs_getle32(&qbs);
+ cmd = aimbs_getle16(&qbs);
+ reqid = aimbs_getle16(&qbs);
+
+ if (cmd == 0x0041) { /* offline message */
+ guint16 msglen;
+ struct aim_icq_offlinemsg msg;
+ aim_rxcallback_t userfunc;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.sender = aimbs_getle32(&qbs);
+ msg.year = aimbs_getle16(&qbs);
+ msg.month = aimbs_getle8(&qbs);
+ msg.day = aimbs_getle8(&qbs);
+ msg.hour = aimbs_getle8(&qbs);
+ msg.minute = aimbs_getle8(&qbs);
+ msg.type = aimbs_getle16(&qbs);
+ msglen = aimbs_getle16(&qbs);
+ msg.msg = aimbs_getstr(&qbs, msglen);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG)))
+ ret = userfunc(sess, rx, &msg);
+
+ g_free(msg.msg);
+
+ } else if (cmd == 0x0042) {
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE)))
+ ret = userfunc(sess, rx);
+ } else if (cmd == 0x07da) { /* information */
+ guint16 subtype;
+ struct aim_icq_info *info;
+ aim_rxcallback_t userfunc;
+
+ subtype = aimbs_getle16(&qbs);
+ aim_bstream_advance(&qbs, 1); /* 0x0a */
+
+ /* find another data from the same request */
+ for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next);
+
+ if (!info) {
+ info = g_new0(struct aim_icq_info, 1);
+ info->reqid = reqid;
+ info->next = sess->icq_info;
+ sess->icq_info = info;
+ }
+
+ switch (subtype) {
+ case 0x00a0: { /* hide ip status */
+ /* nothing */
+ } break;
+ case 0x00aa: { /* password change status */
+ /* nothing */
+ } break;
+ case 0x00c8: { /* general and "home" information */
+ info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->homecountry = aimbs_getle16(&qbs);
+ /* 0x0a 00 02 00 */
+ /* 1 byte timezone? */
+ /* 1 byte hide email flag? */
+ } break;
+ case 0x00dc: { /* personal information */
+ info->age = aimbs_getle8(&qbs);
+ info->unknown = aimbs_getle8(&qbs);
+ info->gender = aimbs_getle8(&qbs);
+ info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->birthyear = aimbs_getle16(&qbs);
+ info->birthmonth = aimbs_getle8(&qbs);
+ info->birthday = aimbs_getle8(&qbs);
+ info->language1 = aimbs_getle8(&qbs);
+ info->language2 = aimbs_getle8(&qbs);
+ info->language3 = aimbs_getle8(&qbs);
+ /* 0x00 00 01 00 00 01 00 00 00 00 00 */
+ } break;
+ case 0x00d2: { /* work information */
+ info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workcountry = aimbs_getle16(&qbs);
+ info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ aim_bstream_advance(&qbs, 2); /* 0x01 00 */
+ info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ } break;
+ case 0x00e6: { /* additional personal information */
+ info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1);
+ } break;
+ case 0x00eb: { /* email address(es) */
+ int i;
+ info->numaddresses = aimbs_getle16(&qbs);
+ info->email2 = g_new0(char *, info->numaddresses);
+ for (i = 0; i < info->numaddresses; i++) {
+ info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ if (i+1 != info->numaddresses)
+ aim_bstream_advance(&qbs, 1); /* 0x00 */
+ }
+ } break;
+ case 0x00f0: { /* personal interests */
+ } break;
+ case 0x00fa: { /* past background and current organizations */
+ } break;
+ case 0x0104: { /* alias info */
+ info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ aim_bstream_advance(&qbs, aimbs_getle16(&qbs));
+ /* email address? */
+ /* Then 0x00 02 00 */
+ } break;
+ case 0x010e: { /* unknown */
+ /* 0x00 00 */
+ } break;
+
+ case 0x019a: { /* simple info */
+ aim_bstream_advance(&qbs, 2);
+ info->uin = aimbs_getle32(&qbs);
+ info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+ /* Then 0x00 02 00 00 00 00 00 */
+ } break;
+ } /* End switch statement */
+
+
+ if (!(snac->flags & 0x0001)) {
+ if (subtype != 0x0104)
+ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO)))
+ ret = userfunc(sess, rx, info);
+
+ /* Bitlbee - not supported, yet
+ if (info->uin && info->nick)
+ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS)))
+ ret = userfunc(sess, rx, info);
+ */
+
+ if (sess->icq_info == info) {
+ sess->icq_info = info->next;
+ } else {
+ struct aim_icq_info *cur;
+ for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
+ if (cur->next)
+ cur->next = cur->next->next;
+ }
+ aim_icq_freeinfo(info);
+ }
+ }
+
+ aim_freetlvchain(&tl);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return icqresponse(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int icq_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0015;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x047c;
+ mod->flags = 0;
+ strncpy(mod->name, "icq", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+
diff --git a/protocols/oscar/icq.h b/protocols/oscar/icq.h
new file mode 100644
index 00000000..c496f39c
--- /dev/null
+++ b/protocols/oscar/icq.h
@@ -0,0 +1,98 @@
+#ifndef __OSCAR_ICQ_H__
+#define __OSCAR_ICQ_H__
+
+#define AIM_CB_FAM_ICQ 0x0015
+
+/*
+ * SNAC Family: ICQ
+ *
+ * Most of these are actually special.
+ */
+#define AIM_CB_ICQ_ERROR 0x0001
+#define AIM_CB_ICQ_OFFLINEMSG 0x00f0
+#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1
+#define AIM_CB_ICQ_SIMPLEINFO 0x00f2
+#define AIM_CB_ICQ_INFO 0x00f2 /* just transitional */
+#define AIM_CB_ICQ_DEFAULT 0xffff
+
+struct aim_icq_offlinemsg {
+ guint32 sender;
+ guint16 year;
+ guint8 month, day, hour, minute;
+ guint16 type;
+ char *msg;
+};
+
+struct aim_icq_simpleinfo {
+ guint32 uin;
+ char *nick;
+ char *first;
+ char *last;
+ char *email;
+};
+
+struct aim_icq_info {
+ gushort reqid;
+
+ /* simple */
+ guint32 uin;
+
+ /* general and "home" information (0x00c8) */
+ char *nick;
+ char *first;
+ char *last;
+ char *email;
+ char *homecity;
+ char *homestate;
+ char *homephone;
+ char *homefax;
+ char *homeaddr;
+ char *mobile;
+ char *homezip;
+ gushort homecountry;
+/* guchar timezone;
+ guchar hideemail; */
+
+ /* personal (0x00dc) */
+ guchar age;
+ guchar unknown;
+ guchar gender;
+ char *personalwebpage;
+ gushort birthyear;
+ guchar birthmonth;
+ guchar birthday;
+ guchar language1;
+ guchar language2;
+ guchar language3;
+
+ /* work (0x00d2) */
+ char *workcity;
+ char *workstate;
+ char *workphone;
+ char *workfax;
+ char *workaddr;
+ char *workzip;
+ gushort workcountry;
+ char *workcompany;
+ char *workdivision;
+ char *workposition;
+ char *workwebpage;
+
+ /* additional personal information (0x00e6) */
+ char *info;
+
+ /* email (0x00eb) */
+ gushort numaddresses;
+ char **email2;
+
+ /* we keep track of these in a linked list because we're 1337 */
+ struct aim_icq_info *next;
+};
+
+
+int aim_icq_reqofflinemsgs(aim_session_t *sess);
+int aim_icq_ackofflinemsgs(aim_session_t *sess);
+int aim_icq_getallinfo(aim_session_t *sess, const char *uin);
+int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
+
+#endif /* __OSCAR_ICQ_H__ */
diff --git a/protocols/oscar/im.c b/protocols/oscar/im.c
new file mode 100644
index 00000000..99661846
--- /dev/null
+++ b/protocols/oscar/im.c
@@ -0,0 +1,2029 @@
+/*
+ * aim_im.c
+ *
+ * The routines for sending/receiving Instant Messages.
+ *
+ * Note the term ICBM (Inter-Client Basic Message) which blankets
+ * all types of genericly routed through-server messages. Within
+ * the ICBM types (family 4), a channel is defined. Each channel
+ * represents a different type of message. Channel 1 is used for
+ * what would commonly be called an "instant message". Channel 2
+ * is used for negotiating "rendezvous". These transactions end in
+ * something more complex happening, such as a chat invitation, or
+ * a file transfer.
+ *
+ * In addition to the channel, every ICBM contains a cookie. For
+ * standard IMs, these are only used for error messages. However,
+ * the more complex rendezvous messages make suitably more complex
+ * use of this field.
+ *
+ */
+
+#include <aim.h>
+#include "im.h"
+#include "info.h"
+
+/*
+ * Takes a msghdr (and a length) and returns a client type
+ * code. Note that this is *only a guess* and has a low likelihood
+ * of actually being accurate.
+ *
+ * Its based on experimental data, with the help of Eric Warmenhoven
+ * who seems to have collected a wide variety of different AIM clients.
+ *
+ *
+ * Heres the current collection:
+ * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414
+ * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464,
+ * 4.3.2229, 4.4.2286
+ * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here)
+ * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any
+ * TOC client
+ *
+ * Note that in this function, only the feature bytes are tested, since
+ * the rest will always be the same.
+ *
+ */
+guint16 aim_fingerprintclient(guint8 *msghdr, int len)
+{
+ static const struct {
+ guint16 clientid;
+ int len;
+ guint8 data[10];
+ } fingerprints[] = {
+ /* AOL Mobile Communicator, WinAIM 1.0.414 */
+ { AIM_CLIENTTYPE_MC,
+ 3, {0x01, 0x01, 0x01}},
+
+ /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
+ { AIM_CLIENTTYPE_WINAIM,
+ 3, {0x01, 0x01, 0x02}},
+
+ /* WinAIM 4.1.2010, libfaim */
+ { AIM_CLIENTTYPE_WINAIM41,
+ 4, {0x01, 0x01, 0x01, 0x02}},
+
+ /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
+ { AIM_CLIENTTYPE_AOL_TOC,
+ 1, {0x01}},
+
+ { 0, 0}
+ };
+ int i;
+
+ if (!msghdr || (len <= 0))
+ return AIM_CLIENTTYPE_UNKNOWN;
+
+ for (i = 0; fingerprints[i].len; i++) {
+ if (fingerprints[i].len != len)
+ continue;
+ if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
+ return fingerprints[i].clientid;
+ }
+
+ return AIM_CLIENTTYPE_UNKNOWN;
+}
+
+/* This should be endian-safe now... but who knows... */
+guint16 aim_iconsum(const guint8 *buf, int buflen)
+{
+ guint32 sum;
+ int i;
+
+ for (i = 0, sum = 0; i + 1 < buflen; i += 2)
+ sum += (buf[i+1] << 8) + buf[i];
+ if (i < buflen)
+ sum += buf[i];
+
+ sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
+
+ return (guint16)sum;
+}
+
+/*
+ * Send an ICBM (instant message).
+ *
+ *
+ * Possible flags:
+ * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
+ * AIM_IMFLAGS_ACK -- Requests that the server send an ack
+ * when the message is received (of type 0x0004/0x000c)
+ * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
+ * online (probably ICQ only).
+ * AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is
+ * made up of UNICODE duples. If you set
+ * this, you'd better be damn sure you know
+ * what you're doing.
+ * AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset
+ * known as ISO-8859-1.
+ *
+ * Generally, you should use the lowest encoding possible to send
+ * your message. If you only use basic punctuation and the generic
+ * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7
+ * characters, but they are all clearly defined in ISO-8859-1, then
+ * use that. Keep in mind that not all characters in the PC ASCII8
+ * character set are defined in the ISO standard. For those cases (most
+ * notably when the (r) symbol is used), you must use the full UNICODE
+ * encoding for your message. In UNICODE mode, _all_ characters must
+ * occupy 16bits, including ones that are not special. (Remember that
+ * the first 128 UNICODE symbols are equivelent to ASCII7, however they
+ * must be prefixed with a zero high order byte.)
+ *
+ * I strongly discourage the use of UNICODE mode, mainly because none
+ * of the clients I use can parse those messages (and besides that,
+ * wchars are difficult and non-portable to handle in most UNIX environments).
+ * If you really need to include special characters, use the HTML UNICODE
+ * entities. These are of the form &#2026; where 2026 is the hex
+ * representation of the UNICODE index (in this case, UNICODE
+ * "Horizontal Ellipsis", or 133 in in ASCII8).
+ *
+ * Implementation note: Since this is one of the most-used functions
+ * in all of libfaim, it is written with performance in mind. As such,
+ * it is not as clear as it could be in respect to how this message is
+ * supposed to be layed out. Most obviously, tlvlists should be used
+ * instead of writing out the bytes manually.
+ *
+ * XXX more precise verification that we never send SNACs larger than 8192
+ * XXX check SNAC size for multipart
+ *
+ */
+int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args)
+{
+ static const guint8 deffeatures[] = {
+ 0x01, 0x01, 0x01, 0x02
+ };
+ aim_conn_t *conn;
+ int i, msgtlvlen;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!args)
+ return -EINVAL;
+
+ if (args->flags & AIM_IMFLAGS_MULTIPART) {
+ if (args->mpmsg->numparts <= 0)
+ return -EINVAL;
+ } else {
+ if (!args->msg || (args->msglen <= 0))
+ return -EINVAL;
+
+ if (args->msglen >= MAXMSGLEN)
+ return -E2BIG;
+ }
+
+ /* Painfully calculate the size of the message TLV */
+ msgtlvlen = 1 + 1; /* 0501 */
+
+ if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
+ msgtlvlen += 2 + args->featureslen;
+ else
+ msgtlvlen += 2 + sizeof(deffeatures);
+
+ if (args->flags & AIM_IMFLAGS_MULTIPART) {
+ aim_mpmsg_section_t *sec;
+
+ for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+ msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+ msgtlvlen += 4 /* charset */ + sec->datalen;
+ }
+
+ } else {
+ msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+ msgtlvlen += 4 /* charset */ + args->msglen;
+ }
+
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
+ return -ENOMEM;
+
+ /* XXX should be optional */
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ /*
+ * Generate a random message cookie
+ *
+ * We could cache these like we do SNAC IDs. (In fact, it
+ * might be a good idea.) In the message error functions,
+ * the 8byte message cookie is returned as well as the
+ * SNAC ID.
+ *
+ */
+ for (i = 0; i < 8; i++)
+ aimbs_put8(&fr->data, (guint8) rand());
+
+ /*
+ * Channel ID
+ */
+ aimbs_put16(&fr->data, 0x0001);
+
+ /*
+ * Destination SN (prepended with byte length)
+ */
+ aimbs_put8(&fr->data, strlen(args->destsn));
+ aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn));
+
+ /*
+ * Message TLV (type 2).
+ */
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, msgtlvlen);
+
+ /*
+ * Features
+ *
+ */
+ aimbs_put8(&fr->data, 0x05);
+ aimbs_put8(&fr->data, 0x01);
+
+ if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
+ aimbs_put16(&fr->data, args->featureslen);
+ aimbs_putraw(&fr->data, args->features, args->featureslen);
+ } else {
+ aimbs_put16(&fr->data, sizeof(deffeatures));
+ aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
+ }
+
+ if (args->flags & AIM_IMFLAGS_MULTIPART) {
+ aim_mpmsg_section_t *sec;
+
+ for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+ aimbs_put16(&fr->data, 0x0101);
+ aimbs_put16(&fr->data, sec->datalen + 4);
+ aimbs_put16(&fr->data, sec->charset);
+ aimbs_put16(&fr->data, sec->charsubset);
+ aimbs_putraw(&fr->data, sec->data, sec->datalen);
+ }
+
+ } else {
+
+ aimbs_put16(&fr->data, 0x0101);
+
+ /*
+ * Message block length.
+ */
+ aimbs_put16(&fr->data, args->msglen + 0x04);
+
+ /*
+ * Character set.
+ */
+ if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
+
+ aimbs_put16(&fr->data, args->charset);
+ aimbs_put16(&fr->data, args->charsubset);
+
+ } else {
+ if (args->flags & AIM_IMFLAGS_UNICODE)
+ aimbs_put16(&fr->data, 0x0002);
+ else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
+ aimbs_put16(&fr->data, 0x0003);
+ else
+ aimbs_put16(&fr->data, 0x0000);
+
+ aimbs_put16(&fr->data, 0x0000);
+ }
+
+ /*
+ * Message. Not terminated.
+ */
+ aimbs_putraw(&fr->data, (guint8 *)args->msg, args->msglen);
+ }
+
+ /*
+ * Set the Request Acknowledge flag.
+ */
+ if (args->flags & AIM_IMFLAGS_ACK) {
+ aimbs_put16(&fr->data, 0x0003);
+ aimbs_put16(&fr->data, 0x0000);
+ }
+
+ /*
+ * Set the Autoresponse flag.
+ */
+ if (args->flags & AIM_IMFLAGS_AWAY) {
+ aimbs_put16(&fr->data, 0x0004);
+ aimbs_put16(&fr->data, 0x0000);
+ }
+
+ if (args->flags & AIM_IMFLAGS_OFFLINE) {
+ aimbs_put16(&fr->data, 0x0006);
+ aimbs_put16(&fr->data, 0x0000);
+ }
+
+ /*
+ * Set the I HAVE A REALLY PURTY ICON flag.
+ */
+ if (args->flags & AIM_IMFLAGS_HASICON) {
+ aimbs_put16(&fr->data, 0x0008);
+ aimbs_put16(&fr->data, 0x000c);
+ aimbs_put32(&fr->data, args->iconlen);
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, args->iconsum);
+ aimbs_put32(&fr->data, args->iconstamp);
+ }
+
+ /*
+ * Set the Buddy Icon Requested flag.
+ */
+ if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
+ aimbs_put16(&fr->data, 0x0009);
+ aimbs_put16(&fr->data, 0x0000);
+ }
+
+ aim_tx_enqueue(sess, fr);
+
+ if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM))
+ aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */
+
+ return 0;
+}
+
+/*
+ * Simple wrapper for aim_send_im_ext()
+ *
+ * You cannot use aim_send_im if you need the HASICON flag. You must
+ * use aim_send_im_ext directly for that.
+ *
+ * aim_send_im also cannot be used if you require UNICODE messages, because
+ * that requires an explicit message length. Use aim_send_im_ext().
+ *
+ */
+int aim_send_im(aim_session_t *sess, const char *destsn, guint16 flags, const char *msg)
+{
+ struct aim_sendimext_args args;
+
+ args.destsn = destsn;
+ args.flags = flags;
+ args.msg = msg;
+ args.msglen = strlen(msg);
+
+ /* Make these don't get set by accident -- they need aim_send_im_ext */
+ args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
+
+ return aim_send_im_ext(sess, &args);
+}
+
+/*
+ * This is also performance sensitive. (If you can believe it...)
+ *
+ */
+int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
+{
+ aim_conn_t *conn;
+ int i;
+ guint8 ck[8];
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++)
+ aimutil_put8(ck+i, (guint8) rand());
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ /*
+ * Cookie
+ */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /*
+ * Channel (2)
+ */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /*
+ * Dest sn
+ */
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ /*
+ * TLV t(0005)
+ *
+ * Encompasses everything below.
+ */
+ aimbs_put16(&fr->data, 0x0005);
+ aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
+
+ aimbs_put16(&fr->data, 0x0000);
+ aimbs_putraw(&fr->data, ck, 8);
+ aim_putcap(&fr->data, AIM_CAPS_BUDDYICON);
+
+ /* TLV t(000a) */
+ aimbs_put16(&fr->data, 0x000a);
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, 0x0001);
+
+ /* TLV t(000f) */
+ aimbs_put16(&fr->data, 0x000f);
+ aimbs_put16(&fr->data, 0x0000);
+
+ /* TLV t(2711) */
+ aimbs_put16(&fr->data, 0x2711);
+ aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
+ aimbs_put16(&fr->data, 0x0000);
+ aimbs_put16(&fr->data, iconsum);
+ aimbs_put32(&fr->data, iconlen);
+ aimbs_put32(&fr->data, stamp);
+ aimbs_putraw(&fr->data, icon, iconlen);
+ aimbs_putraw(&fr->data, (guint8 *)AIM_ICONIDENT, strlen(AIM_ICONIDENT));
+
+ /* TLV t(0003) */
+ aimbs_put16(&fr->data, 0x0003);
+ aimbs_put16(&fr->data, 0x0000);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * This only works for ICQ 2001b (thats 2001 not 2000). Better, only
+ * send it to clients advertising the RTF capability. In fact, if you send
+ * it to a client that doesn't support that capability, the server will gladly
+ * bounce it back to you.
+ *
+ * You'd think this would be in icq.c, but, well, I'm trying to stick with
+ * the one-group-per-file scheme as much as possible. This could easily
+ * be an exception, since Rendezvous IMs are external of the Oscar core,
+ * and therefore are undefined. Really I just need to think of a good way to
+ * make an interface similar to what AOL actually uses. But I'm not using COM.
+ *
+ */
+int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
+{
+ const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+ aim_conn_t *conn;
+ int i;
+ guint8 ck[8];
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int servdatalen;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!args || !args->destsn || !args->rtfmsg)
+ return -EINVAL;
+
+ servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1;
+
+ for (i = 0; i < 8; i++)
+ aimutil_put8(ck+i, (guint8) rand());
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ /*
+ * Cookie
+ */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /*
+ * Channel (2)
+ */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /*
+ * Dest sn
+ */
+ aimbs_put8(&fr->data, strlen(args->destsn));
+ aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn));
+
+ /*
+ * TLV t(0005)
+ *
+ * Encompasses everything below.
+ */
+ aimbs_put16(&fr->data, 0x0005);
+ aimbs_put16(&fr->data, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen);
+
+ aimbs_put16(&fr->data, 0x0000);
+ aimbs_putraw(&fr->data, ck, 8);
+ aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+ /*
+ * t(000a) l(0002) v(0001)
+ */
+ aimbs_put16(&fr->data, 0x000a);
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, 0x0001);
+
+ /*
+ * t(000f) l(0000) v()
+ */
+ aimbs_put16(&fr->data, 0x000f);
+ aimbs_put16(&fr->data, 0x0000);
+
+ /*
+ * Service Data TLV
+ */
+ aimbs_put16(&fr->data, 0x2711);
+ aimbs_put16(&fr->data, servdatalen);
+
+ aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */);
+ aimbs_putle16(&fr->data, 9);
+ aim_putcap(&fr->data, AIM_CAPS_EMPTY);
+ aimbs_putle16(&fr->data, 0);
+ aimbs_putle32(&fr->data, 0);
+ aimbs_putle8(&fr->data, 0);
+ aimbs_putle16(&fr->data, 0x03ea); /* trid1 */
+
+ aimbs_putle16(&fr->data, 14);
+ aimbs_putle16(&fr->data, 0x03eb); /* trid2 */
+ aimbs_putle32(&fr->data, 0);
+ aimbs_putle32(&fr->data, 0);
+ aimbs_putle32(&fr->data, 0);
+
+ aimbs_putle16(&fr->data, 0x0001);
+ aimbs_putle32(&fr->data, 0);
+ aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1);
+ aimbs_putraw(&fr->data, (guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1);
+
+ aimbs_putle32(&fr->data, args->fgcolor);
+ aimbs_putle32(&fr->data, args->bgcolor);
+ aimbs_putle32(&fr->data, strlen(rtfcap)+1);
+ aimbs_putraw(&fr->data, (guint8 *)rtfcap, strlen(rtfcap)+1);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret)
+{
+ aim_conn_t *conn;
+ guint8 ck[8];
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL, *itl = NULL;
+ int hdrlen, i;
+ guint8 *hdr;
+ aim_bstream_t hdrbs;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ /*
+ * Generate a random message cookie
+ *
+ * This cookie needs to be alphanumeric and NULL-terminated to be
+ * TOC-compatible.
+ *
+ * XXX have I mentioned these should be generated in msgcookie.c?
+ *
+ */
+ for (i = 0; i < 7; i++)
+ ck[i] = 0x30 + ((guint8) rand() % 10);
+ ck[7] = '\0';
+
+ if (ckret)
+ memcpy(ckret, ck, 8);
+
+ /* Cookie */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /* Channel */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /* Destination SN */
+ aimbs_put8(&fr->data, strlen(destsn));
+ aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn));
+
+ aim_addtlvtochain_noval(&tl, 0x0003);
+
+ hdrlen = 2+8+16+6+8+6+4;
+ hdr = g_malloc(hdrlen);
+ aim_bstream_init(&hdrbs, hdr, hdrlen);
+
+ aimbs_put16(&hdrbs, 0x0000);
+ aimbs_putraw(&hdrbs, ck, 8);
+ aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE);
+
+ aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+ aim_addtlvtochain_raw(&itl, 0x0003, 4, ip);
+ aim_addtlvtochain16(&itl, 0x0005, port);
+ aim_addtlvtochain_noval(&itl, 0x000f);
+
+ aim_writetlvchain(&hdrbs, &itl);
+
+ aim_addtlvtochain_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+ aim_writetlvchain(&fr->data, &tl);
+
+ g_free(hdr);
+ aim_freetlvchain(&itl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret)
+{
+ aim_conn_t *conn;
+ int i;
+ guint8 ck[8];
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!sn || !filename)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ for (i = 0; i < 7; i++)
+ aimutil_put8(ck+i, 0x30 + ((guint8) rand() % 10));
+ ck[7] = '\0';
+
+ if (ckret)
+ memcpy(ckret, ck, 8);
+
+ /*
+ * Cookie
+ */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /*
+ * Channel (2)
+ */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /*
+ * Dest sn
+ */
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ /*
+ * TLV t(0005)
+ *
+ * Encompasses everything below. Gee.
+ */
+ aimbs_put16(&fr->data, 0x0005);
+ aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4);
+
+ aimbs_put16(&fr->data, 0x0000);
+ aimbs_putraw(&fr->data, ck, 8);
+ aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
+
+ /* TLV t(000a) */
+ aimbs_put16(&fr->data, 0x000a);
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, 0x0001);
+
+ /* TLV t(0003) (IP) */
+ aimbs_put16(&fr->data, 0x0003);
+ aimbs_put16(&fr->data, 0x0004);
+ aimbs_putraw(&fr->data, ip, 4);
+
+ /* TLV t(0005) (port) */
+ aimbs_put16(&fr->data, 0x0005);
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, port);
+
+ /* TLV t(000f) */
+ aimbs_put16(&fr->data, 0x000f);
+ aimbs_put16(&fr->data, 0x0000);
+
+ /* TLV t(2711) */
+ aimbs_put16(&fr->data, 0x2711);
+ aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4);
+
+ /* ? */
+ aimbs_put16(&fr->data, 0x0001);
+ aimbs_put16(&fr->data, numfiles);
+ aimbs_put32(&fr->data, totsize);
+ aimbs_putraw(&fr->data, (guint8 *)filename, strlen(filename));
+
+ /* ? */
+ aimbs_put32(&fr->data, 0x00000000);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/**
+ * Request the status message of the given ICQ user.
+ *
+ * @param sess The oscar session.
+ * @param sn The UIN of the user of whom you wish to request info.
+ * @param type The type of info you wish to request. This should be the current
+ * state of the user, as one of the AIM_ICQ_STATE_* defines.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type)
+{
+ aim_conn_t *conn;
+ int i;
+ guint8 ck[8];
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++)
+ aimutil_put8(ck+i, (guint8) rand());
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+ /* Cookie */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /* Channel (2) */
+ aimbs_put16(&fr->data, 0x0002);
+
+ /* Dest sn */
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ /* TLV t(0005) - Encompasses almost everything below. */
+ aimbs_put16(&fr->data, 0x0005); /* T */
+ aimbs_put16(&fr->data, 0x005e); /* L */
+ { /* V */
+ aimbs_put16(&fr->data, 0x0000);
+
+ /* Cookie */
+ aimbs_putraw(&fr->data, ck, 8);
+
+ /* Put the 16 byte server relay capability */
+ aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+ /* TLV t(000a) */
+ aimbs_put16(&fr->data, 0x000a);
+ aimbs_put16(&fr->data, 0x0002);
+ aimbs_put16(&fr->data, 0x0001);
+
+ /* TLV t(000f) */
+ aimbs_put16(&fr->data, 0x000f);
+ aimbs_put16(&fr->data, 0x0000);
+
+ /* TLV t(2711) */
+ aimbs_put16(&fr->data, 0x2711);
+ aimbs_put16(&fr->data, 0x0036);
+ { /* V */
+ aimbs_putle16(&fr->data, 0x001b); /* L */
+ aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+ aimbs_putle16(&fr->data, 0x0003); /* Client features? */
+ aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+ aimbs_putle8(&fr->data, 0x00); /* Unkizown */
+ aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */
+
+ aimbs_putle16(&fr->data, 0x000e); /* L */
+ aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+
+ /* The type of status message being requested */
+ if (type & AIM_ICQ_STATE_CHAT)
+ aimbs_putle16(&fr->data, 0x03ec);
+ else if(type & AIM_ICQ_STATE_DND)
+ aimbs_putle16(&fr->data, 0x03eb);
+ else if(type & AIM_ICQ_STATE_OUT)
+ aimbs_putle16(&fr->data, 0x03ea);
+ else if(type & AIM_ICQ_STATE_BUSY)
+ aimbs_putle16(&fr->data, 0x03e9);
+ else if(type & AIM_ICQ_STATE_AWAY)
+ aimbs_putle16(&fr->data, 0x03e8);
+
+ aimbs_putle16(&fr->data, 0x0000); /* Status? */
+ aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
+ aimbs_putle16(&fr->data, 0x0001); /* L? */
+ aimbs_putle8(&fr->data, 0x00); /* Null termination? */
+ } /* End TLV t(2711) */
+ } /* End TLV t(0005) */
+
+ /* TLV t(0003) */
+ aimbs_put16(&fr->data, 0x0003);
+ aimbs_put16(&fr->data, 0x0000);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/**
+ * answers status message requests
+ * @param sess the oscar session
+ * @param sender the guy whos asking
+ * @param cookie message id which we are answering for
+ * @param message away message
+ * @param state our current away state the way icq requests it (0xE8 for away, 0xE9 occupied, ...)
+ * @return 0 if no error
+ */
+int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie,
+ const char *message, const guint8 state, const guint16 dc)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02,
+ 10+8+2+1+strlen(sender)+2+0x1d+0x10+9+strlen(message)+1)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+
+ aimbs_putraw(&fr->data, cookie, 8);
+
+ aimbs_put16(&fr->data, 0x0002); /* channel */
+ aimbs_put8(&fr->data, strlen(sender));
+ aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender));
+
+ aimbs_put16(&fr->data, 0x0003); /* reason: channel specific */
+
+ aimbs_putle16(&fr->data, 0x001b); /* length of data SEQ1 */
+ aimbs_putle16(&fr->data, 0x0008); /* protocol version */
+
+ aimbs_putle32(&fr->data, 0x0000); /* no plugin -> 16 times 0x00 */
+ aimbs_putle32(&fr->data, 0x0000);
+ aimbs_putle32(&fr->data, 0x0000);
+ aimbs_putle32(&fr->data, 0x0000);
+
+ aimbs_putle16(&fr->data, 0x0000); /* unknown */
+ aimbs_putle32(&fr->data, 0x0003); /* client features */
+ aimbs_putle8(&fr->data, 0x00); /* unknown */
+ aimbs_putle16(&fr->data, dc); /* Sequence number? XXX - This should decrement by 1 with each request */
+ /* end of SEQ1 */
+
+ aimbs_putle16(&fr->data, 0x000e); /* Length of SEQ2 */
+ aimbs_putle16(&fr->data, dc); /* Sequence number? same as above
+ * XXX - This should decrement by 1 with each request */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+ /* end of SEQ2 */
+
+ /* now for the real fun */
+ aimbs_putle8(&fr->data, state); /* away state */
+ aimbs_putle8(&fr->data, 0x03); /* msg-flag: 03 for states */
+ aimbs_putle16(&fr->data, 0x0000); /* status code ? */
+ aimbs_putle16(&fr->data, 0x0000); /* priority code */
+ aimbs_putle16(&fr->data, strlen(message) + 1); /* message length + termination */
+ aimbs_putraw(&fr->data, (guint8 *) message, strlen(message) + 1); /* null terminated string */
+
+ aim_tx_enqueue(sess, fr);
+
+
+ return 0;
+}
+
+
+static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int i, ret = 0;
+ aim_rxcallback_t userfunc;
+ guint8 cookie[8];
+ guint16 channel;
+ aim_tlvlist_t *tlvlist;
+ char *sn;
+ int snlen;
+ guint16 icbmflags = 0;
+ guint8 flag1 = 0, flag2 = 0;
+ char *msg = NULL;
+ aim_tlv_t *msgblock;
+
+ /* ICBM Cookie. */
+ for (i = 0; i < 8; i++)
+ cookie[i] = aimbs_get8(bs);
+
+ /* Channel ID */
+ channel = aimbs_get16(bs);
+
+ if (channel != 0x01) {
+ do_error_dialog(sess->aux_data, "icbm: ICBM recieved on unsupported channel. Ignoring.", "Gaim");
+ return 0;
+ }
+
+ snlen = aimbs_get8(bs);
+ sn = aimbs_getstr(bs, snlen);
+
+ tlvlist = aim_readtlvchain(bs);
+
+ if (aim_gettlv(tlvlist, 0x0003, 1))
+ icbmflags |= AIM_IMFLAGS_ACK;
+ if (aim_gettlv(tlvlist, 0x0004, 1))
+ icbmflags |= AIM_IMFLAGS_AWAY;
+
+ if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) {
+ aim_bstream_t mbs;
+ int featurelen, msglen;
+
+ aim_bstream_init(&mbs, msgblock->value, msgblock->length);
+
+ aimbs_get8(&mbs);
+ aimbs_get8(&mbs);
+ for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
+ aimbs_get8(&mbs);
+ aimbs_get8(&mbs);
+ aimbs_get8(&mbs);
+
+ msglen = aimbs_get16(&mbs) - 4; /* final block length */
+
+ flag1 = aimbs_get16(&mbs);
+ flag2 = aimbs_get16(&mbs);
+
+ msg = aimbs_getstr(&mbs, msglen);
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2);
+
+ g_free(sn);
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Ahh, the joys of nearly ridiculous over-engineering.
+ *
+ * Not only do AIM ICBM's support multiple channels. Not only do they
+ * support multiple character sets. But they support multiple character
+ * sets / encodings within the same ICBM.
+ *
+ * These multipart messages allow for complex space savings techniques, which
+ * seem utterly unnecessary by today's standards. In fact, there is only
+ * one client still in popular use that still uses this method: AOL for the
+ * Macintosh, Version 5.0. Obscure, yes, I know.
+ *
+ * In modern (non-"legacy") clients, if the user tries to send a character
+ * that is not ISO-8859-1 or ASCII, the client will send the entire message
+ * as UNICODE, meaning that every character in the message will occupy the
+ * full 16 bit UNICODE field, even if the high order byte would be zero.
+ * Multipart messages prevent this wasted space by allowing the client to
+ * only send the characters in UNICODE that need to be sent that way, and
+ * the rest of the message can be sent in whatever the native character
+ * set is (probably ASCII).
+ *
+ * An important note is that sections will be displayed in the order that
+ * they appear in the ICBM. There is no facility for merging or rearranging
+ * sections at run time. So if you have, say, ASCII then UNICODE then ASCII,
+ * you must supply two ASCII sections with a UNICODE in the middle, and incur
+ * the associated overhead.
+ *
+ * Normally I would have laughed and given a firm 'no' to supporting this
+ * seldom-used feature, but something is attracting me to it. In the future,
+ * it may be possible to abuse this to send mixed-media messages to other
+ * open source clients (like encryption or something) -- see faimtest for
+ * examples of how to do this.
+ *
+ * I would definitly recommend avoiding this feature unless you really
+ * know what you are doing, and/or you have something neat to do with it.
+ *
+ */
+int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+
+ memset(mpm, 0, sizeof(aim_mpmsg_t));
+
+ return 0;
+}
+
+static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, guint8 *data, guint16 datalen)
+{
+ aim_mpmsg_section_t *sec;
+
+ if (!(sec = g_new0(aim_mpmsg_section_t,1)))
+ return -1;
+
+ sec->charset = charset;
+ sec->charsubset = charsubset;
+ sec->data = data;
+ sec->datalen = datalen;
+ sec->next = NULL;
+
+ if (!mpm->parts)
+ mpm->parts = sec;
+ else {
+ aim_mpmsg_section_t *cur;
+
+ for (cur = mpm->parts; cur->next; cur = cur->next)
+ ;
+ cur->next = sec;
+ }
+
+ mpm->numparts++;
+
+ return 0;
+}
+
+int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen)
+{
+ guint8 *dup;
+
+ if (!(dup = g_malloc(datalen)))
+ return -1;
+ memcpy(dup, data, datalen);
+
+ if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
+ g_free(dup);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* XXX should provide a way of saying ISO-8859-1 specifically */
+int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
+{
+ char *dup;
+
+ if (!(dup = g_strdup(ascii)))
+ return -1;
+
+ if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, (guint8 *)dup, (guint16) strlen(ascii)) == -1) {
+ g_free(dup);
+ return -1;
+ }
+
+ return 0;
+}
+
+int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen)
+{
+ guint8 *buf;
+ aim_bstream_t bs;
+ int i;
+
+ if (!(buf = g_malloc(unicodelen * 2)))
+ return -1;
+
+ aim_bstream_init(&bs, buf, unicodelen * 2);
+
+ /* We assume unicode is in /host/ byte order -- convert to network */
+ for (i = 0; i < unicodelen; i++)
+ aimbs_put16(&bs, unicode[i]);
+
+ if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
+ g_free(buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+ aim_mpmsg_section_t *cur;
+
+ for (cur = mpm->parts; cur; ) {
+ aim_mpmsg_section_t *tmp;
+
+ tmp = cur->next;
+ g_free(cur->data);
+ g_free(cur);
+ cur = tmp;
+ }
+
+ mpm->numparts = 0;
+ mpm->parts = NULL;
+
+ return;
+}
+
+/*
+ * Start by building the multipart structures, then pick the first
+ * human-readable section and stuff it into args->msg so no one gets
+ * suspicious.
+ *
+ */
+static int incomingim_ch1_parsemsgs(aim_session_t *sess, guint8 *data, int len, struct aim_incomingim_ch1_args *args)
+{
+ static const guint16 charsetpri[] = {
+ 0x0000, /* ASCII first */
+ 0x0003, /* then ISO-8859-1 */
+ 0x0002, /* UNICODE as last resort */
+ };
+ static const int charsetpricount = 3;
+ int i;
+ aim_bstream_t mbs;
+ aim_mpmsg_section_t *sec;
+
+ aim_bstream_init(&mbs, data, len);
+
+ while (aim_bstream_empty(&mbs)) {
+ guint16 msglen, flag1, flag2;
+ char *msgbuf;
+
+ aimbs_get8(&mbs); /* 01 */
+ aimbs_get8(&mbs); /* 01 */
+
+ /* Message string length, including character set info. */
+ msglen = aimbs_get16(&mbs);
+
+ /* Character set info */
+ flag1 = aimbs_get16(&mbs);
+ flag2 = aimbs_get16(&mbs);
+
+ /* Message. */
+ msglen -= 4;
+
+ /*
+ * For now, we don't care what the encoding is. Just copy
+ * it into a multipart struct and deal with it later. However,
+ * always pad the ending with a NULL. This makes it easier
+ * to treat ASCII sections as strings. It won't matter for
+ * UNICODE or binary data, as you should never read past
+ * the specified data length, which will not include the pad.
+ *
+ * XXX There's an API bug here. For sending, the UNICODE is
+ * given in host byte order (aim_mpmsg_addunicode), but here
+ * the received messages are given in network byte order.
+ *
+ */
+ msgbuf = aimbs_getstr(&mbs, msglen);
+ mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, (guint8 *)msgbuf, (guint16) msglen);
+
+ } /* while */
+
+ args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
+
+ /*
+ * Clients that support multiparts should never use args->msg, as it
+ * will point to an arbitrary section.
+ *
+ * Here, we attempt to provide clients that do not support multipart
+ * messages with something to look at -- hopefully a human-readable
+ * string. But, failing that, a UNICODE message, or nothing at all.
+ *
+ * Which means that even if args->msg is NULL, it does not mean the
+ * message was blank.
+ *
+ */
+ for (i = 0; i < charsetpricount; i++) {
+ for (sec = args->mpmsg.parts; sec; sec = sec->next) {
+
+ if (sec->charset != charsetpri[i])
+ continue;
+
+ /* Great. We found one. Fill it in. */
+ args->charset = sec->charset;
+ args->charsubset = sec->charsubset;
+ args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET;
+
+ /* Set up the simple flags */
+ if (args->charset == 0x0000)
+ ; /* ASCII */
+ else if (args->charset == 0x0002)
+ args->icbmflags |= AIM_IMFLAGS_UNICODE;
+ else if (args->charset == 0x0003)
+ args->icbmflags |= AIM_IMFLAGS_ISO_8859_1;
+ else if (args->charset == 0xffff)
+ ; /* no encoding (yeep!) */
+
+ if (args->charsubset == 0x0000)
+ ; /* standard subencoding? */
+ else if (args->charsubset == 0x000b)
+ args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
+ else if (args->charsubset == 0xffff)
+ ; /* no subencoding */
+#if 0
+ /* XXX this isn't really necesary... */
+ if ( ((args.flag1 != 0x0000) &&
+ (args.flag1 != 0x0002) &&
+ (args.flag1 != 0x0003) &&
+ (args.flag1 != 0xffff)) ||
+ ((args.flag2 != 0x0000) &&
+ (args.flag2 != 0x000b) &&
+ (args.flag2 != 0xffff))) {
+ faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
+ }
+#endif
+
+ args->msg = (char *)sec->data;
+ args->msglen = sec->datalen;
+
+ return 0;
+ }
+ }
+
+ /* No human-readable sections found. Oh well. */
+ args->charset = args->charsubset = 0xffff;
+ args->msg = NULL;
+ args->msglen = 0;
+
+ return 0;
+}
+
+static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie)
+{
+ guint16 type, length;
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ struct aim_incomingim_ch1_args args;
+ int endpos;
+
+ memset(&args, 0, sizeof(args));
+
+ aim_mpmsg_init(sess, &args.mpmsg);
+
+ /*
+ * This used to be done using tlvchains. For performance reasons,
+ * I've changed it to process the TLVs in-place. This avoids lots
+ * of per-IM memory allocations.
+ */
+ while (aim_bstream_empty(bs)) {
+
+ type = aimbs_get16(bs);
+ length = aimbs_get16(bs);
+
+ endpos = aim_bstream_curpos(bs) + length;
+
+ if (type == 0x0002) { /* Message Block */
+
+ /*
+ * This TLV consists of the following:
+ * - 0501 -- Unknown
+ * - Features: Don't know how to interpret these
+ * - 0101 -- Unknown
+ * - Message
+ *
+ */
+
+ aimbs_get8(bs); /* 05 */
+ aimbs_get8(bs); /* 01 */
+
+ args.featureslen = aimbs_get16(bs);
+ /* XXX XXX this is all evil! */
+ args.features = bs->data + bs->offset;
+ aim_bstream_advance(bs, args.featureslen);
+ args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
+
+ /*
+ * The rest of the TLV contains one or more message
+ * blocks...
+ */
+ incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
+
+ } else if (type == 0x0003) { /* Server Ack Requested */
+
+ args.icbmflags |= AIM_IMFLAGS_ACK;
+
+ } else if (type == 0x0004) { /* Message is Auto Response */
+
+ args.icbmflags |= AIM_IMFLAGS_AWAY;
+
+ } else if (type == 0x0006) { /* Message was received offline. */
+
+ /* XXX not sure if this actually gets sent. */
+ args.icbmflags |= AIM_IMFLAGS_OFFLINE;
+
+ } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
+
+ args.iconlen = aimbs_get32(bs);
+ aimbs_get16(bs); /* 0x0001 */
+ args.iconsum = aimbs_get16(bs);
+ args.iconstamp = aimbs_get32(bs);
+
+ /*
+ * This looks to be a client bug. MacAIM 4.3 will
+ * send this tag, but with all zero values, in the
+ * first message of a conversation. This makes no
+ * sense whatsoever, so I'm going to say its a bug.
+ *
+ * You really shouldn't advertise a zero-length icon
+ * anyway.
+ *
+ */
+ if (args.iconlen)
+ args.icbmflags |= AIM_IMFLAGS_HASICON;
+
+ } else if (type == 0x0009) {
+
+ args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
+
+ } else if (type == 0x0017) {
+
+ args.extdatalen = length;
+ args.extdata = aimbs_getraw(bs, args.extdatalen);
+
+ } else {
+ // do_error_dialog(sess->aux_data, "Unknown TLV encountered", "Gaim");
+ }
+
+ /*
+ * This is here to protect ourselves from ourselves. That
+ * is, if something above doesn't completly parse its value
+ * section, or, worse, overparses it, this will set the
+ * stream where it needs to be in order to land on the next
+ * TLV when the loop continues.
+ *
+ */
+ aim_bstream_setpos(bs, endpos);
+ }
+
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, userinfo, &args);
+
+ aim_mpmsg_free(sess, &args.mpmsg);
+ g_free(args.extdata);
+
+ return ret;
+}
+
+static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+
+ g_free((char *)args->info.rtfmsg.rtfmsg);
+
+ return;
+}
+
+/*
+ * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is
+ * kind of odd. This sends the client ICQRTF since that is all that I've seen
+ * SERVERRELAY used for.
+ *
+ * Note that this is all little-endian. Cringe.
+ *
+ * This cap is used for auto status message replies, too [ft]
+ *
+ */
+static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+ guint16 hdrlen, msglen, dc;
+ guint8 msgtype, msgflags;
+ guint8 *plugin;
+ int i = 0, tmp = 0;
+ struct gaim_connection *gc = sess->aux_data;
+
+ /* at the moment we just can deal with requests, not with cancel or accept */
+ if (args->status != 0) return;
+
+ hdrlen = aimbs_getle16(servdata);
+
+ aim_bstream_advance(servdata, 0x02); /* protocol version */
+ plugin = aimbs_getraw(servdata, 0x10); /* following data is a message or
+ something plugin specific */
+ /* as there is no plugin handling, just skip the rest */
+ aim_bstream_advance(servdata, hdrlen - 0x12);
+
+ hdrlen = aimbs_getle16(servdata);
+ dc = aimbs_getle16(servdata); /* save the sequence number */
+ aim_bstream_advance(servdata, hdrlen - 0x02);
+
+ /* TODO is it a message or something for a plugin? */
+ for (i = 0; i < 0x10; i++) {
+ tmp |= plugin[i];
+ }
+
+ if (!tmp) { /* message follows */
+
+ msgtype = aimbs_getle8(servdata);
+ msgflags = aimbs_getle8(servdata);
+
+ aim_bstream_advance(servdata, 0x04); /* status code and priority code */
+
+ msglen = aimbs_getle16(servdata); /* message string length */
+ args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen);
+
+ switch(msgtype) {
+ case AIM_MTYPE_PLAIN:
+
+ args->info.rtfmsg.fgcolor = aimbs_getle32(servdata);
+ args->info.rtfmsg.bgcolor = aimbs_getle32(servdata);
+
+ hdrlen = aimbs_getle32(servdata);
+ aim_bstream_advance(servdata, hdrlen);
+
+ /* XXX This is such a hack. */
+ args->reqclass = AIM_CAPS_ICQRTF;
+ break;
+
+ case AIM_MTYPE_AUTOAWAY:
+ case AIM_MTYPE_AUTOBUSY:
+ case AIM_MTYPE_AUTONA:
+ case AIM_MTYPE_AUTODND:
+ case AIM_MTYPE_AUTOFFC:
+ case 0x9c: /* ICQ 5 seems to send this */
+ aim_send_im_ch2_statusmessage(sess, userinfo->sn, args->cookie,
+ gc->away, sess->aim_icq_state, dc);
+ break;
+
+ }
+ } /* message or plugin specific */
+
+ g_free(plugin);
+ args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
+
+ return;
+}
+
+typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args);
+
+static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+ aim_rxcallback_t userfunc;
+ aim_tlv_t *block1, *servdatatlv;
+ aim_tlvlist_t *list2;
+ struct aim_incomingim_ch2_args args;
+ aim_bstream_t bbs, sdbs, *sdbsptr = NULL;
+ guint8 *cookie2;
+ int ret = 0;
+
+ char clientip1[30] = {""};
+ char clientip2[30] = {""};
+ char verifiedip[30] = {""};
+
+ memset(&args, 0, sizeof(args));
+
+ /*
+ * There's another block of TLVs embedded in the type 5 here.
+ */
+ block1 = aim_gettlv(tlvlist, 0x0005, 1);
+ aim_bstream_init(&bbs, block1->value, block1->length);
+
+ /*
+ * First two bytes represent the status of the connection.
+ *
+ * 0 is a request, 1 is a deny (?), 2 is an accept
+ */
+ args.status = aimbs_get16(&bbs);
+
+ /*
+ * Next comes the cookie. Should match the ICBM cookie.
+ */
+ cookie2 = aimbs_getraw(&bbs, 8);
+ if (memcmp(cookie, cookie2, 8) != 0)
+ do_error_dialog(sess->aux_data, "rend: warning cookies don't match!", "Gaim");
+ memcpy(args.cookie, cookie2, 8);
+ g_free(cookie2);
+
+ /*
+ * The next 16bytes are a capability block so we can
+ * identify what type of rendezvous this is.
+ */
+ args.reqclass = aim_getcap(sess, &bbs, 0x10);
+
+ /*
+ * What follows may be TLVs or nothing, depending on the
+ * purpose of the message.
+ *
+ * Ack packets for instance have nothing more to them.
+ */
+ list2 = aim_readtlvchain(&bbs);
+
+ /*
+ * IP address from the perspective of the client.
+ */
+ if (aim_gettlv(list2, 0x0002, 1)) {
+ aim_tlv_t *iptlv;
+
+ iptlv = aim_gettlv(list2, 0x0002, 1);
+
+ g_snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d",
+ aimutil_get8(iptlv->value+0),
+ aimutil_get8(iptlv->value+1),
+ aimutil_get8(iptlv->value+2),
+ aimutil_get8(iptlv->value+3));
+ }
+
+ /*
+ * Secondary IP address from the perspective of the client.
+ */
+ if (aim_gettlv(list2, 0x0003, 1)) {
+ aim_tlv_t *iptlv;
+
+ iptlv = aim_gettlv(list2, 0x0003, 1);
+
+ g_snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d",
+ aimutil_get8(iptlv->value+0),
+ aimutil_get8(iptlv->value+1),
+ aimutil_get8(iptlv->value+2),
+ aimutil_get8(iptlv->value+3));
+ }
+
+ /*
+ * Verified IP address (from the perspective of Oscar).
+ *
+ * This is added by the server.
+ */
+ if (aim_gettlv(list2, 0x0004, 1)) {
+ aim_tlv_t *iptlv;
+
+ iptlv = aim_gettlv(list2, 0x0004, 1);
+
+ g_snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d",
+ aimutil_get8(iptlv->value+0),
+ aimutil_get8(iptlv->value+1),
+ aimutil_get8(iptlv->value+2),
+ aimutil_get8(iptlv->value+3));
+ }
+
+ /*
+ * Port number for something.
+ */
+ if (aim_gettlv(list2, 0x0005, 1))
+ args.port = aim_gettlv16(list2, 0x0005, 1);
+
+ /*
+ * Error code.
+ */
+ if (aim_gettlv(list2, 0x000b, 1))
+ args.errorcode = aim_gettlv16(list2, 0x000b, 1);
+
+ /*
+ * Invitation message / chat description.
+ */
+ if (aim_gettlv(list2, 0x000c, 1))
+ args.msg = aim_gettlv_str(list2, 0x000c, 1);
+
+ /*
+ * Character set.
+ */
+ if (aim_gettlv(list2, 0x000d, 1))
+ args.encoding = aim_gettlv_str(list2, 0x000d, 1);
+
+ /*
+ * Language.
+ */
+ if (aim_gettlv(list2, 0x000e, 1))
+ args.language = aim_gettlv_str(list2, 0x000e, 1);
+
+ /* Unknown -- two bytes = 0x0001 */
+ if (aim_gettlv(list2, 0x000a, 1))
+ ;
+
+ /* Unknown -- no value */
+ if (aim_gettlv(list2, 0x000f, 1))
+ ;
+
+ if (strlen(clientip1))
+ args.clientip = (char *)clientip1;
+ if (strlen(clientip2))
+ args.clientip2 = (char *)clientip2;
+ if (strlen(verifiedip))
+ args.verifiedip = (char *)verifiedip;
+
+ /*
+ * This is must be present in PROPOSALs, but will probably not
+ * exist in CANCELs and ACCEPTs.
+ *
+ * Service Data blocks are module-specific in format.
+ */
+ if ((servdatatlv = aim_gettlv(list2, 0x2711 /* 10001 */, 1))) {
+
+ aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length);
+ sdbsptr = &sdbs;
+ }
+
+ if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)
+ incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, userinfo, &args);
+
+
+ if (args.destructor)
+ ((ch2_args_destructor_t)args.destructor)(sess, &args);
+
+ g_free((char *)args.msg);
+ g_free((char *)args.encoding);
+ g_free((char *)args.language);
+
+ aim_freetlvchain(&list2);
+
+ return ret;
+}
+
+static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+ aim_bstream_t meat;
+ aim_rxcallback_t userfunc;
+ aim_tlv_t *block;
+ struct aim_incomingim_ch4_args args;
+ int ret = 0;
+
+ /*
+ * Make a bstream for the meaty part. Yum. Meat.
+ */
+ if (!(block = aim_gettlv(tlvlist, 0x0005, 1)))
+ return -1;
+ aim_bstream_init(&meat, block->value, block->length);
+
+ args.uin = aimbs_getle32(&meat);
+ args.type = aimbs_getle16(&meat);
+ args.msg = (char *)aimbs_getraw(&meat, aimbs_getle16(&meat));
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, userinfo, &args);
+
+ g_free(args.msg);
+
+ return ret;
+}
+
+/*
+ * It can easily be said that parsing ICBMs is THE single
+ * most difficult thing to do in the in AIM protocol. In
+ * fact, I think I just did say that.
+ *
+ * Below is the best damned solution I've come up with
+ * over the past sixteen months of battling with it. This
+ * can parse both away and normal messages from every client
+ * I have access to. Its not fast, its not clean. But it works.
+ *
+ */
+static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int i, ret = 0;
+ guint8 cookie[8];
+ guint16 channel;
+ aim_userinfo_t userinfo;
+
+ memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
+
+ /*
+ * Read ICBM Cookie. And throw away.
+ */
+ for (i = 0; i < 8; i++)
+ cookie[i] = aimbs_get8(bs);
+
+ /*
+ * Channel ID.
+ *
+ * Channel 0x0001 is the message channel. There are
+ * other channels for things called "rendevous"
+ * which represent chat and some of the other new
+ * features of AIM2/3/3.5.
+ *
+ * Channel 0x0002 is the Rendevous channel, which
+ * is where Chat Invitiations and various client-client
+ * connection negotiations come from.
+ *
+ * Channel 0x0004 is used for ICQ authorization, or
+ * possibly any system notice.
+ *
+ */
+ channel = aimbs_get16(bs);
+
+ /*
+ * Extract the standard user info block.
+ *
+ * Note that although this contains TLVs that appear contiguous
+ * with the TLVs read below, they are two different pieces. The
+ * userinfo block contains the number of TLVs that contain user
+ * information, the rest are not even though there is no seperation.
+ * aim_extractuserinfo() returns the number of bytes used by the
+ * userinfo tlvs, so you can start reading the rest of them right
+ * afterward.
+ *
+ * That also means that TLV types can be duplicated between the
+ * userinfo block and the rest of the message, however there should
+ * never be two TLVs of the same type in one block.
+ *
+ */
+ aim_extractuserinfo(sess, bs, &userinfo);
+
+ /*
+ * From here on, its depends on what channel we're on.
+ *
+ * Technically all channels have a TLV list have this, however,
+ * for the common channel 1 case, in-place parsing is used for
+ * performance reasons (less memory allocation).
+ */
+ if (channel == 1) {
+
+ ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
+
+ } else if (channel == 2) {
+ aim_tlvlist_t *tlvlist;
+
+ /*
+ * Read block of TLVs (not including the userinfo data). All
+ * further data is derived from what is parsed here.
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+
+ aim_freetlvchain(&tlvlist);
+
+ } else if (channel == 4) {
+ aim_tlvlist_t *tlvlist;
+
+ tlvlist = aim_readtlvchain(bs);
+ ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+ aim_freetlvchain(&tlvlist);
+
+ } else {
+
+ do_error_dialog(sess->aux_data, "ICBM received on an unsupported channel. Ignoring.", "Gaim");
+
+ return 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Possible codes:
+ * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
+ * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
+ * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
+ *
+ */
+int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, guint16 code)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+
+ aimbs_putraw(&fr->data, cookie, 8);
+
+ aimbs_put16(&fr->data, 0x0002); /* channel */
+ aimbs_put8(&fr->data, strlen(sender));
+ aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender));
+
+ aim_addtlvtochain16(&tl, 0x0003, code);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * aim_reqicbmparaminfo()
+ *
+ * Request ICBM parameter information.
+ *
+ */
+int aim_reqicbmparams(aim_session_t *sess)
+{
+ aim_conn_t *conn;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
+}
+
+/*
+ *
+ * I definitly recommend sending this. If you don't, you'll be stuck
+ * with the rather unreasonable defaults. You don't want those. Send this.
+ *
+ */
+int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
+{
+ aim_conn_t *conn;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+ return -EINVAL;
+
+ if (!params)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
+
+ /* This is read-only (see Parameter Reply). Must be set to zero here. */
+ aimbs_put16(&fr->data, 0x0000);
+
+ /* These are all read-write */
+ aimbs_put32(&fr->data, params->flags);
+ aimbs_put16(&fr->data, params->maxmsglen);
+ aimbs_put16(&fr->data, params->maxsenderwarn);
+ aimbs_put16(&fr->data, params->maxrecverwarn);
+ aimbs_put32(&fr->data, params->minmsginterval);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ struct aim_icbmparameters params;
+ aim_rxcallback_t userfunc;
+
+ params.maxchan = aimbs_get16(bs);
+ params.flags = aimbs_get32(bs);
+ params.maxmsglen = aimbs_get16(bs);
+ params.maxsenderwarn = aimbs_get16(bs);
+ params.maxrecverwarn = aimbs_get16(bs);
+ params.minmsginterval = aimbs_get32(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, &params);
+
+ return 0;
+}
+
+static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+ guint16 channel, nummissed, reason;
+ aim_userinfo_t userinfo;
+
+ while (aim_bstream_empty(bs)) {
+
+ channel = aimbs_get16(bs);
+ aim_extractuserinfo(sess, bs, &userinfo);
+ nummissed = aimbs_get16(bs);
+ reason = aimbs_get16(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
+ }
+
+ return ret;
+}
+
+/*
+ * Receive the response from an ICQ status message request. This contains the
+ * ICQ status message. Go figure.
+ */
+static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+ guint16 channel, reason;
+ char *sn;
+ guint8 *ck, snlen;
+
+ ck = aimbs_getraw(bs, 8);
+ channel = aimbs_get16(bs);
+ snlen = aimbs_get8(bs);
+ sn = aimbs_getstr(bs, snlen);
+ reason = aimbs_get16(bs);
+
+ switch (reason) {
+ case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */
+ guint8 statusmsgtype, *msg;
+ guint16 len;
+ guint32 state;
+
+ len = aimbs_getle16(bs); /* Should be 0x001b */
+ aim_bstream_advance(bs, len); /* Unknown */
+
+ len = aimbs_getle16(bs); /* Should be 0x000e */
+ aim_bstream_advance(bs, len); /* Unknown */
+
+ statusmsgtype = aimbs_getle8(bs);
+ switch (statusmsgtype) {
+ case 0xe8:
+ state = AIM_ICQ_STATE_AWAY;
+ break;
+ case 0xe9:
+ state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
+ break;
+ case 0xea:
+ state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
+ break;
+ case 0xeb:
+ state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
+ break;
+ case 0xec:
+ state = AIM_ICQ_STATE_CHAT;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+
+ aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
+ aimbs_getle16(bs); /* Unknown - 0x0000 */
+ aimbs_getle16(bs); /* Unknown - 0x0000 */
+
+ len = aimbs_getle16(bs);
+ msg = aimbs_getraw(bs, len);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, sn, reason, state, msg);
+
+ g_free(msg);
+ } break;
+
+ default: {
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, channel, sn, reason);
+ } break;
+ } /* end switch */
+
+ g_free(ck);
+ g_free(sn);
+
+ return ret;
+}
+
+static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ guint16 type;
+ guint8 snlen, *ck;
+ char *sn;
+ int ret = 0;
+
+ ck = aimbs_getraw(bs, 8);
+ type = aimbs_get16(bs);
+ snlen = aimbs_get8(bs);
+ sn = aimbs_getstr(bs, snlen);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, type, sn);
+
+ g_free(sn);
+ g_free(ck);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0005)
+ return paraminfo(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0006)
+ return outgoingim(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0007)
+ return incomingim(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000a)
+ return missedcall(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000b)
+ return clientautoresp(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000c)
+ return msgack(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0004;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "messaging", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
diff --git a/protocols/oscar/im.h b/protocols/oscar/im.h
new file mode 100644
index 00000000..061ff5b5
--- /dev/null
+++ b/protocols/oscar/im.h
@@ -0,0 +1,198 @@
+#ifndef __OSCAR_IM_H__
+#define __OSCAR_IM_H__
+
+#define AIM_CB_FAM_MSG 0x0004
+
+/*
+ * SNAC Family: Messaging Services.
+ */
+#define AIM_CB_MSG_ERROR 0x0001
+#define AIM_CB_MSG_PARAMINFO 0x0005
+#define AIM_CB_MSG_INCOMING 0x0007
+#define AIM_CB_MSG_EVIL 0x0009
+#define AIM_CB_MSG_MISSEDCALL 0x000a
+#define AIM_CB_MSG_CLIENTAUTORESP 0x000b
+#define AIM_CB_MSG_ACK 0x000c
+#define AIM_CB_MSG_DEFAULT 0xffff
+
+#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */
+#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */
+#define AIM_IMFLAGS_UNICODE 0x0004
+#define AIM_IMFLAGS_ISO_8859_1 0x0008
+#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */
+#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */
+#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */
+#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */
+#define AIM_IMFLAGS_EXTDATA 0x0100
+#define AIM_IMFLAGS_CUSTOMCHARSET 0x0200 /* charset fields set */
+#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */
+#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */
+
+/*
+ * Multipart message structures.
+ */
+typedef struct aim_mpmsg_section_s {
+ guint16 charset;
+ guint16 charsubset;
+ guint8 *data;
+ guint16 datalen;
+ struct aim_mpmsg_section_s *next;
+} aim_mpmsg_section_t;
+
+typedef struct aim_mpmsg_s {
+ int numparts;
+ aim_mpmsg_section_t *parts;
+} aim_mpmsg_t;
+
+int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm);
+int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen);
+int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii);
+int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen);
+void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm);
+
+/*
+ * Arguments to aim_send_im_ext().
+ *
+ * This is really complicated. But immensely versatile.
+ *
+ */
+struct aim_sendimext_args {
+
+ /* These are _required_ */
+ const char *destsn;
+ guint32 flags; /* often 0 */
+
+ /* Only required if not using multipart messages */
+ const char *msg;
+ int msglen;
+
+ /* Required if ->msg is not provided */
+ aim_mpmsg_t *mpmsg;
+
+ /* Only used if AIM_IMFLAGS_HASICON is set */
+ guint32 iconlen;
+ time_t iconstamp;
+ guint32 iconsum;
+
+ /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
+ guint8 *features;
+ guint8 featureslen;
+
+ /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
+ guint16 charset;
+ guint16 charsubset;
+};
+
+/*
+ * Arguments to aim_send_rtfmsg().
+ */
+struct aim_sendrtfmsg_args {
+ const char *destsn;
+ guint32 fgcolor;
+ guint32 bgcolor;
+ const char *rtfmsg; /* must be in RTF */
+};
+
+/*
+ * This information is provided in the Incoming ICBM callback for
+ * Channel 1 ICBM's.
+ *
+ * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
+ * are optional, both are always set by the current libfaim code.
+ * That may or may not change in the future. It is mainly for
+ * consistency with aim_sendimext_args.
+ *
+ * Multipart messages require some explanation. If you want to use them,
+ * I suggest you read all the comments in im.c.
+ *
+ */
+struct aim_incomingim_ch1_args {
+
+ /* Always provided */
+ aim_mpmsg_t mpmsg;
+ guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
+
+ /* Only provided if message has a human-readable section */
+ char *msg;
+ int msglen;
+
+ /* Only provided if AIM_IMFLAGS_HASICON is set */
+ time_t iconstamp;
+ guint32 iconlen;
+ guint16 iconsum;
+
+ /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
+ guint8 *features;
+ guint8 featureslen;
+
+ /* Only provided if AIM_IMFLAGS_EXTDATA is set */
+ guint8 extdatalen;
+ guint8 *extdata;
+
+ /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
+ guint16 charset;
+ guint16 charsubset;
+};
+
+/* Valid values for channel 2 args->status */
+#define AIM_RENDEZVOUS_PROPOSE 0x0000
+#define AIM_RENDEZVOUS_CANCEL 0x0001
+#define AIM_RENDEZVOUS_ACCEPT 0x0002
+
+struct aim_incomingim_ch2_args {
+ guint8 cookie[8];
+ guint16 reqclass;
+ guint16 status;
+ guint16 errorcode;
+ const char *clientip;
+ const char *clientip2;
+ const char *verifiedip;
+ guint16 port;
+ const char *msg; /* invite message or file description */
+ const char *encoding;
+ const char *language;
+ union {
+ struct {
+ guint32 checksum;
+ guint32 length;
+ time_t timestamp;
+ guint8 *icon;
+ } icon;
+ struct {
+ struct aim_chat_roominfo roominfo;
+ } chat;
+ struct {
+ guint32 fgcolor;
+ guint32 bgcolor;
+ const char *rtfmsg;
+ } rtfmsg;
+ } info;
+ void *destructor; /* used internally only */
+};
+
+/* Valid values for channel 4 args->type */
+#define AIM_ICQMSG_AUTHREQUEST 0x0006
+#define AIM_ICQMSG_AUTHDENIED 0x0007
+#define AIM_ICQMSG_AUTHGRANTED 0x0008
+
+struct aim_incomingim_ch4_args {
+ guint32 uin; /* Of the sender of the ICBM */
+ guint16 type;
+ char *msg; /* Reason for auth request, deny, or accept */
+};
+
+int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
+int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args);
+int aim_send_im(aim_session_t *, const char *destsn, unsigned short flags, const char *msg);
+int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
+guint16 aim_iconsum(const guint8 *buf, int buflen);
+int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
+int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len);
+const char *aim_directim_getsn(aim_conn_t *conn);
+aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn);
+aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const guint8 *cookie);
+
+int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type);
+int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, const char *message, const guint8 state, const guint16 dc);
+
+#endif /* __OSCAR_IM_H__ */
diff --git a/protocols/oscar/info.c b/protocols/oscar/info.c
new file mode 100644
index 00000000..ffe29d1f
--- /dev/null
+++ b/protocols/oscar/info.c
@@ -0,0 +1,725 @@
+/*
+ * aim_info.c
+ *
+ * The functions here are responsible for requesting and parsing information-
+ * gathering SNACs. Or something like that.
+ *
+ */
+
+#include <aim.h>
+#include "info.h"
+
+struct aim_priv_inforeq {
+ char sn[MAXSNLEN+1];
+ guint16 infotype;
+};
+
+int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, guint16 infotype)
+{
+ struct aim_priv_inforeq privdata;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn || !sn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
+ return -ENOMEM;
+
+ strncpy(privdata.sn, sn, sizeof(privdata.sn));
+ privdata.infotype = infotype;
+ snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
+
+ aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
+ aimbs_put16(&fr->data, infotype);
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+const char *aim_userinfo_sn(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return NULL;
+
+ return ui->sn;
+}
+
+guint16 aim_userinfo_flags(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0;
+
+ return ui->flags;
+}
+
+guint16 aim_userinfo_idle(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0;
+
+ return ui->idletime;
+}
+
+float aim_userinfo_warnlevel(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0.00;
+
+ return (ui->warnlevel / 10);
+}
+
+time_t aim_userinfo_membersince(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0;
+
+ return (time_t)ui->membersince;
+}
+
+time_t aim_userinfo_onlinesince(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0;
+
+ return (time_t)ui->onlinesince;
+}
+
+guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui)
+{
+
+ if (!ui)
+ return 0;
+
+ return ui->sessionlen;
+}
+
+int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap)
+{
+
+ if (!ui || !(ui->present & AIM_USERINFO_PRESENT_CAPABILITIES))
+ return -1;
+
+ return !!(ui->capabilities & cap);
+}
+
+
+/*
+ * Capability blocks.
+ *
+ * These are CLSIDs. They should actually be of the form:
+ *
+ * {0x0946134b, 0x4c7f, 0x11d1,
+ * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
+ *
+ * But, eh.
+ */
+static const struct {
+ guint32 flag;
+ guint8 data[16];
+} aim_caps[] = {
+
+ /*
+ * Chat is oddball.
+ */
+ {AIM_CAPS_CHAT,
+ {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ /*
+ * These are mostly in order.
+ */
+ {AIM_CAPS_VOICE,
+ {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_SENDFILE,
+ {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ /*
+ * Advertised by the EveryBuddy client.
+ */
+ {AIM_CAPS_ICQ,
+ {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_IMIMAGE,
+ {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_BUDDYICON,
+ {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_SAVESTOCKS,
+ {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_GETFILE,
+ {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ /*
+ * Client supports channel 2 extended, TLV(0x2711) based messages.
+ * Currently used only by ICQ clients. ICQ clients and clones use this GUID
+ * as message format sign. Trillian client use another GUID in channel 2
+ * messages to implement its own message format (trillian doesn't use
+ * TLV(x2711) in SecureIM channel 2 messages!).
+ */
+ {AIM_CAPS_ICQSERVERRELAY,
+ {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ /*
+ * Indeed, there are two of these. The former appears to be correct,
+ * but in some versions of winaim, the second one is set. Either they
+ * forgot to fix endianness, or they made a typo. It really doesn't
+ * matter which.
+ */
+ {AIM_CAPS_GAMES,
+ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+ {AIM_CAPS_GAMES2,
+ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_SENDBUDDYLIST,
+ {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_UTF8,
+ {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_ICQRTF,
+ {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
+ 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
+
+ {AIM_CAPS_ICQUNKNOWN,
+ {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
+ 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
+
+ {AIM_CAPS_EMPTY,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+
+ {AIM_CAPS_TRILLIANCRYPT,
+ {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
+ 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
+
+ {AIM_CAPS_APINFO,
+ {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6,
+ 0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}},
+
+ {AIM_CAPS_INTEROP,
+ {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_ICHAT,
+ {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+ {AIM_CAPS_LAST}
+};
+
+/*
+ * This still takes a length parameter even with a bstream because capabilities
+ * are not naturally bounded.
+ *
+ */
+guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len)
+{
+ guint32 flags = 0;
+ int offset;
+
+ for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
+ guint8 *cap;
+ int i, identified;
+
+ cap = aimbs_getraw(bs, 0x10);
+
+ for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
+
+ if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
+ flags |= aim_caps[i].flag;
+ identified++;
+ break; /* should only match once... */
+
+ }
+ }
+
+ if (!identified) {
+ /*FIXME*/
+ g_strdup_printf("unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
+ 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]);
+
+ }
+
+ g_free(cap);
+ }
+
+ return flags;
+}
+
+int aim_putcap(aim_bstream_t *bs, guint32 caps)
+{
+ int i;
+
+ if (!bs)
+ return -EINVAL;
+
+ for (i = 0; aim_bstream_empty(bs); i++) {
+
+ if (aim_caps[i].flag == AIM_CAPS_LAST)
+ break;
+
+ if (caps & aim_caps[i].flag)
+ aimbs_putraw(bs, aim_caps[i].data, 0x10);
+
+ }
+
+ return 0;
+}
+
+/*
+ * AIM is fairly regular about providing user info. This is a generic
+ * routine to extract it in its standard form.
+ */
+int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo)
+{
+ int curtlv, tlvcnt;
+ guint8 snlen;
+
+ if (!bs || !outinfo)
+ return -EINVAL;
+
+ /* Clear out old data first */
+ memset(outinfo, 0x00, sizeof(aim_userinfo_t));
+
+ /*
+ * Screen name. Stored as an unterminated string prepended with a
+ * byte containing its length.
+ */
+ snlen = aimbs_get8(bs);
+ aimbs_getrawbuf(bs, (guint8 *)outinfo->sn, snlen);
+
+ /*
+ * Warning Level. Stored as an unsigned short.
+ */
+ outinfo->warnlevel = aimbs_get16(bs);
+
+ /*
+ * TLV Count. Unsigned short representing the number of
+ * Type-Length-Value triples that follow.
+ */
+ tlvcnt = aimbs_get16(bs);
+
+ /*
+ * Parse out the Type-Length-Value triples as they're found.
+ */
+ for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
+ int endpos;
+ guint16 type, length;
+
+ type = aimbs_get16(bs);
+ length = aimbs_get16(bs);
+
+ endpos = aim_bstream_curpos(bs) + length;
+
+ if (type == 0x0001) {
+ /*
+ * Type = 0x0001: User flags
+ *
+ * Specified as any of the following ORed together:
+ * 0x0001 Trial (user less than 60days)
+ * 0x0002 Unknown bit 2
+ * 0x0004 AOL Main Service user
+ * 0x0008 Unknown bit 4
+ * 0x0010 Free (AIM) user
+ * 0x0020 Away
+ * 0x0400 ActiveBuddy
+ *
+ */
+ outinfo->flags = aimbs_get16(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
+
+ } else if (type == 0x0002) {
+ /*
+ * Type = 0x0002: Member-Since date.
+ *
+ * The time/date that the user originally registered for
+ * the service, stored in time_t format.
+ */
+ outinfo->membersince = aimbs_get32(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
+
+ } else if (type == 0x0003) {
+ /*
+ * Type = 0x0003: On-Since date.
+ *
+ * The time/date that the user started their current
+ * session, stored in time_t format.
+ */
+ outinfo->onlinesince = aimbs_get32(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
+
+ } else if (type == 0x0004) {
+ /*
+ * Type = 0x0004: Idle time.
+ *
+ * Number of seconds since the user actively used the
+ * service.
+ *
+ * Note that the client tells the server when to start
+ * counting idle times, so this may or may not be
+ * related to reality.
+ */
+ outinfo->idletime = aimbs_get16(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
+
+ } else if (type == 0x0006) {
+ /*
+ * Type = 0x0006: ICQ Online Status
+ *
+ * ICQ's Away/DND/etc "enriched" status. Some decoding
+ * of values done by Scott <darkagl@pcnet.com>
+ */
+ aimbs_get16(bs);
+ outinfo->icqinfo.status = aimbs_get16(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
+
+ } else if (type == 0x000a) {
+ /*
+ * Type = 0x000a
+ *
+ * ICQ User IP Address.
+ * Ahh, the joy of ICQ security.
+ */
+ outinfo->icqinfo.ipaddr = aimbs_get32(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
+
+ } else if (type == 0x000c) {
+ /*
+ * Type = 0x000c
+ *
+ * random crap containing the IP address,
+ * apparently a port number, and some Other Stuff.
+ *
+ */
+ aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
+ outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
+
+ } else if (type == 0x000d) {
+ /*
+ * Type = 0x000d
+ *
+ * Capability information.
+ *
+ */
+ outinfo->capabilities = aim_getcap(sess, bs, length);
+ outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+
+ } else if (type == 0x000e) {
+ /*
+ * Type = 0x000e
+ *
+ * Unknown. Always of zero length, and always only
+ * on AOL users.
+ *
+ * Ignore.
+ *
+ */
+
+ } else if ((type == 0x000f) || (type == 0x0010)) {
+ /*
+ * Type = 0x000f: Session Length. (AIM)
+ * Type = 0x0010: Session Length. (AOL)
+ *
+ * The duration, in seconds, of the user's current
+ * session.
+ *
+ * Which TLV type this comes in depends on the
+ * service the user is using (AIM or AOL).
+ *
+ */
+ outinfo->sessionlen = aimbs_get32(bs);
+ outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
+
+ } else {
+
+ /*
+ * Reaching here indicates that either AOL has
+ * added yet another TLV for us to deal with,
+ * or the parsing has gone Terribly Wrong.
+ *
+ * Either way, inform the owner and attempt
+ * recovery.
+ *
+ */
+#ifdef DEBUG
+ // do_error_dialog(sess->aux_data, G_STRLOC, "Unknown TLV encountered");
+#endif
+
+ }
+
+ /* Save ourselves. */
+ aim_bstream_setpos(bs, endpos);
+ }
+
+ return 0;
+}
+
+/*
+ * Inverse of aim_extractuserinfo()
+ */
+int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info)
+{
+ aim_tlvlist_t *tlvlist = NULL;
+
+ if (!bs || !info)
+ return -EINVAL;
+
+ aimbs_put8(bs, strlen(info->sn));
+ aimbs_putraw(bs, (guint8 *)info->sn, strlen(info->sn));
+
+ aimbs_put16(bs, info->warnlevel);
+
+
+ aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
+ aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
+ aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
+ aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
+
+#if ICQ_OSCAR_SUPPORT
+ if (atoi(info->sn) != 0) {
+ aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
+ aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
+ }
+#endif
+
+ aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
+
+ aim_addtlvtochain32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
+
+ aimbs_put16(bs, aim_counttlvchain(&tlvlist));
+ aim_writetlvchain(bs, &tlvlist);
+ aim_freetlvchain(&tlvlist);
+
+ return 0;
+}
+
+int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn || !info)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
+ aim_putuserinfo(&fr->data, info);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn || !sn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Huh? What is this?
+ */
+int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn || !sn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
+ aimbs_put8(&fr->data, strlen(sn));
+ aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Normally contains:
+ * t(0001) - short containing max profile length (value = 1024)
+ * t(0002) - short - unknown (value = 16) [max MIME type length?]
+ * t(0003) - short - unknown (value = 10)
+ * t(0004) - short - unknown (value = 2048) [ICQ only?]
+ */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_tlvlist_t *tlvlist;
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ guint16 maxsiglen = 0;
+
+ tlvlist = aim_readtlvchain(bs);
+
+ if (aim_gettlv(tlvlist, 0x0001, 1))
+ maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, maxsiglen);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_userinfo_t userinfo;
+ char *text_encoding = NULL, *text = NULL;
+ guint16 text_length = 0;
+ aim_rxcallback_t userfunc;
+ aim_tlvlist_t *tlvlist;
+ aim_tlv_t *tlv;
+ aim_snac_t *origsnac = NULL;
+ struct aim_priv_inforeq *inforeq;
+ int ret = 0;
+
+ origsnac = aim_remsnac(sess, snac->id);
+
+ if (!origsnac || !origsnac->data) {
+ do_error_dialog(sess->aux_data, "major problem: no snac stored!", "Gaim");
+ return 0;
+ }
+
+ inforeq = (struct aim_priv_inforeq *)origsnac->data;
+
+ if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
+ (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE) &&
+ (inforeq->infotype != AIM_GETINFO_CAPABILITIES)) {
+ do_error_dialog(sess->aux_data, "unknown infotype in request!", "Gaim");
+ return 0;
+ }
+
+ aim_extractuserinfo(sess, bs, &userinfo);
+
+ tlvlist = aim_readtlvchain(bs);
+
+ /*
+ * Depending on what informational text was requested, different
+ * TLVs will appear here.
+ *
+ * Profile will be 1 and 2, away message will be 3 and 4, caps
+ * will be 5.
+ */
+ if (inforeq->infotype == AIM_GETINFO_GENERALINFO) {
+ text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
+ if((tlv = aim_gettlv(tlvlist, 0x0002, 1))) {
+ text = g_new0(char, tlv->length);
+ memcpy(text, tlv->value, tlv->length);
+ text_length = tlv->length;
+ }
+ } else if (inforeq->infotype == AIM_GETINFO_AWAYMESSAGE) {
+ text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
+ if((tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
+ text = g_new0(char, tlv->length);
+ memcpy(text, tlv->value, tlv->length);
+ text_length = tlv->length;
+ }
+ } else if (inforeq->infotype == AIM_GETINFO_CAPABILITIES) {
+ aim_tlv_t *ct;
+
+ if ((ct = aim_gettlv(tlvlist, 0x0005, 1))) {
+ aim_bstream_t cbs;
+
+ aim_bstream_init(&cbs, ct->value, ct->length);
+
+ userinfo.capabilities = aim_getcap(sess, &cbs, ct->length);
+ userinfo.present = AIM_USERINFO_PRESENT_CAPABILITIES;
+ }
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text, text_length);
+
+ g_free(text_encoding);
+ g_free(text);
+
+ aim_freetlvchain(&tlvlist);
+
+ if (origsnac)
+ g_free(origsnac->data);
+ g_free(origsnac);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return rights(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0006)
+ return userinfo(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0002;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "locate", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
diff --git a/protocols/oscar/info.h b/protocols/oscar/info.h
new file mode 100644
index 00000000..b4d99e9f
--- /dev/null
+++ b/protocols/oscar/info.h
@@ -0,0 +1,44 @@
+#ifndef __OSCAR_INFO_H__
+#define __OSCAR_INFO_H__
+
+#define AIM_CB_FAM_LOC 0x0002
+
+/*
+ * SNAC Family: Location Services.
+ */
+#define AIM_CB_LOC_ERROR 0x0001
+#define AIM_CB_LOC_REQRIGHTS 0x0002
+#define AIM_CB_LOC_RIGHTSINFO 0x0003
+#define AIM_CB_LOC_SETUSERINFO 0x0004
+#define AIM_CB_LOC_REQUSERINFO 0x0005
+#define AIM_CB_LOC_USERINFO 0x0006
+#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
+#define AIM_CB_LOC_WATCHERNOT 0x0008
+#define AIM_CB_LOC_DEFAULT 0xffff
+
+#define AIM_CAPS_BUDDYICON 0x00000001
+#define AIM_CAPS_VOICE 0x00000002
+#define AIM_CAPS_IMIMAGE 0x00000004
+#define AIM_CAPS_CHAT 0x00000008
+#define AIM_CAPS_GETFILE 0x00000010
+#define AIM_CAPS_SENDFILE 0x00000020
+#define AIM_CAPS_GAMES 0x00000040
+#define AIM_CAPS_SAVESTOCKS 0x00000080
+#define AIM_CAPS_SENDBUDDYLIST 0x00000100
+#define AIM_CAPS_GAMES2 0x00000200
+#define AIM_CAPS_ICQ 0x00000400
+#define AIM_CAPS_APINFO 0x00000800
+#define AIM_CAPS_ICQRTF 0x00001000
+#define AIM_CAPS_EMPTY 0x00002000
+#define AIM_CAPS_ICQSERVERRELAY 0x00004000
+#define AIM_CAPS_ICQUNKNOWN 0x00008000
+#define AIM_CAPS_TRILLIANCRYPT 0x00010000
+#define AIM_CAPS_UTF8 0x00020000
+#define AIM_CAPS_INTEROP 0x00040000
+#define AIM_CAPS_ICHAT 0x00080000
+#define AIM_CAPS_EXTCHAN2 0x00100000
+#define AIM_CAPS_LAST 0x00200000
+
+int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+
+#endif /* __OSCAR_INFO_H__ */
diff --git a/protocols/oscar/misc.c b/protocols/oscar/misc.c
new file mode 100644
index 00000000..e5c5c26f
--- /dev/null
+++ b/protocols/oscar/misc.c
@@ -0,0 +1,396 @@
+
+/*
+ * aim_misc.c
+ *
+ * TODO: Seperate a lot of this into an aim_bos.c.
+ *
+ * Other things...
+ *
+ * - Idle setting
+ *
+ *
+ */
+
+#include <aim.h>
+
+/*
+ * aim_bos_setbuddylist(buddylist)
+ *
+ * This just builds the "set buddy list" command then queues it.
+ *
+ * buddy_list = "Screen Name One&ScreenNameTwo&";
+ *
+ * TODO: Clean this up.
+ *
+ * XXX: I can't stress the TODO enough.
+ *
+ */
+int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int len = 0;
+ char *localcpy = NULL;
+ char *tmpptr = NULL;
+
+ if (!buddy_list || !(localcpy = g_strdup(buddy_list)))
+ return -EINVAL;
+
+ for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+ len += 1 + strlen(tmpptr);
+ tmpptr = strtok(NULL, "&");
+ }
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+ strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
+
+ for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+
+ aimbs_put8(&fr->data, strlen(tmpptr));
+ aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr));
+ tmpptr = strtok(NULL, "&");
+ }
+
+ aim_tx_enqueue(sess, fr);
+
+ g_free(localcpy);
+
+ return 0;
+}
+
+/*
+ * aim_bos_setprofile(profile)
+ *
+ * Gives BOS your profile.
+ *
+ */
+int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps)
+{
+ static const char defencoding[] = {"text/aolrtf; charset=\"utf-8\""};
+ aim_frame_t *fr;
+ aim_tlvlist_t *tl = NULL;
+ aim_snacid_t snacid;
+
+ /* Build to packet first to get real length */
+ if (profile) {
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(defencoding), (guint8 *)defencoding);
+ aim_addtlvtochain_raw(&tl, 0x0002, strlen(profile), (guint8 *)profile);
+ }
+
+ /*
+ * So here's how this works:
+ * - You are away when you have a non-zero-length type 4 TLV stored.
+ * - You become unaway when you clear the TLV with a zero-length
+ * type 4 TLV.
+ * - If you do not send the type 4 TLV, your status does not change
+ * (that is, if you were away, you'll remain away).
+ */
+ if (awaymsg) {
+ if (strlen(awaymsg)) {
+ aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), (guint8 *)defencoding);
+ aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), (guint8 *)awaymsg);
+ } else
+ aim_addtlvtochain_noval(&tl, 0x0004);
+ }
+
+ aim_addtlvtochain_caps(&tl, 0x0005, caps);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * aim_bos_reqbuddyrights()
+ *
+ * Request Buddy List rights.
+ *
+ */
+int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
+}
+
+/*
+ * Send a warning to destsn.
+ *
+ * Flags:
+ * AIM_WARN_ANON Send as an anonymous (doesn't count as much)
+ *
+ * returns -1 on error (couldn't alloc packet), 0 on success.
+ *
+ */
+int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ guint16 outflags = 0x0000;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
+
+ aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
+
+ if (flags & AIM_WARN_ANON)
+ outflags |= 0x0001;
+
+ aimbs_put16(&fr->data, outflags);
+ aimbs_put8(&fr->data, strlen(destsn));
+ aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Generic routine for sending commands.
+ *
+ *
+ * I know I can do this in a smarter way...but I'm not thinking straight
+ * right now...
+ *
+ * I had one big function that handled all three cases, but then it broke
+ * and I split it up into three. But then I fixed it. I just never went
+ * back to the single. I don't see any advantage to doing it either way.
+ *
+ */
+int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid = 0x00000000;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+ return -ENOMEM;
+
+ aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *longdata)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!longdata)
+ return aim_genericreq_n(sess, conn, family, subtype);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+ aimbs_put32(&fr->data, *longdata);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *shortdata)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!shortdata)
+ return aim_genericreq_n(sess, conn, family, subtype);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+ aimbs_put16(&fr->data, *shortdata);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * aim_bos_reqlocaterights()
+ *
+ * Request Location services rights.
+ *
+ */
+int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
+}
+
+/*
+ * Set directory profile data (not the same as aim_bos_setprofile!)
+ *
+ * privacy: 1 to allow searching, 0 to disallow.
+ */
+int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+
+ aim_addtlvtochain16(&tl, 0x000a, privacy);
+
+ if (first)
+ aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), (guint8 *)first);
+ if (last)
+ aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), (guint8 *)last);
+ if (middle)
+ aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), (guint8 *)middle);
+ if (maiden)
+ aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), (guint8 *)maiden);
+
+ if (state)
+ aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), (guint8 *)state);
+ if (city)
+ aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), (guint8 *)city);
+
+ if (nickname)
+ aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), (guint8 *)nickname);
+ if (zip)
+ aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), (guint8 *)zip);
+
+ if (street)
+ aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), (guint8 *)street);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* XXX pass these in better */
+int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+
+ /* ?? privacy ?? */
+ aim_addtlvtochain16(&tl, 0x000a, privacy);
+
+ if (interest1)
+ aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), (guint8 *)interest1);
+ if (interest2)
+ aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), (guint8 *)interest2);
+ if (interest3)
+ aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), (guint8 *)interest3);
+ if (interest4)
+ aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), (guint8 *)interest4);
+ if (interest5)
+ aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), (guint8 *)interest5);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Should be generic enough to handle the errors for all groups.
+ *
+ */
+static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ int error = 0;
+ aim_rxcallback_t userfunc;
+ aim_snac_t *snac2;
+
+ snac2 = aim_remsnac(sess, snac->id);
+
+ if (aim_bstream_empty(bs))
+ error = aimbs_get16(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL);
+
+ if (snac2)
+ g_free(snac2->data);
+ g_free(snac2);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0001)
+ return generror(sess, mod, rx, snac, bs);
+ else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx);
+ }
+
+ return 0;
+}
+
+int misc_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0xffff;
+ mod->version = 0x0000;
+ mod->flags = AIM_MODFLAG_MULTIFAMILY;
+ strncpy(mod->name, "misc", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+
diff --git a/protocols/oscar/msgcookie.c b/protocols/oscar/msgcookie.c
new file mode 100644
index 00000000..bb498d72
--- /dev/null
+++ b/protocols/oscar/msgcookie.c
@@ -0,0 +1,196 @@
+/*
+ * Cookie Caching stuff. Adam wrote this, apparently just some
+ * derivatives of n's SNAC work. I cleaned it up, added comments.
+ *
+ */
+
+/*
+ * I'm assuming that cookies are type-specific. that is, we can have
+ * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
+ * lose some error checking. if we assume cookies are not type-specific and are
+ * wrong, we get quirky behavior when cookies step on each others' toes.
+ */
+
+#include <aim.h>
+#include "info.h"
+
+/**
+ * aim_cachecookie - appends a cookie to the cookie list
+ * @sess: session to add to
+ * @cookie: pointer to struct to append
+ *
+ * if cookie->cookie for type cookie->type is found, updates the
+ * ->addtime of the found structure; otherwise adds the given cookie
+ * to the cache
+ *
+ * returns -1 on error, 0 on append, 1 on update. the cookie you pass
+ * in may be free'd, so don't count on its value after calling this!
+ *
+ */
+int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie)
+{
+ aim_msgcookie_t *newcook;
+
+ if (!sess || !cookie)
+ return -EINVAL;
+
+ newcook = aim_checkcookie(sess, cookie->cookie, cookie->type);
+
+ if (newcook == cookie) {
+ newcook->addtime = time(NULL);
+ return 1;
+ } else if (newcook)
+ aim_cookie_free(sess, newcook);
+
+ cookie->addtime = time(NULL);
+
+ cookie->next = sess->msgcookies;
+ sess->msgcookies = cookie;
+
+ return 0;
+}
+
+/**
+ * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list)
+ * @sess: session to grab cookie from
+ * @cookie: cookie string to look for
+ * @type: cookie type to look for
+ *
+ * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process.
+ *
+ * if found, returns the struct; if none found (or on error), returns NULL:
+ */
+aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type)
+{
+ aim_msgcookie_t *cur, **prev;
+
+ if (!cookie || !sess->msgcookies)
+ return NULL;
+
+ for (prev = &sess->msgcookies; (cur = *prev); ) {
+ if ((cur->type == type) &&
+ (memcmp(cur->cookie, cookie, 8) == 0)) {
+ *prev = cur->next;
+ return cur;
+ }
+ prev = &cur->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer.
+ * @c: pointer to the cookie string array
+ * @type: cookie type to use
+ * @data: data to be cached with the cookie
+ *
+ * returns NULL on error, a pointer to the newly-allocated cookie on
+ * success.
+ *
+ */
+aim_msgcookie_t *aim_mkcookie(guint8 *c, int type, void *data)
+{
+ aim_msgcookie_t *cookie;
+
+ if (!c)
+ return NULL;
+
+ if (!(cookie = g_new0(aim_msgcookie_t,1)))
+ return NULL;
+
+ cookie->data = data;
+ cookie->type = type;
+ memcpy(cookie->cookie, c, 8);
+
+ return cookie;
+}
+
+/**
+ * aim_checkcookie - check to see if a cookietuple has been cached
+ * @sess: session to check for the cookie in
+ * @cookie: pointer to the cookie string array
+ * @type: type of the cookie to look for
+ *
+ * this returns a pointer to the cookie struct (still in the list) on
+ * success; returns NULL on error/not found
+ *
+ */
+
+aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const guint8 *cookie, int type)
+{
+ aim_msgcookie_t *cur;
+
+ for (cur = sess->msgcookies; cur; cur = cur->next) {
+ if ((cur->type == type) &&
+ (memcmp(cur->cookie, cookie, 8) == 0))
+ return cur;
+ }
+
+ return NULL;
+}
+
+#if 0 /* debugging feature */
+int aim_dumpcookie(aim_msgcookie_t *cookie)
+{
+
+ if (!cookie)
+ return -EINVAL;
+
+ printf("\tCookie at %p: %d/%s with %p, next %p\n",
+ cookie, cookie->type, cookie->cookie,
+ cookie->data, cookie->next);
+
+ return 0;
+}
+#endif
+
+/**
+ * aim_cookie_free - free an aim_msgcookie_t struct
+ * @sess: session to remove the cookie from
+ * @cookiep: the address of a pointer to the cookie struct to remove
+ *
+ * this function removes the cookie *cookie from teh list of cookies
+ * in sess, and then frees all memory associated with it. including
+ * its data! if you want to use the private data after calling this,
+ * make sure you copy it first.
+ *
+ * returns -1 on error, 0 on success.
+ *
+ */
+int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie)
+{
+ aim_msgcookie_t *cur, **prev;
+
+ if (!sess || !cookie)
+ return -EINVAL;
+
+ for (prev = &sess->msgcookies; (cur = *prev); ) {
+ if (cur == cookie)
+ *prev = cur->next;
+ else
+ prev = &cur->next;
+ }
+
+ g_free(cookie->data);
+ g_free(cookie);
+
+ return 0;
+}
+
+/* XXX I hate switch */
+int aim_msgcookie_gettype(int reqclass)
+{
+ /* XXX: hokey-assed. needs fixed. */
+ switch(reqclass) {
+ case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON;
+ case AIM_CAPS_VOICE: return AIM_COOKIETYPE_OFTVOICE;
+ case AIM_CAPS_IMIMAGE: return AIM_COOKIETYPE_OFTIMAGE;
+ case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT;
+ case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET;
+ case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND;
+ default: return AIM_COOKIETYPE_UNKNOWN;
+ }
+}
+
+
diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c
new file mode 100644
index 00000000..5a1ddc45
--- /dev/null
+++ b/protocols/oscar/oscar.c
@@ -0,0 +1,2491 @@
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "sock.h"
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include "nogaim.h"
+#include "bitlbee.h"
+#include "proxy.h"
+
+#include "aim.h"
+#include "icq.h"
+#include "bos.h"
+#include "ssi.h"
+#include "im.h"
+#include "info.h"
+#include "buddylist.h"
+#include "chat.h"
+#include "chatnav.h"
+
+/* constants to identify proto_opts */
+#define USEROPT_AUTH 0
+#define USEROPT_AUTHPORT 1
+
+#define UC_AOL 0x02
+#define UC_ADMIN 0x04
+#define UC_UNCONFIRMED 0x08
+#define UC_NORMAL 0x10
+#define UC_AB 0x20
+#define UC_WIRELESS 0x40
+
+#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
+
+#define OSCAR_GROUP "Friends"
+
+/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
+ static int gaim_caps = AIM_CAPS_UTF8; */
+
+static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY;
+static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
+
+struct oscar_data {
+ aim_session_t *sess;
+ aim_conn_t *conn;
+
+ guint cnpa;
+ guint paspa;
+
+ GSList *create_rooms;
+
+ gboolean conf;
+ gboolean reqemail;
+ gboolean setemail;
+ char *email;
+ gboolean setnick;
+ char *newsn;
+ gboolean chpass;
+ char *oldp;
+ char *newp;
+
+ GSList *oscar_chats;
+
+ gboolean killme;
+ gboolean icq;
+ GSList *evilhack;
+
+ struct {
+ guint maxbuddies; /* max users you can watch */
+ guint maxwatchers; /* max users who can watch you */
+ guint maxpermits; /* max users on permit list */
+ guint maxdenies; /* max users on deny list */
+ guint maxsiglen; /* max size (bytes) of profile */
+ guint maxawaymsglen; /* max size (bytes) of posted away message */
+ } rights;
+};
+
+struct create_room {
+ char *name;
+ int exchange;
+};
+
+struct chat_connection {
+ char *name;
+ char *show; /* AOL did something funny to us */
+ guint16 exchange;
+ guint16 instance;
+ int fd; /* this is redundant since we have the conn below */
+ aim_conn_t *conn;
+ int inpa;
+ int id;
+ struct gaim_connection *gc; /* i hate this. */
+ struct conversation *cnv; /* bah. */
+ int maxlen;
+ int maxvis;
+};
+
+struct ask_direct {
+ struct gaim_connection *gc;
+ char *sn;
+ char ip[64];
+ guint8 cookie[8];
+};
+
+struct icq_auth {
+ struct gaim_connection *gc;
+ guint32 uin;
+};
+
+static char *extract_name(const char *name) {
+ char *tmp;
+ int i, j;
+ char *x = strchr(name, '-');
+ if (!x) return NULL;
+ x = strchr(++x, '-');
+ if (!x) return NULL;
+ tmp = g_strdup(++x);
+
+ for (i = 0, j = 0; x[i]; i++) {
+ char hex[3];
+ if (x[i] != '%') {
+ tmp[j++] = x[i];
+ continue;
+ }
+ strncpy(hex, x + ++i, 2); hex[2] = 0;
+ i++;
+ tmp[j++] = (char)strtol(hex, NULL, 16);
+ }
+
+ tmp[j] = 0;
+ return tmp;
+}
+
+#if 0
+static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {
+ GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
+ struct chat_connection *c = NULL;
+
+ while (g) {
+ c = (struct chat_connection *)g->data;
+ if (c->id == id)
+ break;
+ g = g->next;
+ c = NULL;
+ }
+
+ return c;
+}
+#endif
+
+static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
+ aim_conn_t *conn) {
+ GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
+ struct chat_connection *c = NULL;
+
+ while (g) {
+ c = (struct chat_connection *)g->data;
+ if (c->conn == conn)
+ break;
+ g = g->next;
+ c = NULL;
+ }
+
+ return c;
+}
+
+static int gaim_parse_auth_resp (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_login (aim_session_t *, aim_frame_t *, ...);
+static int gaim_handle_redirect (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_oncoming (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_offgoing (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_misses (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_motd (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chatnav_info (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_join (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_leave (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
+static int gaim_bosrights (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_bos (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_admin (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_chat (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_chatnav (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_msgerr (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_locerr (aim_session_t *, aim_frame_t *, ...);
+static int gaim_icbm_param_info (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
+static int gaim_memrequest (aim_session_t *, aim_frame_t *, ...);
+static int gaim_selfinfo (aim_session_t *, aim_frame_t *, ...);
+static int gaim_offlinemsg (aim_session_t *, aim_frame_t *, ...);
+static int gaim_offlinemsgdone (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parserights (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parselist (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parseack (aim_session_t *, aim_frame_t *, ...);
+
+static int gaim_icqinfo (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parseaiminfo (aim_session_t *, aim_frame_t *, ...);
+
+static char *msgerrreason[] = {
+ "Invalid error",
+ "Invalid SNAC",
+ "Rate to host",
+ "Rate to client",
+ "Not logged in",
+ "Service unavailable",
+ "Service not defined",
+ "Obsolete SNAC",
+ "Not supported by host",
+ "Not supported by client",
+ "Refused by client",
+ "Reply too big",
+ "Responses lost",
+ "Request denied",
+ "Busted SNAC payload",
+ "Insufficient rights",
+ "In local permit/deny",
+ "Too evil (sender)",
+ "Too evil (receiver)",
+ "User temporarily unavailable",
+ "No match",
+ "List overflow",
+ "Request ambiguous",
+ "Queue full",
+ "Not while on AOL"
+};
+static int msgerrreasonlen = 25;
+
+static void oscar_callback(gpointer data, gint source,
+ GaimInputCondition condition) {
+ aim_conn_t *conn = (aim_conn_t *)data;
+ aim_session_t *sess = aim_conn_getsess(conn);
+ struct gaim_connection *gc = sess ? sess->aux_data : NULL;
+ struct oscar_data *odata;
+
+ if (!gc) {
+ /* gc is null. we return, else we seg SIGSEG on next line. */
+ return;
+ }
+
+ if (!g_slist_find(get_connections(), gc)) {
+ /* oh boy. this is probably bad. i guess the only thing we
+ * can really do is return? */
+ return;
+ }
+
+ odata = (struct oscar_data *)gc->proto_data;
+
+ if (condition & GAIM_INPUT_READ) {
+ if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+ if (aim_handlerendconnect(odata->sess, conn) < 0) {
+ aim_conn_kill(odata->sess, &conn);
+ }
+ } else {
+ if (aim_get_command(odata->sess, conn) >= 0) {
+ aim_rxdispatch(odata->sess);
+ if (odata->killme)
+ signoff(gc);
+ } else {
+ if ((conn->type == AIM_CONN_TYPE_BOS) ||
+ !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
+ hide_login_progress_error(gc, _("Disconnected."));
+ signoff(gc);
+ } else if (conn->type == AIM_CONN_TYPE_CHAT) {
+ struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
+ char buf[BUF_LONG];
+ c->conn = NULL;
+ if (c->inpa > 0)
+ gaim_input_remove(c->inpa);
+ c->inpa = 0;
+ c->fd = -1;
+ aim_conn_kill(odata->sess, &conn);
+ sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
+ do_error_dialog(sess->aux_data, buf, _("Chat Error!"));
+ } else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
+ if (odata->cnpa > 0)
+ gaim_input_remove(odata->cnpa);
+ odata->cnpa = 0;
+ while (odata->create_rooms) {
+ struct create_room *cr = odata->create_rooms->data;
+ g_free(cr->name);
+ odata->create_rooms =
+ g_slist_remove(odata->create_rooms, cr);
+ g_free(cr);
+ do_error_dialog(sess->aux_data, _("Chat is currently unavailable"),
+ _("Gaim - Chat"));
+ }
+ aim_conn_kill(odata->sess, &conn);
+ } else if (conn->type == AIM_CONN_TYPE_AUTH) {
+ if (odata->paspa > 0)
+ gaim_input_remove(odata->paspa);
+ odata->paspa = 0;
+ aim_conn_kill(odata->sess, &conn);
+ } else {
+ aim_conn_kill(odata->sess, &conn);
+ }
+ }
+ }
+ }
+}
+
+static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+ struct gaim_connection *gc = data;
+ struct oscar_data *odata;
+ aim_session_t *sess;
+ aim_conn_t *conn;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ return;
+ }
+
+ odata = gc->proto_data;
+ sess = odata->sess;
+ conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+
+ if (source < 0) {
+ hide_login_progress(gc, _("Couldn't connect to host"));
+ signoff(gc);
+ return;
+ }
+
+ aim_conn_completeconnect(sess, conn);
+ gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ,
+ oscar_callback, conn);
+}
+
+static void oscar_login(struct aim_user *user) {
+ aim_session_t *sess;
+ aim_conn_t *conn;
+ char buf[256];
+ struct gaim_connection *gc = new_gaim_conn(user);
+ struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
+
+ if (isdigit(*user->username)) {
+ odata->icq = TRUE;
+ /* this is odd but it's necessary for a proper do_import and do_export */
+ gc->protocol = PROTO_ICQ;
+ gc->password[8] = 0;
+ } else {
+ gc->protocol = PROTO_TOC;
+ gc->flags |= OPT_CONN_HTML;
+ }
+
+ sess = g_new0(aim_session_t, 1);
+
+ aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
+
+ /* we need an immediate queue because we don't use a while-loop to
+ * see if things need to be sent. */
+ aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
+ odata->sess = sess;
+ sess->aux_data = gc;
+
+ conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
+ if (conn == NULL) {
+ hide_login_progress(gc, _("Unable to login to AIM"));
+ signoff(gc);
+ return;
+ }
+
+ if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 &&
+ g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) {
+ serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.");
+ }
+
+ g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
+ set_login_progress(gc, 2, buf);
+
+ aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
+ aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
+
+ conn->status |= AIM_CONN_STATUS_INPROGRESS;
+ conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
+ user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER,
+ user->proto_opt[USEROPT_AUTHPORT][0] ?
+ atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+ oscar_login_connect, gc);
+ if (conn->fd < 0) {
+ hide_login_progress(gc, _("Couldn't connect to host"));
+ signoff(gc);
+ return;
+ }
+ aim_request_login(sess, conn, gc->username);
+}
+
+static void oscar_close(struct gaim_connection *gc) {
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+ while (odata->oscar_chats) {
+ struct chat_connection *n = odata->oscar_chats->data;
+ if (n->inpa > 0)
+ gaim_input_remove(n->inpa);
+ g_free(n->name);
+ g_free(n->show);
+ odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
+ g_free(n);
+ }
+ while (odata->create_rooms) {
+ struct create_room *cr = odata->create_rooms->data;
+ g_free(cr->name);
+ odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+ g_free(cr);
+ }
+ if (odata->email)
+ g_free(odata->email);
+ if (odata->newp)
+ g_free(odata->newp);
+ if (odata->oldp)
+ g_free(odata->oldp);
+ if (gc->inpa > 0)
+ gaim_input_remove(gc->inpa);
+ if (odata->cnpa > 0)
+ gaim_input_remove(odata->cnpa);
+ if (odata->paspa > 0)
+ gaim_input_remove(odata->paspa);
+ aim_session_kill(odata->sess);
+ g_free(odata->sess);
+ odata->sess = NULL;
+ g_free(gc->proto_data);
+ gc->proto_data = NULL;
+}
+
+static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
+ struct gaim_connection *gc = data;
+ struct oscar_data *odata;
+ aim_session_t *sess;
+ aim_conn_t *bosconn;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ return;
+ }
+
+ odata = gc->proto_data;
+ sess = odata->sess;
+ bosconn = odata->conn;
+
+ if (source < 0) {
+ hide_login_progress(gc, _("Could Not Connect"));
+ signoff(gc);
+ return;
+ }
+
+ aim_conn_completeconnect(sess, bosconn);
+ gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ,
+ oscar_callback, bosconn);
+ set_login_progress(gc, 4, _("Connection established, cookie sent"));
+}
+
+static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ struct aim_authresp_info *info;
+ int i; char *host; int port;
+ struct aim_user *user;
+ aim_conn_t *bosconn;
+
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *od = gc->proto_data;
+ user = gc->user;
+ port = user->proto_opt[USEROPT_AUTHPORT][0] ?
+ atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+
+ va_start(ap, fr);
+ info = va_arg(ap, struct aim_authresp_info *);
+ va_end(ap);
+
+ if (info->errorcode || !info->bosip || !info->cookie) {
+ switch (info->errorcode) {
+ case 0x05:
+ /* Incorrect nick/password */
+ hide_login_progress(gc, _("Incorrect nickname or password."));
+// plugin_event(event_error, (void *)980, 0, 0, 0);
+ break;
+ case 0x11:
+ /* Suspended account */
+ hide_login_progress(gc, _("Your account is currently suspended."));
+ break;
+ case 0x18:
+ /* connecting too frequently */
+ hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
+ break;
+ case 0x1c:
+ /* client too old */
+ hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE));
+ break;
+ default:
+ hide_login_progress(gc, _("Authentication Failed"));
+ break;
+ }
+ od->killme = TRUE;
+ return 1;
+ }
+
+
+ aim_conn_kill(sess, &fr->conn);
+
+ bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
+ if (bosconn == NULL) {
+ hide_login_progress(gc, _("Internal Error"));
+ od->killme = TRUE;
+ return 0;
+ }
+
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
+
+ ((struct oscar_data *)gc->proto_data)->conn = bosconn;
+ for (i = 0; i < (int)strlen(info->bosip); i++) {
+ if (info->bosip[i] == ':') {
+ port = atoi(&(info->bosip[i+1]));
+ break;
+ }
+ }
+ host = g_strndup(info->bosip, i);
+ bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
+ bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
+ g_free(host);
+ if (bosconn->fd < 0) {
+ hide_login_progress(gc, _("Could Not Connect"));
+ od->killme = TRUE;
+ return 0;
+ }
+ aim_sendcookie(sess, bosconn, info->cookie);
+ gaim_input_remove(gc->inpa);
+
+ return 1;
+}
+
+struct pieceofcrap {
+ struct gaim_connection *gc;
+ unsigned long offset;
+ unsigned long len;
+ char *modname;
+ int fd;
+ aim_conn_t *conn;
+ unsigned int inpa;
+};
+
+static void damn_you(gpointer data, gint source, GaimInputCondition c)
+{
+ struct pieceofcrap *pos = data;
+ struct oscar_data *od = pos->gc->proto_data;
+ char in = '\0';
+ int x = 0;
+ unsigned char m[17];
+
+ while (read(pos->fd, &in, 1) == 1) {
+ if (in == '\n')
+ x++;
+ else if (in != '\r')
+ x = 0;
+ if (x == 2)
+ break;
+ in = '\0';
+ }
+ if (in != '\n') {
+ do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
+ " You may be disconnected shortly.", "Login Error");
+ gaim_input_remove(pos->inpa);
+ closesocket(pos->fd);
+ g_free(pos);
+ return;
+ }
+ read(pos->fd, m, 16);
+ m[16] = '\0';
+ gaim_input_remove(pos->inpa);
+ closesocket(pos->fd);
+ aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
+ g_free(pos);
+}
+
+static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
+ struct pieceofcrap *pos = data;
+ char buf[BUF_LONG];
+
+ if (source < 0) {
+ do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
+ " You may be disconnected shortly.", "Login Error");
+ if (pos->modname)
+ g_free(pos->modname);
+ g_free(pos);
+ return;
+ }
+
+ g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA
+ "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
+ pos->offset, pos->len, pos->modname ? pos->modname : "");
+ write(pos->fd, buf, strlen(buf));
+ if (pos->modname)
+ g_free(pos->modname);
+ pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
+ return;
+}
+
+/* size of icbmui.ocm, the largest module in AIM 3.5 */
+#define AIM_MAX_FILE_SIZE 98304
+
+int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ struct pieceofcrap *pos;
+ guint32 offset, len;
+ char *modname;
+ int fd;
+
+ va_start(ap, fr);
+ offset = (guint32)va_arg(ap, unsigned long);
+ len = (guint32)va_arg(ap, unsigned long);
+ modname = va_arg(ap, char *);
+ va_end(ap);
+
+ if (len == 0) {
+ aim_sendmemblock(sess, fr->conn, offset, len, NULL,
+ AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
+ return 1;
+ }
+ /* uncomment this when you're convinced it's right. remember, it's been wrong before.
+ if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
+ char *buf;
+ int i = 8;
+ if (modname)
+ i += strlen(modname);
+ buf = g_malloc(i);
+ i = 0;
+ if (modname) {
+ memcpy(buf, modname, strlen(modname));
+ i += strlen(modname);
+ }
+ buf[i++] = offset & 0xff;
+ buf[i++] = (offset >> 8) & 0xff;
+ buf[i++] = (offset >> 16) & 0xff;
+ buf[i++] = (offset >> 24) & 0xff;
+ buf[i++] = len & 0xff;
+ buf[i++] = (len >> 8) & 0xff;
+ buf[i++] = (len >> 16) & 0xff;
+ buf[i++] = (len >> 24) & 0xff;
+ aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
+ g_free(buf);
+ return 1;
+ }
+ */
+
+ pos = g_new0(struct pieceofcrap, 1);
+ pos->gc = sess->aux_data;
+ pos->conn = fr->conn;
+
+ pos->offset = offset;
+ pos->len = len;
+ pos->modname = modname ? g_strdup(modname) : NULL;
+
+ fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos);
+ if (fd < 0) {
+ if (pos->modname)
+ g_free(pos->modname);
+ g_free(pos);
+ do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM."
+ " You may be disconnected shortly.", "Login Error");
+ }
+ pos->fd = fd;
+
+ return 1;
+}
+
+static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
+#if 0
+ struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
+#else
+ struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
+#endif
+ char *key;
+ va_list ap;
+ struct gaim_connection *gc = sess->aux_data;
+
+ va_start(ap, fr);
+ key = va_arg(ap, char *);
+ va_end(ap);
+
+ aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
+
+ return 1;
+}
+
+static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
+ struct gaim_connection *gc = sess->aux_data;
+ struct chat_connection *chatcon;
+ static int id = 1;
+
+ aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
+
+ aim_clientready(sess, fr->conn);
+
+ chatcon = find_oscar_chat_by_conn(gc, fr->conn);
+ chatcon->id = id;
+ chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
+
+ return 1;
+}
+
+static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
+
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0);
+ aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
+
+ aim_clientready(sess, fr->conn);
+
+ aim_chatnav_reqrights(sess, fr->conn);
+
+ return 1;
+}
+
+static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
+ struct gaim_connection *gc = data;
+ struct oscar_data *odata;
+ aim_session_t *sess;
+ aim_conn_t *tstconn;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ return;
+ }
+
+ odata = gc->proto_data;
+ sess = odata->sess;
+ tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
+
+ if (source < 0) {
+ aim_conn_kill(sess, &tstconn);
+ return;
+ }
+
+ aim_conn_completeconnect(sess, tstconn);
+ odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
+ oscar_callback, tstconn);
+}
+
+static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+ struct gaim_connection *gc = data;
+ struct oscar_data *odata;
+ aim_session_t *sess;
+ aim_conn_t *tstconn;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ return;
+ }
+
+ odata = gc->proto_data;
+ sess = odata->sess;
+ tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+
+ if (source < 0) {
+ aim_conn_kill(sess, &tstconn);
+ return;
+ }
+
+ aim_conn_completeconnect(sess, tstconn);
+ odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
+ oscar_callback, tstconn);
+}
+
+static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+ struct chat_connection *ccon = data;
+ struct gaim_connection *gc = ccon->gc;
+ struct oscar_data *odata;
+ aim_session_t *sess;
+ aim_conn_t *tstconn;
+
+ if (!g_slist_find(get_connections(), gc)) {
+ closesocket(source);
+ g_free(ccon->show);
+ g_free(ccon->name);
+ g_free(ccon);
+ return;
+ }
+
+ odata = gc->proto_data;
+ sess = odata->sess;
+ tstconn = ccon->conn;
+
+ if (source < 0) {
+ aim_conn_kill(sess, &tstconn);
+ g_free(ccon->show);
+ g_free(ccon->name);
+ g_free(ccon);
+ return;
+ }
+
+ aim_conn_completeconnect(sess, ccon->conn);
+ ccon->inpa = gaim_input_add(tstconn->fd,
+ GAIM_INPUT_READ,
+ oscar_callback, tstconn);
+ odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
+}
+
+/* Hrmph. I don't know how to make this look better. --mid */
+static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ struct aim_redirect_data *redir;
+ struct gaim_connection *gc = sess->aux_data;
+ struct aim_user *user = gc->user;
+ aim_conn_t *tstconn;
+ int i;
+ char *host;
+ int port;
+
+ port = user->proto_opt[USEROPT_AUTHPORT][0] ?
+ atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+
+ va_start(ap, fr);
+ redir = va_arg(ap, struct aim_redirect_data *);
+ va_end(ap);
+
+ for (i = 0; i < (int)strlen(redir->ip); i++) {
+ if (redir->ip[i] == ':') {
+ port = atoi(&(redir->ip[i+1]));
+ break;
+ }
+ }
+ host = g_strndup(redir->ip, i);
+
+ switch(redir->group) {
+ case 0x7: /* Authorizer */
+ tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
+ if (tstconn == NULL) {
+ g_free(host);
+ return 1;
+ }
+ aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
+// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
+// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
+// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
+
+ tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
+ tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
+ if (tstconn->fd < 0) {
+ aim_conn_kill(sess, &tstconn);
+ g_free(host);
+ return 1;
+ }
+ aim_sendcookie(sess, tstconn, redir->cookie);
+ break;
+ case 0xd: /* ChatNav */
+ tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
+ if (tstconn == NULL) {
+ g_free(host);
+ return 1;
+ }
+ aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
+
+ tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
+ tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
+ if (tstconn->fd < 0) {
+ aim_conn_kill(sess, &tstconn);
+ g_free(host);
+ return 1;
+ }
+ aim_sendcookie(sess, tstconn, redir->cookie);
+ break;
+ case 0xe: /* Chat */
+ {
+ struct chat_connection *ccon;
+
+ tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
+ if (tstconn == NULL) {
+ g_free(host);
+ return 1;
+ }
+
+ aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
+
+ ccon = g_new0(struct chat_connection, 1);
+ ccon->conn = tstconn;
+ ccon->gc = gc;
+ ccon->fd = -1;
+ ccon->name = g_strdup(redir->chat.room);
+ ccon->exchange = redir->chat.exchange;
+ ccon->instance = redir->chat.instance;
+ ccon->show = extract_name(redir->chat.room);
+
+ ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
+ ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
+ if (ccon->conn->fd < 0) {
+ aim_conn_kill(sess, &tstconn);
+ g_free(host);
+ g_free(ccon->show);
+ g_free(ccon->name);
+ g_free(ccon);
+ return 1;
+ }
+ aim_sendcookie(sess, tstconn, redir->cookie);
+ }
+ break;
+ default: /* huh? */
+ break;
+ }
+
+ g_free(host);
+ return 1;
+}
+
+static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *od = gc->proto_data;
+ aim_userinfo_t *info;
+ time_t time_idle = 0, signon = 0;
+ int type = 0;
+ int caps = 0;
+ char *tmp;
+
+ va_list ap;
+ va_start(ap, fr);
+ info = va_arg(ap, aim_userinfo_t *);
+ va_end(ap);
+
+ if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
+ caps = info->capabilities;
+ if (info->flags & AIM_FLAG_ACTIVEBUDDY)
+ type |= UC_AB;
+
+ if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
+ if (info->flags & AIM_FLAG_UNCONFIRMED)
+ type |= UC_UNCONFIRMED;
+ if (info->flags & AIM_FLAG_ADMINISTRATOR)
+ type |= UC_ADMIN;
+ if (info->flags & AIM_FLAG_AOL)
+ type |= UC_AOL;
+ if (info->flags & AIM_FLAG_FREE)
+ type |= UC_NORMAL;
+ if (info->flags & AIM_FLAG_AWAY)
+ type |= UC_UNAVAILABLE;
+ if (info->flags & AIM_FLAG_WIRELESS)
+ type |= UC_WIRELESS;
+ }
+ if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
+ type = (info->icqinfo.status << 7);
+ if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
+ (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
+ type |= UC_UNAVAILABLE;
+ }
+ }
+
+ if (caps & AIM_CAPS_ICQ)
+ caps ^= AIM_CAPS_ICQ;
+
+ if (info->present & AIM_USERINFO_PRESENT_IDLE) {
+ time(&time_idle);
+ time_idle -= info->idletime*60;
+ }
+
+ if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
+ signon = time(NULL) - info->sessionlen;
+
+ tmp = g_strdup(normalize(gc->username));
+ if (!strcmp(tmp, normalize(info->sn)))
+ g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn);
+ g_free(tmp);
+
+ serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon,
+ time_idle, type, caps);
+
+ return 1;
+}
+
+static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
+ aim_userinfo_t *info;
+ va_list ap;
+ struct gaim_connection *gc = sess->aux_data;
+
+ va_start(ap, fr);
+ info = va_arg(ap, aim_userinfo_t *);
+ va_end(ap);
+
+ serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0);
+
+ return 1;
+}
+
+static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
+ char *tmp = g_malloc(BUF_LONG + 1);
+ struct gaim_connection *gc = sess->aux_data;
+ int flags = 0;
+
+ if (args->icbmflags & AIM_IMFLAGS_AWAY)
+ flags |= IM_FLAG_AWAY;
+
+ if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
+ char *src;
+
+ if (args->icbmflags & AIM_IMFLAGS_UNICODE)
+ src = "UNICODEBIG";
+ else
+ src = "ISO8859-1";
+
+ /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
+ if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
+ // Successfully converted!
+ } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
+ int i;
+
+ for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
+ unsigned short uni;
+
+ uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
+
+ if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
+ g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
+ } else { /* something else, do UNICODE entity */
+ g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
+ }
+ }
+ } else {
+ g_snprintf(tmp, BUF_LONG, "%s", args->msg);
+ }
+ } else
+ g_snprintf(tmp, BUF_LONG, "%s", args->msg);
+
+ strip_linefeed(tmp);
+ serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
+ g_free(tmp);
+
+ return 1;
+}
+
+static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
+#if 0
+ struct gaim_connection *gc = sess->aux_data;
+#endif
+
+ if (args->status != AIM_RENDEZVOUS_PROPOSE)
+ return 1;
+#if 0
+ if (args->reqclass & AIM_CAPS_CHAT) {
+ char *name = extract_name(args->info.chat.roominfo.name);
+ int *exch = g_new0(int, 1);
+ GList *m = NULL;
+ m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
+ *exch = args->info.chat.roominfo.exchange;
+ m = g_list_append(m, exch);
+ serv_got_chat_invite(gc,
+ name ? name : args->info.chat.roominfo.name,
+ userinfo->sn,
+ (char *)args->msg,
+ m);
+ if (name)
+ g_free(name);
+ }
+#endif
+ return 1;
+}
+
+static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
+ char *uin, message;
+ struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
+
+ uin = g_strdup_printf("%u", data->uin);
+ message = 0;
+ aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
+ // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
+ show_got_added(data->gc, NULL, uin, NULL, NULL);
+
+ g_free(uin);
+ g_free(data);
+}
+
+static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
+ char *uin, *message;
+ struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
+
+ uin = g_strdup_printf("%u", data->uin);
+ message = g_strdup_printf("No reason given.");
+ aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
+ // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
+ g_free(message);
+
+ g_free(uin);
+ g_free(data);
+}
+
+/*
+ * For when other people ask you for authorization
+ */
+static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
+ struct icq_auth *data = g_new(struct icq_auth, 1);
+ char *reason = NULL;
+ char *dialog_msg;
+
+ if (strlen(msg) > 6)
+ reason = msg + 6;
+
+ dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason:\n\n%s", uin, reason ? reason : "No reason given.");
+ data->gc = gc;
+ data->uin = uin;
+ do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
+ g_free(dialog_msg);
+}
+
+static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
+ struct gaim_connection *gc = sess->aux_data;
+
+ switch (args->type) {
+ case 0x0001: { /* An almost-normal instant message. Mac ICQ sends this. It's peculiar. */
+ char *uin, *message;
+ uin = g_strdup_printf("%u", args->uin);
+ message = g_strdup(args->msg);
+ strip_linefeed(message);
+ serv_got_im(gc, uin, message, 0, time(NULL), -1);
+ g_free(uin);
+ g_free(message);
+ } break;
+
+ case 0x0004: { /* Someone sent you a URL */
+ char *uin, *message;
+ char **m;
+
+ uin = g_strdup_printf("%u", args->uin);
+ m = g_strsplit(args->msg, "\376", 2);
+
+ if ((strlen(m[0]) != 0)) {
+ message = g_strjoinv(" -- ", m);
+ } else {
+ message = m[1];
+ }
+
+ strip_linefeed(message);
+ serv_got_im(gc, uin, message, 0, time(NULL), -1);
+ g_free(uin);
+ g_free(m);
+ g_free(message);
+ } break;
+
+ case 0x0006: { /* Someone requested authorization */
+ gaim_icq_authask(gc, args->uin, args->msg);
+ } break;
+
+ case 0x0007: { /* Someone has denied you authorization */
+ serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
+ } break;
+
+ case 0x0008: { /* Someone has granted you authorization */
+ serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
+ } break;
+
+ case 0x0012: {
+ /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
+ } break;
+
+ default: {;
+ } break;
+ }
+
+ return 1;
+}
+/*
+int handle_cmp_aim(const char * a, const char * b) {
+ return handle_cmp(a, b, PROTO_TOC);
+}
+*/
+static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
+ int channel, ret = 0;
+ aim_userinfo_t *userinfo;
+ va_list ap;
+
+ va_start(ap, fr);
+ channel = va_arg(ap, int);
+ userinfo = va_arg(ap, aim_userinfo_t *);
+
+ if (set_getint(sess->aux_data, "debug")) {
+ serv_got_crap(sess->aux_data, "channel %i called", channel);
+ }
+
+ switch (channel) {
+ case 1: { /* standard message */
+ struct aim_incomingim_ch1_args *args;
+ args = va_arg(ap, struct aim_incomingim_ch1_args *);
+ ret = incomingim_chan1(sess, fr->conn, userinfo, args);
+ } break;
+
+ case 2: { /* rendevous */
+ struct aim_incomingim_ch2_args *args;
+ args = va_arg(ap, struct aim_incomingim_ch2_args *);
+ ret = incomingim_chan2(sess, fr->conn, userinfo, args);
+ } break;
+
+ case 4: { /* ICQ */
+ struct aim_incomingim_ch4_args *args;
+ args = va_arg(ap, struct aim_incomingim_ch4_args *);
+ ret = incomingim_chan4(sess, fr->conn, userinfo, args);
+ } break;
+
+ default: {;
+ } break;
+ }
+
+ va_end(ap);
+
+ return ret;
+}
+
+static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ guint16 chan, nummissed, reason;
+ aim_userinfo_t *userinfo;
+ char buf[1024];
+
+ va_start(ap, fr);
+ chan = (guint16)va_arg(ap, unsigned int);
+ userinfo = va_arg(ap, aim_userinfo_t *);
+ nummissed = (guint16)va_arg(ap, unsigned int);
+ reason = (guint16)va_arg(ap, unsigned int);
+ va_end(ap);
+
+ switch(reason) {
+ case 0:
+ /* Invalid (0) */
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s because it was invalid.") :
+ _("You missed %d messages from %s because they were invalid."),
+ nummissed,
+ userinfo->sn);
+ break;
+ case 1:
+ /* Message too large */
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s because it was too large.") :
+ _("You missed %d messages from %s because they were too large."),
+ nummissed,
+ userinfo->sn);
+ break;
+ case 2:
+ /* Rate exceeded */
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s because the rate limit has been exceeded.") :
+ _("You missed %d messages from %s because the rate limit has been exceeded."),
+ nummissed,
+ userinfo->sn);
+ break;
+ case 3:
+ /* Evil Sender */
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s because it was too evil.") :
+ _("You missed %d messages from %s because they are too evil."),
+ nummissed,
+ userinfo->sn);
+ break;
+ case 4:
+ /* Evil Receiver */
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s because you are too evil.") :
+ _("You missed %d messages from %s because you are too evil."),
+ nummissed,
+ userinfo->sn);
+ break;
+ default:
+ g_snprintf(buf,
+ sizeof(buf),
+ nummissed == 1 ?
+ _("You missed %d message from %s for unknown reasons.") :
+ _("You missed %d messages from %s for unknown reasons."),
+ nummissed,
+ userinfo->sn);
+ break;
+ }
+ do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+ return 1;
+}
+
+static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ guint16 reason;
+ char *m;
+
+ va_start(ap, fr);
+ reason = (guint16)va_arg(ap, unsigned int);
+ va_end(ap);
+
+ m = g_strdup_printf(_("SNAC threw error: %s\n"),
+ reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
+ do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
+ g_free(m);
+
+ return 1;
+}
+
+static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ char *destn;
+ guint16 reason;
+ char buf[1024];
+
+ va_start(ap, fr);
+ reason = (guint16)va_arg(ap, unsigned int);
+ destn = va_arg(ap, char *);
+ va_end(ap);
+
+ sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
+ (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+ do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+ return 1;
+}
+
+static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ char *destn;
+ guint16 reason;
+ char buf[1024];
+
+ va_start(ap, fr);
+ reason = (guint16)va_arg(ap, unsigned int);
+ destn = va_arg(ap, char *);
+ va_end(ap);
+
+ sprintf(buf, _("User information for %s unavailable: %s"), destn,
+ (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+ do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+
+ return 1;
+}
+
+static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
+ char *msg;
+ guint16 id;
+ va_list ap;
+
+ va_start(ap, fr);
+ id = (guint16)va_arg(ap, unsigned int);
+ msg = va_arg(ap, char *);
+ va_end(ap);
+
+ if (id < 4)
+ do_error_dialog(sess->aux_data, _("Your connection may be lost."),
+ _("AOL error"));
+
+ return 1;
+}
+
+static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ guint16 type;
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+ va_start(ap, fr);
+ type = (guint16)va_arg(ap, unsigned int);
+
+ switch(type) {
+ case 0x0002: {
+ guint8 maxrooms;
+ struct aim_chat_exchangeinfo *exchanges;
+ int exchangecount; // i;
+
+ maxrooms = (guint8)va_arg(ap, unsigned int);
+ exchangecount = va_arg(ap, int);
+ exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
+ va_end(ap);
+
+ while (odata->create_rooms) {
+ struct create_room *cr = odata->create_rooms->data;
+ aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
+ g_free(cr->name);
+ odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+ g_free(cr);
+ }
+ }
+ break;
+ case 0x0008: {
+ char *fqcn, *name, *ck;
+ guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
+ guint8 createperms;
+ guint32 createtime;
+
+ fqcn = va_arg(ap, char *);
+ instance = (guint16)va_arg(ap, unsigned int);
+ exchange = (guint16)va_arg(ap, unsigned int);
+ flags = (guint16)va_arg(ap, unsigned int);
+ createtime = va_arg(ap, guint32);
+ maxmsglen = (guint16)va_arg(ap, unsigned int);
+ maxoccupancy = (guint16)va_arg(ap, unsigned int);
+ createperms = (guint8)va_arg(ap, int);
+ unknown = (guint16)va_arg(ap, unsigned int);
+ name = va_arg(ap, char *);
+ ck = va_arg(ap, char *);
+ va_end(ap);
+
+ aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
+ }
+ break;
+ default:
+ va_end(ap);
+ break;
+ }
+ return 1;
+}
+
+static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ int count, i;
+ aim_userinfo_t *info;
+ struct gaim_connection *g = sess->aux_data;
+
+ struct chat_connection *c = NULL;
+
+ va_start(ap, fr);
+ count = va_arg(ap, int);
+ info = va_arg(ap, aim_userinfo_t *);
+ va_end(ap);
+
+ c = find_oscar_chat_by_conn(g, fr->conn);
+ if (!c)
+ return 1;
+
+ for (i = 0; i < count; i++)
+ add_chat_buddy(c->cnv, info[i].sn);
+
+ return 1;
+}
+
+static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ int count, i;
+ aim_userinfo_t *info;
+ struct gaim_connection *g = sess->aux_data;
+
+ struct chat_connection *c = NULL;
+
+ va_start(ap, fr);
+ count = va_arg(ap, int);
+ info = va_arg(ap, aim_userinfo_t *);
+ va_end(ap);
+
+ c = find_oscar_chat_by_conn(g, fr->conn);
+ if (!c)
+ return 1;
+
+ for (i = 0; i < count; i++)
+ remove_chat_buddy(c->cnv, info[i].sn, NULL);
+
+ return 1;
+}
+
+static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ aim_userinfo_t *userinfo;
+ struct aim_chat_roominfo *roominfo;
+ char *roomname;
+ int usercount;
+ char *roomdesc;
+ guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
+ guint32 creationtime;
+ struct gaim_connection *gc = sess->aux_data;
+ struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
+
+ va_start(ap, fr);
+ roominfo = va_arg(ap, struct aim_chat_roominfo *);
+ roomname = va_arg(ap, char *);
+ usercount= va_arg(ap, int);
+ userinfo = va_arg(ap, aim_userinfo_t *);
+ roomdesc = va_arg(ap, char *);
+ unknown_c9 = (guint16)va_arg(ap, int);
+ creationtime = (guint32)va_arg(ap, unsigned long);
+ maxmsglen = (guint16)va_arg(ap, int);
+ unknown_d2 = (guint16)va_arg(ap, int);
+ unknown_d5 = (guint16)va_arg(ap, int);
+ maxvisiblemsglen = (guint16)va_arg(ap, int);
+ va_end(ap);
+
+ ccon->maxlen = maxmsglen;
+ ccon->maxvis = maxvisiblemsglen;
+
+ return 1;
+}
+
+static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ aim_userinfo_t *info;
+ char *msg;
+ struct gaim_connection *gc = sess->aux_data;
+ struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
+ char *tmp;
+
+ va_start(ap, fr);
+ info = va_arg(ap, aim_userinfo_t *);
+ msg = va_arg(ap, char *);
+
+ tmp = g_malloc(BUF_LONG);
+ g_snprintf(tmp, BUF_LONG, "%s", msg);
+ serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
+ g_free(tmp);
+
+ return 1;
+}
+
+static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
+#if 0
+ static const char *codes[5] = {
+ "invalid",
+ "change",
+ "warning",
+ "limit",
+ "limit cleared",
+ };
+#endif
+ va_list ap;
+ guint16 code, rateclass;
+ guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
+
+ va_start(ap, fr);
+ code = (guint16)va_arg(ap, unsigned int);
+ rateclass= (guint16)va_arg(ap, unsigned int);
+ windowsize = (guint32)va_arg(ap, unsigned long);
+ clear = (guint32)va_arg(ap, unsigned long);
+ alert = (guint32)va_arg(ap, unsigned long);
+ limit = (guint32)va_arg(ap, unsigned long);
+ disconnect = (guint32)va_arg(ap, unsigned long);
+ currentavg = (guint32)va_arg(ap, unsigned long);
+ maxavg = (guint32)va_arg(ap, unsigned long);
+ va_end(ap);
+
+ /* XXX fix these values */
+ if (code == AIM_RATE_CODE_CHANGE) {
+ if (currentavg >= clear)
+ aim_conn_setlatency(fr->conn, 0);
+ } else if (code == AIM_RATE_CODE_WARNING) {
+ aim_conn_setlatency(fr->conn, windowsize/4);
+ } else if (code == AIM_RATE_CODE_LIMIT) {
+ do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
+ "Please wait 10 seconds and try again."), _("Gaim - Error"));
+ aim_conn_setlatency(fr->conn, windowsize/2);
+ } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
+ aim_conn_setlatency(fr->conn, 0);
+ }
+
+ return 1;
+}
+
+static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ aim_userinfo_t *info;
+ struct gaim_connection *gc = sess->aux_data;
+
+ va_start(ap, fr);
+ info = va_arg(ap, aim_userinfo_t *);
+ va_end(ap);
+
+ gc->evil = info->warnlevel/10;
+ /* gc->correction_time = (info->onlinesince - gc->login_time); */
+
+ return 1;
+}
+
+static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
+
+ aim_reqpersonalinfo(sess, fr->conn);
+ aim_bos_reqlocaterights(sess, fr->conn);
+ aim_bos_reqbuddyrights(sess, fr->conn);
+
+ aim_reqicbmparams(sess);
+
+ aim_bos_reqrights(sess, fr->conn);
+ aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
+ aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
+ AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
+
+ return 1;
+}
+
+static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *od = gc->proto_data;
+
+ aim_clientready(sess, fr->conn);
+
+ if (od->chpass) {
+ aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
+ g_free(od->oldp);
+ od->oldp = NULL;
+ g_free(od->newp);
+ od->newp = NULL;
+ od->chpass = FALSE;
+ }
+ if (od->setnick) {
+ aim_admin_setnick(sess, fr->conn, od->newsn);
+ g_free(od->newsn);
+ od->newsn = NULL;
+ od->setnick = FALSE;
+ }
+ if (od->conf) {
+ aim_admin_reqconfirm(sess, fr->conn);
+ od->conf = FALSE;
+ }
+ if (od->reqemail) {
+ aim_admin_getinfo(sess, fr->conn, 0x0011);
+ od->reqemail = FALSE;
+ }
+ if (od->setemail) {
+ aim_admin_setemail(sess, fr->conn, od->email);
+ g_free(od->email);
+ od->setemail = FALSE;
+ }
+
+ return 1;
+}
+
+static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
+ struct aim_icbmparameters *params;
+ va_list ap;
+
+ va_start(ap, fr);
+ params = va_arg(ap, struct aim_icbmparameters *);
+ va_end(ap);
+
+ /* Maybe senderwarn and recverwarn should be user preferences... */
+ params->maxmsglen = 8000;
+ params->minmsginterval = 0;
+
+ aim_seticbmparam(sess, params);
+
+ return 1;
+}
+
+static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+ va_list ap;
+ guint16 maxsiglen;
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+ va_start(ap, fr);
+ maxsiglen = va_arg(ap, int);
+ va_end(ap);
+
+ odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
+
+ aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
+
+ return 1;
+}
+
+static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ guint16 maxbuddies, maxwatchers;
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+ va_start(ap, fr);
+ maxbuddies = (guint16)va_arg(ap, unsigned int);
+ maxwatchers = (guint16)va_arg(ap, unsigned int);
+ va_end(ap);
+
+ odata->rights.maxbuddies = (guint)maxbuddies;
+ odata->rights.maxwatchers = (guint)maxwatchers;
+
+ return 1;
+}
+
+static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
+ guint16 maxpermits, maxdenies;
+ va_list ap;
+ struct gaim_connection *gc = sess->aux_data;
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+ va_start(ap, fr);
+ maxpermits = (guint16)va_arg(ap, unsigned int);
+ maxdenies = (guint16)va_arg(ap, unsigned int);
+ va_end(ap);
+
+ odata->rights.maxpermits = (guint)maxpermits;
+ odata->rights.maxdenies = (guint)maxdenies;
+
+// serv_finish_login(gc);
+
+ if (bud_list_cache_exists(gc))
+ do_import(gc, NULL);
+
+ aim_clientready(sess, fr->conn);
+
+ aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
+
+ aim_ssi_reqrights(sess, fr->conn);
+ aim_ssi_reqalldata(sess, fr->conn);
+
+ return 1;
+}
+
+static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
+ va_list ap;
+ struct aim_icq_offlinemsg *msg;
+ struct gaim_connection *gc = sess->aux_data;
+
+ va_start(ap, fr);
+ msg = va_arg(ap, struct aim_icq_offlinemsg *);
+ va_end(ap);
+
+ switch (msg->type) {
+ case 0x0001: { /* Basic offline message */
+ char sender[32];
+ char *dialog_msg = g_strdup(msg->msg);
+ time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
+ g_snprintf(sender, sizeof(sender), "%u", msg->sender);
+ strip_linefeed(dialog_msg);
+ serv_got_im(gc, sender, dialog_msg, 0, t, -1);
+ g_free(dialog_msg);
+ } break;
+
+ case 0x0004: { /* Someone sent you a URL */
+ char sender[32];
+ char *dialog_msg;
+ char **m;
+
+ time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
+ g_snprintf(sender, sizeof(sender), "%u", msg->sender);
+
+ m = g_strsplit(msg->msg, "\376", 2);
+
+ if ((strlen(m[0]) != 0)) {
+ dialog_msg = g_strjoinv(" -- ", m);
+ } else {
+ dialog_msg = m[1];
+ }
+
+ strip_linefeed(dialog_msg);
+ serv_got_im(gc, sender, dialog_msg, 0, t, -1);
+ g_free(dialog_msg);
+ g_free(m);
+ } break;
+
+ case 0x0006: { /* Authorization request */
+ gaim_icq_authask(gc, msg->sender, msg->msg);
+ } break;
+
+ case 0x0007: { /* Someone has denied you authorization */
+ serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
+ } break;
+
+ case 0x0008: { /* Someone has granted you authorization */
+ serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
+ } break;
+
+ case 0x0012: {
+ /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */
+ } break;
+
+ default: {;
+ }
+ }
+
+ return 1;
+}
+
+static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+ aim_icq_ackofflinemsgs(sess);
+ return 1;
+}
+
+static void oscar_keepalive(struct gaim_connection *gc) {
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+ aim_flap_nop(odata->sess, odata->conn);
+}
+
+static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
+ struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+ int ret = 0;
+ if (imflags & IM_FLAG_AWAY) {
+ ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
+ } else {
+ struct aim_sendimext_args args;
+ char *s;
+
+ args.flags = AIM_IMFLAGS_ACK;
+ if (odata->icq)
+ args.flags |= AIM_IMFLAGS_OFFLINE;
+ for (s = message; *s; s++)
+ if (*s & 128)
+ break;
+
+ /* Message contains high ASCII chars, time for some translation! */
+ if (*s) {
+ s = g_malloc(BUF_LONG);
+ /* Try if we can put it in an ISO8859-1 string first.
+ If we can't, fall back to UTF16. */
+ if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
+ args.flags |= AIM_IMFLAGS_ISO_8859_1;
+ len = ret;
+ } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
+ args.flags |= AIM_IMFLAGS_UNICODE;
+ len = ret;
+ } else {
+ /* OOF, translation failed... Oh well.. */
+ g_free( s );
+ s = message;
+ }
+ } else {
+ s = message;
+ }
+
+ args.features = gaim_features;
+ args.featureslen = sizeof(gaim_features);
+
+ args.destsn = name;
+ args.msg = s;
+ args.msglen = len;
+
+ ret = aim_send_im_ext(odata->sess, &args);
+
+ if (s != message) {
+ g_free(s);
+ }
+ }
+ if (ret >= 0)
+ return 1;
+ return ret;
+}
+
+static void oscar_get_info(struct gaim_connection *g, char *name) {
+ struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+ if (odata->icq)
+ aim_icq_getallinfo(odata->sess, name);
+ else {
+ aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
+ aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
+ }
+}
+
+static void oscar_get_away(struct gaim_connection *g, char *who) {
+ struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+ if (odata->icq) {
+ struct buddy *budlight = find_buddy(g, who);
+ if (budlight)
+ if ((budlight->uc & 0xff80) >> 7)
+ if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
+ aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
+ } else
+ aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
+}
+
+static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
+{
+
+ if (!g_strcasecmp(state, _("Visible"))) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+ return;
+ } else if (!g_strcasecmp(state, _("Invisible"))) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
+ return;
+ } /* else... */
+
+ if (od->rights.maxawaymsglen == 0)
+ do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
+
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+
+ if (gc->away)
+ g_free(gc->away);
+ gc->away = NULL;
+
+ if (!message) {
+ aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
+ return;
+ }
+
+ if (strlen(message) > od->rights.maxawaymsglen) {
+ gchar *errstr;
+
+ errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
+
+ do_error_dialog(gc, errstr, "Away Message Too Long");
+
+ g_free(errstr);
+ }
+
+ gc->away = g_strndup(message, od->rights.maxawaymsglen);
+ aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
+
+ return;
+}
+
+static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
+{
+ const char *msg = NULL;
+ gboolean no_message = FALSE;
+
+ /* clean old states */
+ if (gc->away) {
+ g_free(gc->away);
+ gc->away = NULL;
+ }
+ od->sess->aim_icq_state = 0;
+
+ /* if no message, then use an empty message */
+ if (message) {
+ msg = message;
+ } else {
+ msg = "";
+ no_message = TRUE;
+ }
+
+ if (!g_strcasecmp(state, "Online")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+ } else if (!g_strcasecmp(state, "Away")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
+ } else if (!g_strcasecmp(state, "Do Not Disturb")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
+ } else if (!g_strcasecmp(state, "Not Available")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
+ } else if (!g_strcasecmp(state, "Occupied")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
+ } else if (!g_strcasecmp(state, "Free For Chat")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
+ } else if (!g_strcasecmp(state, "Invisible")) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
+ gc->away = g_strdup(msg);
+ } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
+ if (no_message) {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+ } else {
+ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
+ gc->away = g_strdup(msg);
+ od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
+ }
+ }
+
+ return;
+}
+
+static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
+{
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+
+ oscar_set_away_aim(gc, od, state, message);
+ if (od->icq)
+ oscar_set_away_icq(gc, od, state, message);
+
+ return;
+}
+
+static void oscar_add_buddy(struct gaim_connection *g, char *name) {
+ struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+ aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
+}
+
+static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
+ struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+ struct aim_ssi_item *ssigroup;
+ while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
+}
+
+static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
+ return 1;
+}
+
+static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
+ struct gaim_connection *gc = sess->aux_data;
+ struct aim_ssi_item *curitem;
+ int tmp;
+
+ /* Add from server list to local list */
+ tmp = 0;
+ for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
+ switch (curitem->type) {
+ case 0x0000: /* Buddy */
+ if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
+ char *realname = NULL;
+
+ if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
+ realname = aim_gettlv_str(curitem->data, 0x0131, 1);
+
+ add_buddy(gc, NULL, curitem->name, realname);
+
+ if (realname)
+ g_free(realname);
+ }
+ break;
+
+ case 0x0002: /* Permit buddy */
+ if (curitem->name) {
+ GSList *list;
+ for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+ if (!list) {
+ char *name;
+ name = g_strdup(normalize(curitem->name));
+ gc->permit = g_slist_append(gc->permit, name);
+ build_allow_list();
+ tmp++;
+ }
+ }
+ break;
+
+ case 0x0003: /* Deny buddy */
+ if (curitem->name) {
+ GSList *list;
+ for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+ if (!list) {
+ char *name;
+ name = g_strdup(normalize(curitem->name));
+ gc->deny = g_slist_append(gc->deny, name);
+ build_block_list();
+ tmp++;
+ }
+ }
+ break;
+
+ case 0x0004: /* Permit/deny setting */
+ if (curitem->data) {
+ guint8 permdeny;
+ if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
+ gc->permdeny = permdeny;
+ tmp++;
+ }
+ }
+ break;
+
+ case 0x0005: /* Presence setting */
+ /* We don't want to change Gaim's setting because it applies to all accounts */
+ break;
+ } /* End of switch on curitem->type */
+ } /* End of for loop */
+
+ if (tmp)
+ do_export(gc);
+ aim_ssi_enable(sess, fr->conn);
+
+ /* Request offline messages, now that the buddy list is complete. */
+ aim_icq_reqofflinemsgs(sess);
+
+ /* Now that we have a buddy list, we can tell BitlBee that we're online. */
+ account_online(gc);
+
+ return 1;
+}
+
+static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
+{
+ aim_snac_t *origsnac;
+ va_list ap;
+
+ va_start( ap, fr );
+ origsnac = va_arg( ap, aim_snac_t * );
+ va_end( ap );
+
+ if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
+ {
+ int i, st, count = aim_bstream_empty( &fr->data );
+ char *list;
+
+ if( count & 1 )
+ {
+ /* Hmm, the length should be even... */
+ do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
+ return( 0 );
+ }
+ count >>= 1;
+
+ list = (char *) origsnac->data;
+ for( i = 0; i < count; i ++ )
+ {
+ st = aimbs_get16( &fr->data );
+ if( st == 0x0E )
+ {
+ serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
+
+ aim_ssi_auth_request( sess, fr->conn, list, "" );
+ aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
+ }
+ list += strlen( list ) + 1;
+ }
+ }
+
+ return( 1 );
+}
+
+static void oscar_set_permit_deny(struct gaim_connection *gc) {
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+ if (od->icq) {
+ GSList *list;
+ char buf[MAXMSGLEN];
+ int at;
+
+ switch(gc->permdeny) {
+ case 1:
+ aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
+ break;
+ case 2:
+ aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
+ break;
+ case 3:
+ list = gc->permit;
+ at = 0;
+ while (list) {
+ at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
+ list = list->next;
+ }
+ aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
+ break;
+ case 4:
+ list = gc->deny;
+ at = 0;
+ while (list) {
+ at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
+ list = list->next;
+ }
+ aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
+ break;
+ default:
+ break;
+ }
+ signoff_blocked(gc);
+ } else {
+ if (od->sess->ssi.received_data)
+ aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
+ }
+}
+
+static void oscar_add_permit(struct gaim_connection *gc, char *who) {
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+ if (od->icq) {
+ aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
+ } else {
+ if (od->sess->ssi.received_data)
+ aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
+ }
+}
+
+static void oscar_add_deny(struct gaim_connection *gc, char *who) {
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+ if (od->icq) {
+ aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
+ } else {
+ if (od->sess->ssi.received_data)
+ aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
+ }
+}
+
+static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+ if (!od->icq) {
+ if (od->sess->ssi.received_data)
+ aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
+ }
+}
+
+static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
+ struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+ if (!od->icq) {
+ if (od->sess->ssi.received_data)
+ aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
+ }
+}
+
+static GList *oscar_away_states(struct gaim_connection *gc)
+{
+ struct oscar_data *od = gc->proto_data;
+ GList *m = NULL;
+
+ if (!od->icq)
+ return g_list_append(m, GAIM_AWAY_CUSTOM);
+
+ m = g_list_append(m, "Online");
+ m = g_list_append(m, "Away");
+ m = g_list_append(m, "Do Not Disturb");
+ m = g_list_append(m, "Not Available");
+ m = g_list_append(m, "Occupied");
+ m = g_list_append(m, "Free For Chat");
+ m = g_list_append(m, "Invisible");
+
+ return m;
+}
+
+static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+ struct gaim_connection *gc = sess->aux_data;
+ gchar who[16];
+ GString *str;
+ va_list ap;
+ struct aim_icq_info *info;
+
+ va_start(ap, fr);
+ info = va_arg(ap, struct aim_icq_info *);
+ va_end(ap);
+
+ if (!info->uin)
+ return 0;
+
+ str = g_string_sized_new(100);
+ g_snprintf(who, sizeof(who), "%u", info->uin);
+
+ g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"),
+ info->nick ? info->nick : "-");
+ info_string_append(str, "\n", _("First Name"), info->first);
+ info_string_append(str, "\n", _("Last Name"), info->last);
+ info_string_append(str, "\n", _("Email Address"), info->email);
+ if (info->numaddresses && info->email2) {
+ int i;
+ for (i = 0; i < info->numaddresses; i++) {
+ info_string_append(str, "\n", _("Email Address"), info->email2[i]);
+ }
+ }
+ info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
+ info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
+ if (info->birthyear || info->birthmonth || info->birthday) {
+ char date[30];
+ struct tm tm;
+ tm.tm_mday = (int)info->birthday;
+ tm.tm_mon = (int)info->birthmonth-1;
+ tm.tm_year = (int)info->birthyear-1900;
+ strftime(date, sizeof(date), "%Y-%m-%d", &tm);
+ info_string_append(str, "\n", _("Birthday"), date);
+ }
+ if (info->age) {
+ char age[5];
+ g_snprintf(age, sizeof(age), "%hhd", info->age);
+ info_string_append(str, "\n", _("Age"), age);
+ }
+ info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
+ if (info->info && info->info[0]) {
+ g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"),
+ info->info, _("End of Additional Information"));
+ }
+ g_string_sprintfa(str, "\n");
+ if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
+ g_string_sprintfa(str, "%s:", _("Home Address"));
+ info_string_append(str, "\n", _("Address"), info->homeaddr);
+ info_string_append(str, "\n", _("City"), info->homecity);
+ info_string_append(str, "\n", _("State"), info->homestate);
+ info_string_append(str, "\n", _("Zip Code"), info->homezip);
+ g_string_sprintfa(str, "\n");
+ }
+ if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
+ g_string_sprintfa(str, "%s:", _("Work Address"));
+ info_string_append(str, "\n", _("Address"), info->workaddr);
+ info_string_append(str, "\n", _("City"), info->workcity);
+ info_string_append(str, "\n", _("State"), info->workstate);
+ info_string_append(str, "\n", _("Zip Code"), info->workzip);
+ g_string_sprintfa(str, "\n");
+ }
+ if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
+ g_string_sprintfa(str, "%s:", _("Work Information"));
+ info_string_append(str, "\n", _("Company"), info->workcompany);
+ info_string_append(str, "\n", _("Division"), info->workdivision);
+ info_string_append(str, "\n", _("Position"), info->workposition);
+ if (info->workwebpage && info->workwebpage[0]) {
+ info_string_append(str, "\n", _("Web Page"), info->workwebpage);
+ }
+ g_string_sprintfa(str, "\n");
+ }
+
+ serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
+ g_string_free(str, TRUE);
+
+ return 1;
+
+}
+
+static char *oscar_encoding_extract(const char *encoding)
+{
+ char *ret = NULL;
+ char *begin, *end;
+
+ g_return_val_if_fail(encoding != NULL, NULL);
+
+ /* Make sure encoding begins with charset= */
+ if (strncmp(encoding, "text/plain; charset=", 20) &&
+ strncmp(encoding, "text/aolrtf; charset=", 21) &&
+ strncmp(encoding, "text/x-aolrtf; charset=", 23))
+ {
+ return NULL;
+ }
+
+ begin = strchr(encoding, '"');
+ end = strrchr(encoding, '"');
+
+ if ((begin == NULL) || (end == NULL) || (begin >= end))
+ return NULL;
+
+ ret = g_strndup(begin+1, (end-1) - begin);
+
+ return ret;
+}
+
+static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
+{
+ char *utf8 = g_new0(char, 8192);
+
+ if ((encoding == NULL) || encoding[0] == '\0') {
+ /* gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
+ } else if (!g_strcasecmp(encoding, "iso-8859-1")) {
+ do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
+ } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
+ do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
+ } else if (!g_strcasecmp(encoding, "unicode-2-0")) {
+ do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
+ } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
+ /* gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
+ "attempting to convert to UTF-8 anyway\n", encoding);*/
+ do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
+ }
+
+ /*
+ * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
+ * we have been unable to convert the text to utf-8 from the encoding
+ * that was specified. So we assume it's UTF-8 and hope for the best.
+ */
+ if (*utf8 == 0) {
+ strncpy(utf8, text, textlen);
+ }
+
+ return utf8;
+}
+
+static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+ struct gaim_connection *gc = sess->aux_data;
+ va_list ap;
+ aim_userinfo_t *userinfo;
+ guint16 infotype;
+ char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
+ guint16 text_length;
+ char *utf8 = NULL;
+
+ va_start(ap, fr);
+ userinfo = va_arg(ap, aim_userinfo_t *);
+ infotype = va_arg(ap, int);
+ text_encoding = va_arg(ap, char*);
+ text = va_arg(ap, char*);
+ text_length = va_arg(ap, int);
+ va_end(ap);
+
+ if(text_encoding)
+ extracted_encoding = oscar_encoding_extract(text_encoding);
+ if(infotype == AIM_GETINFO_GENERALINFO) {
+ /*Display idle time*/
+ char buff[256];
+ struct tm idletime;
+ if(userinfo->idletime) {
+ memset(&idletime, 0, sizeof(struct tm));
+ idletime.tm_mday = (userinfo->idletime / 60) / 24;
+ idletime.tm_hour = (userinfo->idletime / 60) % 24;
+ idletime.tm_min = userinfo->idletime % 60;
+ idletime.tm_sec = 0;
+ strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
+ serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
+ }
+
+ if(text) {
+ utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
+ serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
+ } else {
+ serv_got_crap(gc, _("No user info available."));
+ }
+ } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
+ utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
+ serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
+ }
+
+ g_free(utf8);
+
+ return 1;
+}
+
+static char *oscar_get_status_string( struct gaim_connection *gc, int number )
+{
+ struct oscar_data *od = gc->proto_data;
+
+ if( ! number & UC_UNAVAILABLE )
+ {
+ return( NULL );
+ }
+ else if( od->icq )
+ {
+ number >>= 7;
+ if( number & AIM_ICQ_STATE_DND )
+ return( "Do Not Disturb" );
+ else if( number & AIM_ICQ_STATE_OUT )
+ return( "Not Available" );
+ else if( number & AIM_ICQ_STATE_BUSY )
+ return( "Occupied" );
+ else if( number & AIM_ICQ_STATE_INVISIBLE )
+ return( "Invisible" );
+ else
+ return( "Away" );
+ }
+ else
+ {
+ return( "Away" );
+ }
+}
+
+static struct prpl *my_protocol = NULL;
+
+void oscar_init(struct prpl *ret) {
+ ret->protocol = PROTO_OSCAR;
+ ret->away_states = oscar_away_states;
+ ret->login = oscar_login;
+ ret->close = oscar_close;
+ ret->send_im = oscar_send_im;
+ ret->get_info = oscar_get_info;
+ ret->set_away = oscar_set_away;
+ ret->get_away = oscar_get_away;
+ ret->add_buddy = oscar_add_buddy;
+ ret->remove_buddy = oscar_remove_buddy;
+ ret->add_permit = oscar_add_permit;
+ ret->add_deny = oscar_add_deny;
+ ret->rem_permit = oscar_rem_permit;
+ ret->rem_deny = oscar_rem_deny;
+ ret->set_permit_deny = oscar_set_permit_deny;
+ ret->keepalive = oscar_keepalive;
+ ret->get_status_string = oscar_get_status_string;
+
+ my_protocol = ret;
+}
diff --git a/protocols/oscar/oscar_util.c b/protocols/oscar/oscar_util.c
new file mode 100644
index 00000000..ed8409a4
--- /dev/null
+++ b/protocols/oscar/oscar_util.c
@@ -0,0 +1,167 @@
+/*
+ *
+ *
+ *
+ */
+
+#include <aim.h>
+#include <ctype.h>
+
+int aimutil_putstr(u_char *dest, const char *src, int len)
+{
+ memcpy(dest, src, len);
+ return len;
+}
+
+/*
+ * Tokenizing functions. Used to portably replace strtok/sep.
+ * -- DMP.
+ *
+ */
+int aimutil_tokslen(char *toSearch, int index, char dl)
+{
+ int curCount = 1;
+ char *next;
+ char *last;
+ int toReturn;
+
+ last = toSearch;
+ next = strchr(toSearch, dl);
+
+ while(curCount < index && next != NULL) {
+ curCount++;
+ last = next + 1;
+ next = strchr(last, dl);
+ }
+
+ if ((curCount < index) || (next == NULL))
+ toReturn = strlen(toSearch) - (curCount - 1);
+ else
+ toReturn = next - toSearch - (curCount - 1);
+
+ return toReturn;
+}
+
+int aimutil_itemcnt(char *toSearch, char dl)
+{
+ int curCount;
+ char *next;
+
+ curCount = 1;
+
+ next = strchr(toSearch, dl);
+
+ while(next != NULL) {
+ curCount++;
+ next = strchr(next + 1, dl);
+ }
+
+ return curCount;
+}
+
+char *aimutil_itemidx(char *toSearch, int index, char dl)
+{
+ int curCount;
+ char *next;
+ char *last;
+ char *toReturn;
+
+ curCount = 0;
+
+ last = toSearch;
+ next = strchr(toSearch, dl);
+
+ while (curCount < index && next != NULL) {
+ curCount++;
+ last = next + 1;
+ next = strchr(last, dl);
+ }
+
+ if (curCount < index) {
+ toReturn = g_strdup("");
+ }
+ next = strchr(last, dl);
+
+ if (curCount < index) {
+ toReturn = g_strdup("");
+ } else {
+ if (next == NULL) {
+ toReturn = g_malloc((strlen(last) + 1) * sizeof(char));
+ strcpy(toReturn, last);
+ } else {
+ toReturn = g_malloc((next - last + 1) * sizeof(char));
+ memcpy(toReturn, last, (next - last));
+ toReturn[next - last] = '\0';
+ }
+ }
+ return toReturn;
+}
+
+/*
+* int snlen(const char *)
+*
+* This takes a screen name and returns its length without
+* spaces. If there are no spaces in the SN, then the
+* return is equal to that of strlen().
+*
+*/
+static int aim_snlen(const char *sn)
+{
+ int i = 0;
+ const char *curPtr = NULL;
+
+ if (!sn)
+ return 0;
+
+ curPtr = sn;
+ while ( (*curPtr) != (char) NULL) {
+ if ((*curPtr) != ' ')
+ i++;
+ curPtr++;
+ }
+
+ return i;
+}
+
+/*
+* int sncmp(const char *, const char *)
+*
+* This takes two screen names and compares them using the rules
+* on screen names for AIM/AOL. Mainly, this means case and space
+* insensitivity (all case differences and spacing differences are
+* ignored).
+*
+* Return: 0 if equal
+* non-0 if different
+*
+*/
+
+int aim_sncmp(const char *sn1, const char *sn2)
+{
+ const char *curPtr1 = NULL, *curPtr2 = NULL;
+
+ if (aim_snlen(sn1) != aim_snlen(sn2))
+ return 1;
+
+ curPtr1 = sn1;
+ curPtr2 = sn2;
+ while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) {
+ if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) {
+ if (*curPtr1 == ' ')
+ curPtr1++;
+ if (*curPtr2 == ' ')
+ curPtr2++;
+ } else {
+ if ( toupper(*curPtr1) != toupper(*curPtr2))
+ return 1;
+ curPtr1++;
+ curPtr2++;
+ }
+ }
+
+ /* Should both be NULL */
+ if (*curPtr1 != *curPtr2)
+ return 1;
+
+ return 0;
+}
diff --git a/protocols/oscar/rxhandlers.c b/protocols/oscar/rxhandlers.c
new file mode 100644
index 00000000..eb0898ec
--- /dev/null
+++ b/protocols/oscar/rxhandlers.c
@@ -0,0 +1,408 @@
+/*
+ * aim_rxhandlers.c
+ *
+ * This file contains most all of the incoming packet handlers, along
+ * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is
+ * actually done in aim_rxqueue.c.
+ *
+ */
+
+#include <aim.h>
+
+struct aim_rxcblist_s {
+ guint16 family;
+ guint16 type;
+ aim_rxcallback_t handler;
+ u_short flags;
+ struct aim_rxcblist_s *next;
+};
+
+aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group)
+{
+ aim_module_t *cur;
+
+ for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+ if (cur->family == group)
+ return cur;
+ }
+
+ return NULL;
+}
+
+static aim_module_t *aim__findmodule(aim_session_t *sess, const char *name)
+{
+ aim_module_t *cur;
+
+ for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+ if (strcmp(name, cur->name) == 0)
+ return cur;
+ }
+
+ return NULL;
+}
+
+int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *))
+{
+ aim_module_t *mod;
+
+ if (!sess || !modfirst)
+ return -1;
+
+ if (!(mod = g_new0(aim_module_t,1)))
+ return -1;
+
+ if (modfirst(sess, mod) == -1) {
+ g_free(mod);
+ return -1;
+ }
+
+ if (aim__findmodule(sess, mod->name)) {
+ if (mod->shutdown)
+ mod->shutdown(sess, mod);
+ g_free(mod);
+ return -1;
+ }
+
+ mod->next = (aim_module_t *)sess->modlistv;
+ sess->modlistv = mod;
+
+
+ return 0;
+}
+
+void aim__shutdownmodules(aim_session_t *sess)
+{
+ aim_module_t *cur;
+
+ for (cur = (aim_module_t *)sess->modlistv; cur; ) {
+ aim_module_t *tmp;
+
+ tmp = cur->next;
+
+ if (cur->shutdown)
+ cur->shutdown(sess, cur);
+
+ g_free(cur);
+
+ cur = tmp;
+ }
+
+ sess->modlistv = NULL;
+
+ return;
+}
+
+static int consumesnac(aim_session_t *sess, aim_frame_t *rx)
+{
+ aim_module_t *cur;
+ aim_modsnac_t snac;
+
+ if (aim_bstream_empty(&rx->data) < 10)
+ return 0;
+
+ snac.family = aimbs_get16(&rx->data);
+ snac.subtype = aimbs_get16(&rx->data);
+ snac.flags = aimbs_get16(&rx->data);
+ snac.id = aimbs_get32(&rx->data);
+
+ /* Contains TLV(s) in the FNAC header */
+ if(snac.flags & 0x8000) {
+ aim_bstream_advance(&rx->data, aimbs_get16(&rx->data));
+ } else if(snac.flags & 0x0001) {
+ /* Following SNAC will be related */
+ }
+
+ if (set_getint(sess->aux_data, "debug")) {
+ serv_got_crap(sess->aux_data, "snac %x/%x received", snac.family, snac.subtype);
+ }
+
+ for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+ if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
+ (cur->family != snac.family))
+ continue;
+
+ if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+ return 1;
+
+ }
+
+ return 0;
+}
+
+static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, guint16 family, guint16 subtype)
+{
+ aim_module_t *cur;
+ aim_modsnac_t snac;
+
+ snac.family = family;
+ snac.subtype = subtype;
+ snac.flags = snac.id = 0;
+
+ for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+ if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
+ (cur->family != snac.family))
+ continue;
+
+ if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+ return 1;
+
+ }
+
+ return 0;
+}
+
+static int negchan_middle(aim_session_t *sess, aim_frame_t *fr)
+{
+ aim_tlvlist_t *tlvlist;
+ char *msg = NULL;
+ guint16 code = 0;
+ aim_rxcallback_t userfunc;
+ int ret = 1;
+
+ if (aim_bstream_empty(&fr->data) == 0) {
+ /* XXX should do something with this */
+ return 1;
+ }
+
+ /* Used only by the older login protocol */
+ /* XXX remove this special case? */
+ if (fr->conn->type == AIM_CONN_TYPE_AUTH)
+ return consumenonsnac(sess, fr, 0x0017, 0x0003);
+
+ tlvlist = aim_readtlvchain(&fr->data);
+
+ if (aim_gettlv(tlvlist, 0x0009, 1))
+ code = aim_gettlv16(tlvlist, 0x0009, 1);
+
+ if (aim_gettlv(tlvlist, 0x000b, 1))
+ msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+ if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
+ ret = userfunc(sess, fr, code, msg);
+
+ aim_freetlvchain(&tlvlist);
+
+ g_free(msg);
+
+ return ret;
+}
+
+/*
+ * Some SNACs we do not allow to be hooked, for good reason.
+ */
+static int checkdisallowed(guint16 group, guint16 type)
+{
+ static const struct {
+ guint16 group;
+ guint16 type;
+ } dontuse[] = {
+ {0x0001, 0x0002},
+ {0x0001, 0x0003},
+ {0x0001, 0x0006},
+ {0x0001, 0x0007},
+ {0x0001, 0x0008},
+ {0x0001, 0x0017},
+ {0x0001, 0x0018},
+ {0x0000, 0x0000}
+ };
+ int i;
+
+ for (i = 0; dontuse[i].group != 0x0000; i++) {
+ if ((dontuse[i].group == group) && (dontuse[i].type == type))
+ return 1;
+ }
+
+ return 0;
+}
+
+int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags)
+{
+ struct aim_rxcblist_s *newcb;
+
+ if (!conn)
+ return -1;
+
+ if (checkdisallowed(family, type)) {
+ g_assert(0);
+ return -1;
+ }
+
+ if (!(newcb = (struct aim_rxcblist_s *)g_new0(struct aim_rxcblist_s, 1)))
+ return -1;
+
+ newcb->family = family;
+ newcb->type = type;
+ newcb->flags = flags;
+ newcb->handler = newhandler;
+ newcb->next = NULL;
+
+ if (!conn->handlerlist)
+ conn->handlerlist = (void *)newcb;
+ else {
+ struct aim_rxcblist_s *cur;
+
+ for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next)
+ ;
+ cur->next = newcb;
+ }
+
+ return 0;
+}
+
+int aim_clearhandlers(aim_conn_t *conn)
+{
+ struct aim_rxcblist_s *cur;
+
+ if (!conn)
+ return -1;
+
+ for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) {
+ struct aim_rxcblist_s *tmp;
+
+ tmp = cur->next;
+ g_free(cur);
+ cur = tmp;
+ }
+ conn->handlerlist = NULL;
+
+ return 0;
+}
+
+aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type)
+{
+ struct aim_rxcblist_s *cur;
+
+ if (!conn)
+ return NULL;
+
+ for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) {
+ if ((cur->family == family) && (cur->type == type))
+ return cur->handler;
+ }
+
+ if (type == AIM_CB_SPECIAL_DEFAULT) {
+ return NULL; /* prevent infinite recursion */
+ }
+
+ return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+}
+
+void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src)
+{
+ struct aim_rxcblist_s *cur;
+
+ for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) {
+ aim_conn_addhandler(sess, dest, cur->family, cur->type,
+ cur->handler, cur->flags);
+ }
+
+ return;
+}
+
+static int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,guint16 family, guint16 type, aim_frame_t *ptr)
+{
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, conn, family, type)))
+ return userfunc(sess, ptr);
+
+ return 1; /* XXX */
+}
+
+/*
+ * aim_rxdispatch()
+ *
+ * Basically, heres what this should do:
+ * 1) Determine correct packet handler for this packet
+ * 2) Mark the packet handled (so it can be dequeued in purge_queue())
+ * 3) Send the packet to the packet handler
+ * 4) Go to next packet in the queue and start over
+ * 5) When done, run purge_queue() to purge handled commands
+ *
+ * TODO: Clean up.
+ * TODO: More support for mid-level handlers.
+ * TODO: Allow for NULL handlers.
+ *
+ */
+void aim_rxdispatch(aim_session_t *sess)
+{
+ int i;
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) {
+
+ /*
+ * XXX: This is still fairly ugly.
+ */
+
+ if (cur->handled)
+ continue;
+
+ /*
+ * This is a debugging/sanity check only and probably
+ * could/should be removed for stable code.
+ */
+ if (((cur->hdrtype == AIM_FRAMETYPE_OFT) &&
+ (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
+ ((cur->hdrtype == AIM_FRAMETYPE_FLAP) &&
+ (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
+ do_error_dialog(sess->aux_data, "incompatible frame type/connection type combination", "Gaim");
+ cur->handled = 1;
+ continue;
+ }
+
+ if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
+ if (cur->hdrtype != AIM_FRAMETYPE_OFT) {
+ do_error_dialog(sess->aux_data, "non-OFT frames on OFT connection", "Gaim");
+ cur->handled = 1; /* get rid of it */
+ } else {
+ /* FIXME: implement this (OFT frame) */
+ cur->handled = 1; /* get rid of it */
+ }
+ continue;
+ }
+
+ if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+ /* not possible */
+ do_error_dialog(sess->aux_data, "RENDEZVOUS packet in rxqueue", "Gaim");
+ cur->handled = 1;
+ continue;
+ }
+
+ if (cur->hdr.flap.type == 0x01) {
+
+ cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
+
+ continue;
+
+ } else if (cur->hdr.flap.type == 0x02) {
+
+ if ((cur->handled = consumesnac(sess, cur)))
+ continue;
+
+ } else if (cur->hdr.flap.type == 0x04) {
+
+ cur->handled = negchan_middle(sess, cur);
+ continue;
+
+ } else if (cur->hdr.flap.type == 0x05)
+ ;
+
+ if (!cur->handled) {
+ consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */
+ cur->handled = 1;
+ }
+ }
+
+ /*
+ * This doesn't have to be called here. It could easily be done
+ * by a seperate thread or something. It's an administrative operation,
+ * and can take a while. Though the less you call it the less memory
+ * you'll have :)
+ */
+ aim_purge_rxqueue(sess);
+
+ return;
+}
diff --git a/protocols/oscar/rxqueue.c b/protocols/oscar/rxqueue.c
new file mode 100644
index 00000000..d95bc026
--- /dev/null
+++ b/protocols/oscar/rxqueue.c
@@ -0,0 +1,508 @@
+/*
+ * aim_rxqueue.c
+ *
+ * This file contains the management routines for the receive
+ * (incoming packet) queue. The actual packet handlers are in
+ * aim_rxhandlers.c.
+ */
+
+#include <aim.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ *
+ */
+int aim_recv(int fd, void *buf, size_t count)
+{
+ int left, cur;
+
+ for (cur = 0, left = count; left; ) {
+ int ret;
+
+ ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
+
+ /* Of course EOF is an error, only morons disagree with that. */
+ if (ret <= 0)
+ return -1;
+
+ cur += ret;
+ left -= ret;
+ }
+
+ return cur;
+}
+
+/*
+ * Read into a byte stream. Will not read more than count, but may read
+ * less if there is not enough room in the stream buffer.
+ */
+static int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
+{
+ int red = 0;
+
+ if (!bs || (fd < 0) || (count < 0))
+ return -1;
+
+ if (count > (bs->len - bs->offset))
+ count = bs->len - bs->offset; /* truncate to remaining space */
+
+ if (count) {
+
+ red = aim_recv(fd, bs->data + bs->offset, count);
+
+ if (red <= 0)
+ return -1;
+ }
+
+ bs->offset += red;
+
+ return red;
+}
+
+int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len)
+{
+
+ if (!bs)
+ return -1;
+
+ bs->data = data;
+ bs->len = len;
+ bs->offset = 0;
+
+ return 0;
+}
+
+int aim_bstream_empty(aim_bstream_t *bs)
+{
+ return bs->len - bs->offset;
+}
+
+int aim_bstream_curpos(aim_bstream_t *bs)
+{
+ return bs->offset;
+}
+
+int aim_bstream_setpos(aim_bstream_t *bs, int off)
+{
+
+ if (off > bs->len)
+ return -1;
+
+ bs->offset = off;
+
+ return off;
+}
+
+void aim_bstream_rewind(aim_bstream_t *bs)
+{
+
+ aim_bstream_setpos(bs, 0);
+
+ return;
+}
+
+int aim_bstream_advance(aim_bstream_t *bs, int n)
+{
+
+ if (aim_bstream_empty(bs) < n)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += n;
+
+ return n;
+}
+
+guint8 aimbs_get8(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 1)
+ return 0; /* XXX throw an exception */
+
+ bs->offset++;
+
+ return aimutil_get8(bs->data + bs->offset - 1);
+}
+
+guint16 aimbs_get16(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 2)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += 2;
+
+ return aimutil_get16(bs->data + bs->offset - 2);
+}
+
+guint32 aimbs_get32(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 4)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += 4;
+
+ return aimutil_get32(bs->data + bs->offset - 4);
+}
+
+guint8 aimbs_getle8(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 1)
+ return 0; /* XXX throw an exception */
+
+ bs->offset++;
+
+ return aimutil_getle8(bs->data + bs->offset - 1);
+}
+
+guint16 aimbs_getle16(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 2)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += 2;
+
+ return aimutil_getle16(bs->data + bs->offset - 2);
+}
+
+guint32 aimbs_getle32(aim_bstream_t *bs)
+{
+
+ if (aim_bstream_empty(bs) < 4)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += 4;
+
+ return aimutil_getle32(bs->data + bs->offset - 4);
+}
+
+int aimbs_put8(aim_bstream_t *bs, guint8 v)
+{
+
+ if (aim_bstream_empty(bs) < 1)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_put8(bs->data + bs->offset, v);
+
+ return 1;
+}
+
+int aimbs_put16(aim_bstream_t *bs, guint16 v)
+{
+
+ if (aim_bstream_empty(bs) < 2)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_put16(bs->data + bs->offset, v);
+
+ return 2;
+}
+
+int aimbs_put32(aim_bstream_t *bs, guint32 v)
+{
+
+ if (aim_bstream_empty(bs) < 4)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_put32(bs->data + bs->offset, v);
+
+ return 1;
+}
+
+int aimbs_putle8(aim_bstream_t *bs, guint8 v)
+{
+
+ if (aim_bstream_empty(bs) < 1)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_putle8(bs->data + bs->offset, v);
+
+ return 1;
+}
+
+int aimbs_putle16(aim_bstream_t *bs, guint16 v)
+{
+
+ if (aim_bstream_empty(bs) < 2)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_putle16(bs->data + bs->offset, v);
+
+ return 2;
+}
+
+int aimbs_putle32(aim_bstream_t *bs, guint32 v)
+{
+
+ if (aim_bstream_empty(bs) < 4)
+ return 0; /* XXX throw an exception */
+
+ bs->offset += aimutil_putle32(bs->data + bs->offset, v);
+
+ return 1;
+}
+
+int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len)
+{
+
+ if (aim_bstream_empty(bs) < len)
+ return 0;
+
+ memcpy(buf, bs->data + bs->offset, len);
+ bs->offset += len;
+
+ return len;
+}
+
+guint8 *aimbs_getraw(aim_bstream_t *bs, int len)
+{
+ guint8 *ob;
+
+ if (!(ob = g_malloc(len)))
+ return NULL;
+
+ if (aimbs_getrawbuf(bs, ob, len) < len) {
+ g_free(ob);
+ return NULL;
+ }
+
+ return ob;
+}
+
+char *aimbs_getstr(aim_bstream_t *bs, int len)
+{
+ guint8 *ob;
+
+ if (!(ob = g_malloc(len+1)))
+ return NULL;
+
+ if (aimbs_getrawbuf(bs, ob, len) < len) {
+ g_free(ob);
+ return NULL;
+ }
+
+ ob[len] = '\0';
+
+ return (char *)ob;
+}
+
+int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len)
+{
+
+ if (aim_bstream_empty(bs) < len)
+ return 0; /* XXX throw an exception */
+
+ memcpy(bs->data + bs->offset, v, len);
+ bs->offset += len;
+
+ return len;
+}
+
+int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len)
+{
+
+ if (aim_bstream_empty(srcbs) < len)
+ return 0; /* XXX throw exception (underrun) */
+
+ if (aim_bstream_empty(bs) < len)
+ return 0; /* XXX throw exception (overflow) */
+
+ memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
+ bs->offset += len;
+ srcbs->offset += len;
+
+ return len;
+}
+
+/**
+ * aim_frame_destroy - free aim_frame_t
+ * @frame: the frame to free
+ *
+ * returns -1 on error; 0 on success.
+ *
+ */
+void aim_frame_destroy(aim_frame_t *frame)
+{
+
+ g_free(frame->data.data); /* XXX aim_bstream_free */
+
+ if (frame->hdrtype == AIM_FRAMETYPE_OFT)
+ g_free(frame->hdr.oft.hdr2);
+ g_free(frame);
+
+ return;
+}
+
+
+/*
+ * Grab a single command sequence off the socket, and enqueue
+ * it in the incoming event queue in a seperate struct.
+ */
+int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
+{
+ guint8 flaphdr_raw[6];
+ aim_bstream_t flaphdr;
+ aim_frame_t *newrx;
+ guint16 payloadlen;
+
+ if (!sess || !conn)
+ return 0;
+
+ if (conn->fd == -1)
+ return -1; /* its a aim_conn_close()'d connection */
+
+ if (conn->fd < 3) /* can happen when people abuse the interface */
+ return 0;
+
+ if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+ return aim_conn_completeconnect(sess, conn);
+
+ /*
+ * Rendezvous (client-client) connections do not speak
+ * FLAP, so this function will break on them.
+ */
+ if (conn->type == AIM_CONN_TYPE_RENDEZVOUS)
+ return aim_get_command_rendezvous(sess, conn);
+ else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+ do_error_dialog(sess->aux_data,"AIM_CONN_TYPE_RENDEZVOUS_OUT shouldn't use FLAP", "Gaim");
+ return 0;
+ }
+
+ aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw));
+
+ /*
+ * Read FLAP header. Six bytes:
+ *
+ * 0 char -- Always 0x2a
+ * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login.
+ * 2 short -- Sequence number
+ * 4 short -- Number of data bytes that follow.
+ */
+ if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ aim_bstream_rewind(&flaphdr);
+
+ /*
+ * This shouldn't happen unless the socket breaks, the server breaks,
+ * or we break. We must handle it just in case.
+ */
+ if (aimbs_get8(&flaphdr) != 0x2a) {
+ guint8 start;
+
+ aim_bstream_rewind(&flaphdr);
+ start = aimbs_get8(&flaphdr);
+ do_error_dialog(sess->aux_data, "FLAP framing disrupted", "Gaim");
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ /* allocate a new struct */
+ if (!(newrx = (aim_frame_t *)g_new0(aim_frame_t,1)))
+ return -1;
+
+ /* we're doing FLAP if we're here */
+ newrx->hdrtype = AIM_FRAMETYPE_FLAP;
+
+ newrx->hdr.flap.type = aimbs_get8(&flaphdr);
+ newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr);
+ payloadlen = aimbs_get16(&flaphdr);
+
+ newrx->nofree = 0; /* free by default */
+
+ if (payloadlen) {
+ guint8 *payload = NULL;
+
+ if (!(payload = (guint8 *) g_malloc(payloadlen))) {
+ aim_frame_destroy(newrx);
+ return -1;
+ }
+
+ aim_bstream_init(&newrx->data, payload, payloadlen);
+
+ /* read the payload */
+ if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
+ aim_frame_destroy(newrx); /* free's payload */
+ aim_conn_close(conn);
+ return -1;
+ }
+ } else
+ aim_bstream_init(&newrx->data, NULL, 0);
+
+
+ aim_bstream_rewind(&newrx->data);
+
+ newrx->conn = conn;
+
+ newrx->next = NULL; /* this will always be at the bottom */
+
+ if (!sess->queue_incoming)
+ sess->queue_incoming = newrx;
+ else {
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_incoming; cur->next; cur = cur->next)
+ ;
+ cur->next = newrx;
+ }
+
+ newrx->conn->lastactivity = time(NULL);
+
+ return 0;
+}
+
+/*
+ * Purge recieve queue of all handled commands (->handled==1). Also
+ * allows for selective freeing using ->nofree so that the client can
+ * keep the data for various purposes.
+ *
+ * If ->nofree is nonzero, the frame will be delinked from the global list,
+ * but will not be free'ed. The client _must_ keep a pointer to the
+ * data -- libfaim will not! If the client marks ->nofree but
+ * does not keep a pointer, it's lost forever.
+ *
+ */
+void aim_purge_rxqueue(aim_session_t *sess)
+{
+ aim_frame_t *cur, **prev;
+
+ for (prev = &sess->queue_incoming; (cur = *prev); ) {
+ if (cur->handled) {
+
+ *prev = cur->next;
+
+ if (!cur->nofree)
+ aim_frame_destroy(cur);
+
+ } else
+ prev = &cur->next;
+ }
+
+ return;
+}
+
+/*
+ * Since aim_get_command will aim_conn_kill dead connections, we need
+ * to clean up the rxqueue of unprocessed connections on that socket.
+ *
+ * XXX: this is something that was handled better in the old connection
+ * handling method, but eh.
+ */
+void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *currx;
+
+ for (currx = sess->queue_incoming; currx; currx = currx->next) {
+ if ((!currx->handled) && (currx->conn == conn))
+ currx->handled = 1;
+ }
+ return;
+}
+
diff --git a/protocols/oscar/search.c b/protocols/oscar/search.c
new file mode 100644
index 00000000..9685a3d1
--- /dev/null
+++ b/protocols/oscar/search.c
@@ -0,0 +1,121 @@
+
+/*
+ * aim_search.c
+ *
+ * TODO: Add aim_usersearch_name()
+ *
+ */
+
+#include <aim.h>
+
+int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn || !address)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, g_strdup(address), strlen(address)+1);
+ aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
+
+ aimbs_putraw(&fr->data, (guint8 *)address, strlen(address));
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* XXX can this be integrated with the rest of the error handling? */
+static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+ aim_snac_t *snac2;
+
+ /* XXX the modules interface should have already retrieved this for us */
+ if (!(snac2 = aim_remsnac(sess, snac->id))) {
+ do_error_dialog(sess->aux_data, "couldn't get snac", "Gaim");
+ return 0;
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, snac2->data /* address */);
+
+ /* XXX freesnac()? */
+ if (snac2)
+ g_free(snac2->data);
+ g_free(snac2);
+
+ return ret;
+}
+
+static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int j = 0, m, ret = 0;
+ aim_tlvlist_t *tlvlist;
+ char *cur = NULL, *buf = NULL;
+ aim_rxcallback_t userfunc;
+ aim_snac_t *snac2;
+ char *searchaddr = NULL;
+
+ if ((snac2 = aim_remsnac(sess, snac->id)))
+ searchaddr = (char *)snac2->data;
+
+ tlvlist = aim_readtlvchain(bs);
+ m = aim_counttlvchain(&tlvlist);
+
+ /* XXX uhm. */
+ while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
+ buf = g_realloc(buf, (j+1) * (MAXSNLEN+1));
+
+ strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
+ g_free(cur);
+
+ j++;
+ }
+
+ aim_freetlvchain(&tlvlist);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, searchaddr, j, buf);
+
+ /* XXX freesnac()? */
+ if (snac2)
+ g_free(snac2->data);
+ g_free(snac2);
+
+ g_free(buf);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0001)
+ return error(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0003)
+ return reply(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int search_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x000a;
+ mod->version = 0x0001;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "search", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
+
diff --git a/protocols/oscar/search.h b/protocols/oscar/search.h
new file mode 100644
index 00000000..77eeb265
--- /dev/null
+++ b/protocols/oscar/search.h
@@ -0,0 +1,6 @@
+#ifndef __OSCAR_SEARCH_H__
+#define __OSCAR_SEARCH_H__
+
+int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
+
+#endif /* __OSCAR_SEARCH_H__ */
diff --git a/protocols/oscar/service.c b/protocols/oscar/service.c
new file mode 100644
index 00000000..875a2eb0
--- /dev/null
+++ b/protocols/oscar/service.c
@@ -0,0 +1,946 @@
+/*
+ * Group 1. This is a very special group. All connections support
+ * this group, as it does some particularly good things (like rate limiting).
+ */
+
+#include <aim.h>
+
+#include "md5.h"
+
+/* Client Online (group 1, subtype 2) */
+int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ struct snacgroup *sg;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!ins)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
+
+ /*
+ * Send only the tool versions that the server cares about (that it
+ * marked as supporting in the server ready SNAC).
+ */
+ for (sg = ins->groups; sg; sg = sg->next) {
+ aim_module_t *mod;
+
+ if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+ aimbs_put16(&fr->data, mod->family);
+ aimbs_put16(&fr->data, mod->version);
+ aimbs_put16(&fr->data, mod->toolid);
+ aimbs_put16(&fr->data, mod->toolversion);
+ }
+ }
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Host Online (group 1, type 3)
+ *
+ * See comments in conn.c about how the group associations are supposed
+ * to work, and how they really work.
+ *
+ * This info probably doesn't even need to make it to the client.
+ *
+ * We don't actually call the client here. This starts off the connection
+ * initialization routine required by all AIM connections. The next time
+ * the client is called is the CONNINITDONE callback, which should be
+ * shortly after the rate information is acknowledged.
+ *
+ */
+static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ guint16 *families;
+ int famcount;
+
+
+ if (!(families = g_malloc(aim_bstream_empty(bs))))
+ return 0;
+
+ for (famcount = 0; aim_bstream_empty(bs); famcount++) {
+ families[famcount] = aimbs_get16(bs);
+ aim_conn_addgroup(rx->conn, families[famcount]);
+ }
+
+ g_free(families);
+
+
+ /*
+ * Next step is in the Host Versions handler.
+ *
+ * Note that we must send this before we request rates, since
+ * the format of the rate information depends on the versions we
+ * give it.
+ *
+ */
+ aim_setversions(sess, rx->conn);
+
+ return 1;
+}
+
+/* Service request (group 1, type 4) */
+int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid)
+{
+ return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
+}
+
+/* Redirect (group 1, type 5) */
+static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ struct aim_redirect_data redir;
+ aim_rxcallback_t userfunc;
+ aim_tlvlist_t *tlvlist;
+ aim_snac_t *origsnac = NULL;
+ int ret = 0;
+
+ memset(&redir, 0, sizeof(redir));
+
+ tlvlist = aim_readtlvchain(bs);
+
+ if (!aim_gettlv(tlvlist, 0x000d, 1) ||
+ !aim_gettlv(tlvlist, 0x0005, 1) ||
+ !aim_gettlv(tlvlist, 0x0006, 1)) {
+ aim_freetlvchain(&tlvlist);
+ return 0;
+ }
+
+ redir.group = aim_gettlv16(tlvlist, 0x000d, 1);
+ redir.ip = aim_gettlv_str(tlvlist, 0x0005, 1);
+ redir.cookie = (guint8 *)aim_gettlv_str(tlvlist, 0x0006, 1);
+
+ /* Fetch original SNAC so we can get csi if needed */
+ origsnac = aim_remsnac(sess, snac->id);
+
+ if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
+ struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
+
+ redir.chat.exchange = csi->exchange;
+ redir.chat.room = csi->name;
+ redir.chat.instance = csi->instance;
+ }
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, &redir);
+
+ g_free((void *)redir.ip);
+ g_free((void *)redir.cookie);
+
+ if (origsnac)
+ g_free(origsnac->data);
+ g_free(origsnac);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/* Request Rate Information. (group 1, type 6) */
+int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
+}
+
+/*
+ * OSCAR defines several 'rate classes'. Each class has seperate
+ * rate limiting properties (limit level, alert level, disconnect
+ * level, etc), and a set of SNAC family/type pairs associated with
+ * it. The rate classes, their limiting properties, and the definitions
+ * of which SNACs are belong to which class, are defined in the
+ * Rate Response packet at login to each host.
+ *
+ * Logically, all rate offenses within one class count against further
+ * offenses for other SNACs in the same class (ie, sending messages
+ * too fast will limit the number of user info requests you can send,
+ * since those two SNACs are in the same rate class).
+ *
+ * Since the rate classes are defined dynamically at login, the values
+ * below may change. But they seem to be fairly constant.
+ *
+ * Currently, BOS defines five rate classes, with the commonly used
+ * members as follows...
+ *
+ * Rate class 0x0001:
+ * - Everything thats not in any of the other classes
+ *
+ * Rate class 0x0002:
+ * - Buddy list add/remove
+ * - Permit list add/remove
+ * - Deny list add/remove
+ *
+ * Rate class 0x0003:
+ * - User information requests
+ * - Outgoing ICBMs
+ *
+ * Rate class 0x0004:
+ * - A few unknowns: 2/9, 2/b, and f/2
+ *
+ * Rate class 0x0005:
+ * - Chat room create
+ * - Outgoing chat ICBMs
+ *
+ * The only other thing of note is that class 5 (chat) has slightly looser
+ * limiting properties than class 3 (normal messages). But thats just a
+ * small bit of trivia for you.
+ *
+ * The last thing that needs to be learned about the rate limiting
+ * system is how the actual numbers relate to the passing of time. This
+ * seems to be a big mystery.
+ *
+ */
+
+static void rc_addclass(struct rateclass **head, struct rateclass *inrc)
+{
+ struct rateclass *rc, *rc2;
+
+ if (!(rc = g_malloc(sizeof(struct rateclass))))
+ return;
+
+ memcpy(rc, inrc, sizeof(struct rateclass));
+ rc->next = NULL;
+
+ for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next)
+ ;
+
+ if (!rc2)
+ *head = rc;
+ else
+ rc2->next = rc;
+
+ return;
+}
+
+static struct rateclass *rc_findclass(struct rateclass **head, guint16 id)
+{
+ struct rateclass *rc;
+
+ for (rc = *head; rc; rc = rc->next) {
+ if (rc->classid == id)
+ return rc;
+ }
+
+ return NULL;
+}
+
+static void rc_addpair(struct rateclass *rc, guint16 group, guint16 type)
+{
+ struct snacpair *sp, *sp2;
+
+ if (!(sp = g_new0(struct snacpair, 1)))
+ return;
+
+ sp->group = group;
+ sp->subtype = type;
+ sp->next = NULL;
+
+ for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next)
+ ;
+
+ if (!sp2)
+ rc->members = sp;
+ else
+ sp2->next = sp;
+
+ return;
+}
+
+/* Rate Parameters (group 1, type 7) */
+static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
+ guint16 numclasses, i;
+ aim_rxcallback_t userfunc;
+
+
+ /*
+ * First are the parameters for each rate class.
+ */
+ numclasses = aimbs_get16(bs);
+ for (i = 0; i < numclasses; i++) {
+ struct rateclass rc;
+
+ memset(&rc, 0, sizeof(struct rateclass));
+
+ rc.classid = aimbs_get16(bs);
+ rc.windowsize = aimbs_get32(bs);
+ rc.clear = aimbs_get32(bs);
+ rc.alert = aimbs_get32(bs);
+ rc.limit = aimbs_get32(bs);
+ rc.disconnect = aimbs_get32(bs);
+ rc.current = aimbs_get32(bs);
+ rc.max = aimbs_get32(bs);
+
+ /*
+ * The server will send an extra five bytes of parameters
+ * depending on the version we advertised in 1/17. If we
+ * didn't send 1/17 (evil!), then this will crash and you
+ * die, as it will default to the old version but we have
+ * the new version hardcoded here.
+ */
+ if (mod->version >= 3)
+ aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown));
+
+ rc_addclass(&ins->rates, &rc);
+ }
+
+ /*
+ * Then the members of each class.
+ */
+ for (i = 0; i < numclasses; i++) {
+ guint16 classid, count;
+ struct rateclass *rc;
+ int j;
+
+ classid = aimbs_get16(bs);
+ count = aimbs_get16(bs);
+
+ rc = rc_findclass(&ins->rates, classid);
+
+ for (j = 0; j < count; j++) {
+ guint16 group, subtype;
+
+ group = aimbs_get16(bs);
+ subtype = aimbs_get16(bs);
+
+ if (rc)
+ rc_addpair(rc, group, subtype);
+ }
+ }
+
+ /*
+ * We don't pass the rate information up to the client, as it really
+ * doesn't care. The information is stored in the connection, however
+ * so that we can do more fun stuff later (not really).
+ */
+
+ /*
+ * Last step in the conn init procedure is to acknowledge that we
+ * agree to these draconian limitations.
+ */
+ aim_rates_addparam(sess, rx->conn);
+
+ /*
+ * Finally, tell the client it's ready to go...
+ */
+ if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
+ userfunc(sess, rx);
+
+
+ return 1;
+}
+
+/* Add Rate Parameter (group 1, type 8) */
+int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ struct rateclass *rc;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
+
+ for (rc = ins->rates; rc; rc = rc->next)
+ aimbs_put16(&fr->data, rc->classid);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* Delete Rate Parameter (group 1, type 9) */
+int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ struct rateclass *rc;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid);
+
+ for (rc = ins->rates; rc; rc = rc->next)
+ aimbs_put16(&fr->data, rc->classid);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* Rate Change (group 1, type 0x0a) */
+static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ guint16 code, rateclass;
+ guint32 currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
+
+ code = aimbs_get16(bs);
+ rateclass = aimbs_get16(bs);
+
+ windowsize = aimbs_get32(bs);
+ clear = aimbs_get32(bs);
+ alert = aimbs_get32(bs);
+ limit = aimbs_get32(bs);
+ disconnect = aimbs_get32(bs);
+ currentavg = aimbs_get32(bs);
+ maxavg = aimbs_get32(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
+
+ return 0;
+}
+
+/*
+ * How Migrations work.
+ *
+ * The server sends a Server Pause message, which the client should respond to
+ * with a Server Pause Ack, which contains the families it needs on this
+ * connection. The server will send a Migration Notice with an IP address, and
+ * then disconnect. Next the client should open the connection and send the
+ * cookie. Repeat the normal login process and pretend this never happened.
+ *
+ * The Server Pause contains no data.
+ *
+ */
+
+/* Service Pause (group 1, type 0x0b) */
+static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx);
+
+ return 0;
+}
+
+/*
+ * Service Pause Acknowledgement (group 1, type 0x0c)
+ *
+ * It is rather important that aim_sendpauseack() gets called for the exact
+ * same connection that the Server Pause callback was called for, since
+ * libfaim extracts the data for the SNAC from the connection structure.
+ *
+ * Of course, if you don't do that, more bad things happen than just what
+ * libfaim can cause.
+ *
+ */
+int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ struct snacgroup *sg;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid);
+
+ /*
+ * This list should have all the groups that the original
+ * Host Online / Server Ready said this host supports. And
+ * we want them all back after the migration.
+ */
+ for (sg = ins->groups; sg; sg = sg->next)
+ aimbs_put16(&fr->data, sg->group);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* Service Resume (group 1, type 0x0d) */
+static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx);
+
+ return 0;
+}
+
+/* Request self-info (group 1, type 0x0e) */
+int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
+}
+
+/* Self User Info (group 1, type 0x0f) */
+static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ aim_userinfo_t userinfo;
+
+ aim_extractuserinfo(sess, bs, &userinfo);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, &userinfo);
+
+ return 0;
+}
+
+/* Evil Notification (group 1, type 0x10) */
+static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ guint16 newevil;
+ aim_userinfo_t userinfo;
+
+ memset(&userinfo, 0, sizeof(aim_userinfo_t));
+
+ newevil = aimbs_get16(bs);
+
+ if (aim_bstream_empty(bs))
+ aim_extractuserinfo(sess, bs, &userinfo);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, newevil, &userinfo);
+
+ return 0;
+}
+
+/*
+ * Idle Notification (group 1, type 0x11)
+ *
+ * Should set your current idle time in seconds. Note that this should
+ * never be called consecutively with a non-zero idle time. That makes
+ * OSCAR do funny things. Instead, just set it once you go idle, and then
+ * call it again with zero when you're back.
+ *
+ */
+int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, guint32 idletime)
+{
+ return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
+}
+
+/*
+ * Service Migrate (group 1, type 0x12)
+ *
+ * This is the final SNAC sent on the original connection during a migration.
+ * It contains the IP and cookie used to connect to the new server, and
+ * optionally a list of the SNAC groups being migrated.
+ *
+ */
+static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ int ret = 0;
+ guint16 groupcount, i;
+ aim_tlvlist_t *tl;
+ char *ip = NULL;
+ aim_tlv_t *cktlv;
+
+ /*
+ * Apparently there's some fun stuff that can happen right here. The
+ * migration can actually be quite selective about what groups it
+ * moves to the new server. When not all the groups for a connection
+ * are migrated, or they are all migrated but some groups are moved
+ * to a different server than others, it is called a bifurcated
+ * migration.
+ *
+ * Let's play dumb and not support that.
+ *
+ */
+ groupcount = aimbs_get16(bs);
+ for (i = 0; i < groupcount; i++) {
+ guint16 group;
+
+ group = aimbs_get16(bs);
+
+ do_error_dialog(sess->aux_data, "bifurcated migration unsupported", "Gaim");
+ }
+
+ tl = aim_readtlvchain(bs);
+
+ if (aim_gettlv(tl, 0x0005, 1))
+ ip = aim_gettlv_str(tl, 0x0005, 1);
+
+ cktlv = aim_gettlv(tl, 0x0006, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL);
+
+ aim_freetlvchain(&tl);
+ g_free(ip);
+
+ return ret;
+}
+
+/* Message of the Day (group 1, type 0x13) */
+static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ char *msg = NULL;
+ int ret = 0;
+ aim_tlvlist_t *tlvlist;
+ guint16 id;
+
+ /*
+ * Code.
+ *
+ * Valid values:
+ * 1 Mandatory upgrade
+ * 2 Advisory upgrade
+ * 3 System bulletin
+ * 4 Nothing's wrong ("top o the world" -- normal)
+ * 5 Lets-break-something.
+ *
+ */
+ id = aimbs_get16(bs);
+
+ /*
+ * TLVs follow
+ */
+ tlvlist = aim_readtlvchain(bs);
+
+ msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, id, msg);
+
+ g_free(msg);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Set privacy flags (group 1, type 0x14)
+ *
+ * Normally 0x03.
+ *
+ * Bit 1: Allows other AIM users to see how long you've been idle.
+ * Bit 2: Allows other AIM users to see how long you've been a member.
+ *
+ */
+int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, guint32 flags)
+{
+ return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
+}
+
+/*
+ * No-op (group 1, type 0x16)
+ *
+ * WinAIM sends these every 4min or so to keep the connection alive. Its not
+ * real necessary.
+ *
+ */
+int aim_nop(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
+}
+
+/*
+ * Set client versions (group 1, subtype 0x17)
+ *
+ * If you've seen the clientonline/clientready SNAC you're probably
+ * wondering what the point of this one is. And that point seems to be
+ * that the versions in the client online SNAC are sent too late for the
+ * server to be able to use them to change the protocol for the earlier
+ * login packets (client versions are sent right after Host Online is
+ * received, but client online versions aren't sent until quite a bit later).
+ * We can see them already making use of this by changing the format of
+ * the rate information based on what version of group 1 we advertise here.
+ *
+ */
+int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+ struct snacgroup *sg;
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!ins)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
+
+ /*
+ * Send only the versions that the server cares about (that it
+ * marked as supporting in the server ready SNAC).
+ */
+ for (sg = ins->groups; sg; sg = sg->next) {
+ aim_module_t *mod;
+
+ if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+ aimbs_put16(&fr->data, mod->family);
+ aimbs_put16(&fr->data, mod->version);
+ }
+ }
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/* Host versions (group 1, subtype 0x18) */
+static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int vercount;
+ guint8 *versions;
+
+ /* This is frivolous. (Thank you SmarterChild.) */
+ vercount = aim_bstream_empty(bs)/4;
+ versions = aimbs_getraw(bs, aim_bstream_empty(bs));
+ g_free(versions);
+
+ /*
+ * Now request rates.
+ */
+ aim_reqrates(sess, rx->conn);
+
+ return 1;
+}
+
+/*
+ * Subtype 0x001e - Extended Status
+ *
+ * Sets your ICQ status (available, away, do not disturb, etc.)
+ *
+ * These are the same TLVs seen in user info. You can
+ * also set 0x0008 and 0x000c.
+ */
+int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ aim_tlvlist_t *tl = NULL;
+ guint32 data;
+ int tlvlen;
+
+ data = AIM_ICQ_STATE_WEBAWARE | AIM_ICQ_STATE_HIDEIP | status; /* yay for error checking ;^) */
+
+ tlvlen = aim_addtlvtochain32(&tl, 0x0006, data);
+
+ printf("%d\n", tlvlen);
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+ aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+
+ aim_writetlvchain(&fr->data, &tl);
+ aim_freetlvchain(&tl);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * Starting this past week (26 Mar 2001, say), AOL has started sending
+ * this nice little extra SNAC. AFAIK, it has never been used until now.
+ *
+ * The request contains eight bytes. The first four are an offset, the
+ * second four are a length.
+ *
+ * The offset is an offset into aim.exe when it is mapped during execution
+ * on Win32. So far, AOL has only been requesting bytes in static regions
+ * of memory. (I won't put it past them to start requesting data in
+ * less static regions -- regions that are initialized at run time, but still
+ * before the client recieves this request.)
+ *
+ * When the client recieves the request, it adds it to the current ds
+ * (0x00400000) and dereferences it, copying the data into a buffer which
+ * it then runs directly through the MD5 hasher. The 16 byte output of
+ * the hash is then sent back to the server.
+ *
+ * If the client does not send any data back, or the data does not match
+ * the data that the specific client should have, the client will get the
+ * following message from "AOL Instant Messenger":
+ * "You have been disconnected from the AOL Instant Message Service (SM)
+ * for accessing the AOL network using unauthorized software. You can
+ * download a FREE, fully featured, and authorized client, here
+ * http://www.aol.com/aim/download2.html"
+ * The connection is then closed, recieving disconnect code 1, URL
+ * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
+ *
+ * Note, however, that numerous inconsistencies can cause the above error,
+ * not just sending back a bad hash. Do not immediatly suspect this code
+ * if you get disconnected. AOL and the open/free software community have
+ * played this game for a couple years now, generating the above message
+ * on numerous ocassions.
+ *
+ * Anyway, neener. We win again.
+ *
+ */
+/* Client verification (group 1, subtype 0x1f) */
+static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ aim_rxcallback_t userfunc;
+ guint32 offset, len;
+ aim_tlvlist_t *list;
+ char *modname;
+
+ offset = aimbs_get32(bs);
+ len = aimbs_get32(bs);
+ list = aim_readtlvchain(bs);
+
+ modname = aim_gettlv_str(list, 0x0001, 1);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, offset, len, modname);
+
+ g_free(modname);
+ aim_freetlvchain(&list);
+
+ return 0;
+}
+
+/* Client verification reply (group 1, subtype 0x20) */
+int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid);
+ aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */
+
+ if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
+
+ aimbs_putraw(&fr->data, buf, 0x10);
+
+ } else if (buf && (len > 0)) { /* use input buffer */
+ md5_state_t state;
+ md5_byte_t digest[0x10];
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)buf, len);
+ md5_finish(&state, digest);
+
+ aimbs_putraw(&fr->data, (guint8 *)digest, 0x10);
+
+ } else if (len == 0) { /* no length, just hash NULL (buf is optional) */
+ md5_state_t state;
+ guint8 nil = '\0';
+ md5_byte_t digest[0x10];
+
+ /*
+ * These MD5 routines are stupid in that you have to have
+ * at least one append. So thats why this doesn't look
+ * real logical.
+ */
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)&nil, 0);
+ md5_finish(&state, digest);
+
+ aimbs_putraw(&fr->data, (guint8 *)digest, 0x10);
+
+ } else {
+
+ /*
+ * This data is correct for AIM 3.5.1670.
+ *
+ * Using these blocks is as close to "legal" as you can get
+ * without using an AIM binary.
+ *
+ */
+ if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
+
+#if 1 /* with "AnrbnrAqhfzcd" */
+ aimbs_put32(&fr->data, 0x44a95d26);
+ aimbs_put32(&fr->data, 0xd2490423);
+ aimbs_put32(&fr->data, 0x93b8821f);
+ aimbs_put32(&fr->data, 0x51c54b01);
+#else /* no filename */
+ aimbs_put32(&fr->data, 0x1df8cbae);
+ aimbs_put32(&fr->data, 0x5523b839);
+ aimbs_put32(&fr->data, 0xa0e10db3);
+ aimbs_put32(&fr->data, 0xa46d3b39);
+#endif
+
+ } else if ((offset == 0x00001000) && (len == 0x00000000)) {
+
+ aimbs_put32(&fr->data, 0xd41d8cd9);
+ aimbs_put32(&fr->data, 0x8f00b204);
+ aimbs_put32(&fr->data, 0xe9800998);
+ aimbs_put32(&fr->data, 0xecf8427e);
+
+ } else
+ do_error_dialog(sess->aux_data, "WARNING: unknown hash request", "Gaim");
+
+ }
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0003)
+ return hostonline(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0005)
+ return redirect(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0007)
+ return rateresp(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000a)
+ return ratechange(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000b)
+ return serverpause(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000d)
+ return serverresume(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x000f)
+ return selfinfo(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0010)
+ return evilnotify(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0012)
+ return migrate(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0013)
+ return motd(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x0018)
+ return hostversions(sess, mod, rx, snac, bs);
+ else if (snac->subtype == 0x001f)
+ return memrequest(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int general_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x0001;
+ mod->version = 0x0003;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "general", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
+
diff --git a/protocols/oscar/snac.c b/protocols/oscar/snac.c
new file mode 100644
index 00000000..e2bac179
--- /dev/null
+++ b/protocols/oscar/snac.c
@@ -0,0 +1,145 @@
+/*
+ *
+ * Various SNAC-related dodads...
+ *
+ * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added
+ * whenever a new SNAC is sent and it should remain in the list until the
+ * response for it has been receieved.
+ *
+ * cleansnacs() should be called periodically by the client in order
+ * to facilitate the aging out of unreplied-to SNACs. This can and does
+ * happen, so it should be handled.
+ *
+ */
+
+#include <aim.h>
+
+/*
+ * Called from aim_session_init() to initialize the hash.
+ */
+void aim_initsnachash(aim_session_t *sess)
+{
+ int i;
+
+ for (i = 0; i < AIM_SNAC_HASH_SIZE; i++)
+ sess->snac_hash[i] = NULL;
+
+ return;
+}
+
+aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
+{
+ aim_snac_t snac;
+
+ snac.id = sess->snacid_next++;
+ snac.family = family;
+ snac.type = type;
+ snac.flags = flags;
+
+ if (datalen) {
+ if (!(snac.data = g_malloc(datalen)))
+ return 0; /* er... */
+ memcpy(snac.data, data, datalen);
+ } else
+ snac.data = NULL;
+
+ return aim_newsnac(sess, &snac);
+}
+
+/*
+ * Clones the passed snac structure and caches it in the
+ * list/hash.
+ */
+aim_snacid_t aim_newsnac(aim_session_t *sess, aim_snac_t *newsnac)
+{
+ aim_snac_t *snac;
+ int index;
+
+ if (!newsnac)
+ return 0;
+
+ if (!(snac = g_malloc(sizeof(aim_snac_t))))
+ return 0;
+ memcpy(snac, newsnac, sizeof(aim_snac_t));
+ snac->issuetime = time(NULL);
+
+ index = snac->id % AIM_SNAC_HASH_SIZE;
+
+ snac->next = (aim_snac_t *)sess->snac_hash[index];
+ sess->snac_hash[index] = (void *)snac;
+
+ return snac->id;
+}
+
+/*
+ * Finds a snac structure with the passed SNAC ID,
+ * removes it from the list/hash, and returns a pointer to it.
+ *
+ * The returned structure must be freed by the caller.
+ *
+ */
+aim_snac_t *aim_remsnac(aim_session_t *sess, aim_snacid_t id)
+{
+ aim_snac_t *cur, **prev;
+ int index;
+
+ index = id % AIM_SNAC_HASH_SIZE;
+
+ for (prev = (aim_snac_t **)&sess->snac_hash[index]; (cur = *prev); ) {
+ if (cur->id == id) {
+ *prev = cur->next;
+ return cur;
+ } else
+ prev = &cur->next;
+ }
+
+ return cur;
+}
+
+/*
+ * This is for cleaning up old SNACs that either don't get replies or
+ * a reply was never received for. Garabage collection. Plain and simple.
+ *
+ * maxage is the _minimum_ age in seconds to keep SNACs.
+ *
+ */
+void aim_cleansnacs(aim_session_t *sess, int maxage)
+{
+ int i;
+
+ for (i = 0; i < AIM_SNAC_HASH_SIZE; i++) {
+ aim_snac_t *cur, **prev;
+ time_t curtime;
+
+ if (!sess->snac_hash[i])
+ continue;
+
+ curtime = time(NULL); /* done here in case we waited for the lock */
+
+ for (prev = (aim_snac_t **)&sess->snac_hash[i]; (cur = *prev); ) {
+ if ((curtime - cur->issuetime) > maxage) {
+
+ *prev = cur->next;
+
+ /* XXX should we have destructors here? */
+ g_free(cur->data);
+ g_free(cur);
+
+ } else
+ prev = &cur->next;
+ }
+ }
+
+ return;
+}
+
+int aim_putsnac(aim_bstream_t *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid)
+{
+
+ aimbs_put16(bs, family);
+ aimbs_put16(bs, subtype);
+ aimbs_put16(bs, flags);
+ aimbs_put32(bs, snacid);
+
+ return 10;
+}
diff --git a/protocols/oscar/ssi.c b/protocols/oscar/ssi.c
new file mode 100644
index 00000000..76b5b427
--- /dev/null
+++ b/protocols/oscar/ssi.c
@@ -0,0 +1,1523 @@
+/*
+ * Server-Side/Stored Information.
+ *
+ * Relatively new facility that allows storing of certain types of information,
+ * such as a users buddy list, permit/deny list, and permit/deny preferences,
+ * to be stored on the server, so that they can be accessed from any client.
+ *
+ * We keep a copy of the ssi data in sess->ssi, because the data needs to be
+ * accessed for various reasons. So all the "aim_ssi_itemlist_bleh" functions
+ * near the top just manage the local data.
+ *
+ * The SNAC sending and receiving functions are lower down in the file, and
+ * they're simpler. They are in the order of the subtypes they deal with,
+ * starting with the request rights function (subtype 0x0002), then parse
+ * rights (subtype 0x0003), then--well, you get the idea.
+ *
+ * This is entirely too complicated.
+ * You don't know the half of it.
+ *
+ * XXX - Test for memory leaks
+ * XXX - Better parsing of rights, and use the rights info to limit adds
+ *
+ */
+
+#include <aim.h>
+#include "ssi.h"
+
+/**
+ * Locally add a new item to the given item list.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param parent A pointer to the parent group, or NULL if the item should have no
+ * parent group (ie. the group ID# should be 0).
+ * @param name A null terminated string of the name of the new item, or NULL if the
+ * item should have no name.
+ * @param type The type of the item, 0x0001 for a contact, 0x0002 for a group, etc.
+ * @return The newly created item.
+ */
+static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, char *name, guint16 type)
+{
+ int i;
+ struct aim_ssi_item *cur, *newitem;
+
+ if (!(newitem = g_new0(struct aim_ssi_item, 1)))
+ return NULL;
+
+ /* Set the name */
+ if (name) {
+ if (!(newitem->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)))) {
+ g_free(newitem);
+ return NULL;
+ }
+ strcpy(newitem->name, name);
+ } else
+ newitem->name = NULL;
+
+ /* Set the group ID# and the buddy ID# */
+ newitem->gid = 0x0000;
+ newitem->bid = 0x0000;
+ if (type == AIM_SSI_TYPE_GROUP) {
+ if (name)
+ do {
+ newitem->gid += 0x0001;
+ for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+ if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid))
+ i=1;
+ } while (i);
+ } else {
+ if (parent)
+ newitem->gid = parent->gid;
+ do {
+ newitem->bid += 0x0001;
+ for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+ if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid))
+ i=1;
+ } while (i);
+ }
+
+ /* Set the rest */
+ newitem->type = type;
+ newitem->data = NULL;
+ newitem->next = *list;
+ *list = newitem;
+
+ return newitem;
+}
+
+/**
+ * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param parentgroup A pointer to the group who's additional data you want to rebuild.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
+{
+ int newlen; //, i;
+ struct aim_ssi_item *cur;
+
+ /* Free the old additional data */
+ if (parentgroup->data) {
+ aim_freetlvchain((aim_tlvlist_t **)&parentgroup->data);
+ parentgroup->data = NULL;
+ }
+
+ /* Find the length for the new additional data */
+ newlen = 0;
+ if (parentgroup->gid == 0x0000) {
+ for (cur=*list; cur; cur=cur->next)
+ if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
+ newlen += 2;
+ } else {
+ for (cur=*list; cur; cur=cur->next)
+ if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+ newlen += 2;
+ }
+
+ /* Rebuild the additional data */
+ if (newlen>0) {
+ guint8 *newdata;
+
+ if (!(newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8))))
+ return -ENOMEM;
+ newlen = 0;
+ if (parentgroup->gid == 0x0000) {
+ for (cur=*list; cur; cur=cur->next)
+ if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
+ newlen += aimutil_put16(newdata+newlen, cur->gid);
+ } else {
+ for (cur=*list; cur; cur=cur->next)
+ if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+ newlen += aimutil_put16(newdata+newlen, cur->bid);
+ }
+ aim_addtlvtochain_raw((aim_tlvlist_t **)&(parentgroup->data), 0x00c8, newlen, newdata);
+
+ g_free(newdata);
+ }
+
+ return 0;
+}
+
+/**
+ * Locally free all of the stored buddy list information.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_freelist(aim_session_t *sess)
+{
+ struct aim_ssi_item *cur, *delitem;
+
+ cur = sess->ssi.items;
+ while (cur) {
+ if (cur->name) g_free(cur->name);
+ if (cur->data) aim_freetlvchain((aim_tlvlist_t **)&cur->data);
+ delitem = cur;
+ cur = cur->next;
+ g_free(delitem);
+ }
+
+ sess->ssi.items = NULL;
+ sess->ssi.revision = 0;
+ sess->ssi.timestamp = (time_t)0;
+
+ return 0;
+}
+
+/**
+ * Locally find an item given a group ID# and a buddy ID#.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gid The group ID# of the desired item.
+ * @param bid The buddy ID# of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
+{
+ struct aim_ssi_item *cur;
+ for (cur=list; cur; cur=cur->next)
+ if ((cur->gid == gid) && (cur->bid == bid))
+ return cur;
+ return NULL;
+}
+
+/**
+ * Locally find an item given a group name, screen name, and type. If group name
+ * and screen name are null, then just return the first item of the given type.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group name of the desired item.
+ * @param bn The buddy name of the desired item.
+ * @param type The type of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type)
+{
+ struct aim_ssi_item *cur;
+ if (!list)
+ return NULL;
+
+ if (gn && sn) { /* For finding buddies in groups */
+ for (cur=list; cur; cur=cur->next)
+ if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+ struct aim_ssi_item *curg;
+ for (curg=list; curg; curg=curg->next)
+ if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
+ return cur;
+ }
+
+ } else if (sn) { /* For finding groups, permits, denies, and ignores */
+ for (cur=list; cur; cur=cur->next)
+ if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn)))
+ return cur;
+
+ /* For stuff without names--permit deny setting, visibility mask, etc. */
+ } else for (cur=list; cur; cur=cur->next) {
+ if (cur->type == type)
+ return cur;
+ }
+
+ return NULL;
+}
+
+/**
+ * Locally find the parent item of the given buddy name.
+ *
+ * @param list A pointer to the current list of items.
+ * @param bn The buddy name of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
+{
+ struct aim_ssi_item *cur, *curg;
+ if (!list || !sn)
+ return NULL;
+ if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY)))
+ return NULL;
+ for (curg=list; curg; curg=curg->next)
+ if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid))
+ return curg;
+ return NULL;
+}
+
+/**
+ * Locally find the permit/deny setting item, and return the setting.
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current SSI permit deny setting, or 0 if no setting was found.
+ */
+int aim_ssi_getpermdeny(struct aim_ssi_item *list)
+{
+ struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
+ if (cur) {
+ aim_tlvlist_t *tlvlist = cur->data;
+ if (tlvlist) {
+ aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
+ if (tlv && tlv->value)
+ return aimutil_get8(tlv->value);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Locally find the presence flag item, and return the setting. The returned setting is a
+ * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines
+ * in aim.h
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current visibility mask.
+ */
+guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
+{
+ struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
+ if (cur) {
+ aim_tlvlist_t *tlvlist = cur->data;
+ if (tlvlist) {
+ aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
+ if (tlv && tlv->length)
+ return aimutil_get32(tlv->value);
+ }
+ }
+ return 0xFFFFFFFF;
+}
+
+/**
+ * Add the given packet to the holding queue. We totally need to send SSI SNACs one at
+ * a time, so we have a local queue where packets get put before they are sent, and
+ * then we send stuff one at a time, nice and orderly-like.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param fr The newly created SNAC that you want to send.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
+{
+ aim_frame_t *cur;
+
+ if (!sess || !conn || !fr)
+ return -EINVAL;
+
+ fr->next = NULL;
+ if (sess->ssi.holding_queue == NULL) {
+ sess->ssi.holding_queue = fr;
+ if (!sess->ssi.waiting_for_ack)
+ aim_ssi_modbegin(sess, conn);
+ } else {
+ for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) ;
+ cur->next = fr;
+ }
+
+ return 0;
+}
+
+/**
+ * Send the next SNAC from the holding queue. This is called
+ * automatically when an ack from an add, mod, or del is received.
+ * If the queue is empty, it sends the modend SNAC.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *cur;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ if (!sess->ssi.waiting_for_ack) {
+ if (sess->ssi.holding_queue) {
+ sess->ssi.waiting_for_ack = 1;
+ cur = sess->ssi.holding_queue->next;
+ sess->ssi.holding_queue->next = NULL;
+ aim_tx_enqueue(sess, sess->ssi.holding_queue);
+ sess->ssi.holding_queue = cur;
+ } else
+ aim_ssi_modend(sess, conn);
+ }
+
+ return 0;
+}
+
+/**
+ * Send SNACs necessary to remove all SSI data from the server list,
+ * and then free the local copy as well.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn)
+{
+ int num;
+ struct aim_ssi_item *cur, **items;
+
+ for (cur=sess->ssi.items, num=0; cur; cur=cur->next)
+ num++;
+
+ if (!(items = g_new0(struct aim_ssi_item *, num)))
+ return -ENOMEM;
+
+ for (cur=sess->ssi.items, num=0; cur; cur=cur->next) {
+ items[num] = cur;
+ num++;
+ }
+
+ aim_ssi_addmoddel(sess, conn, items, num, AIM_CB_SSI_DEL);
+ g_free(items);
+ aim_ssi_dispatch(sess, conn);
+ aim_ssi_freelist(sess);
+
+ return 0;
+}
+
+/**
+ * This "cleans" the ssi list. It does a few things, with the intent of making
+ * sure there ain't nothin' wrong with your SSI.
+ * -Make sure all buddies are in a group, and all groups have the correct
+ * additional data.
+ * -Make sure there are no empty groups in the list. While there is nothing
+ * wrong empty groups in the SSI, it's wiser to not have them.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)
+{
+ unsigned int i;
+ struct aim_ssi_item *cur, *parentgroup;
+
+ /* Make sure we actually need to clean out the list */
+ for (cur=sess->ssi.items, i=0; cur && !i; cur=cur->next)
+ /* Any buddies directly in the master group */
+ if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000))
+ i++;
+ if (!i)
+ return 0;
+
+ /* Remove all the additional data from all groups */
+ for (cur=sess->ssi.items; cur; cur=cur->next)
+ if ((cur->data) && (cur->type == AIM_SSI_TYPE_GROUP)) {
+ aim_freetlvchain((aim_tlvlist_t **)&cur->data);
+ cur->data = NULL;
+ }
+
+ /* If there are buddies directly in the master group, make sure */
+ /* there is a group to put them in. Any group, any group at all. */
+ for (cur=sess->ssi.items; ((cur) && ((cur->type != AIM_SSI_TYPE_BUDDY) || (cur->gid != 0x0000))); cur=cur->next);
+ if (!cur) {
+ for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);
+ if (!parentgroup) {
+ char *newgroup;
+ newgroup = (char*)g_malloc(strlen("Unknown")*sizeof(char));
+ strcpy(newgroup, "Unknown");
+ aim_ssi_addgroups(sess, conn, &newgroup, 1);
+ }
+ }
+
+ /* Set parentgroup equal to any arbitray group */
+ for (parentgroup=sess->ssi.items; parentgroup->gid==0x0000 || parentgroup->type!=AIM_SSI_TYPE_GROUP; parentgroup=parentgroup->next);
+
+ /* If there are any buddies directly in the master group, put them in a real group */
+ for (cur=sess->ssi.items; cur; cur=cur->next)
+ if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) {
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_DEL);
+ cur->gid = parentgroup->gid;
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+ }
+
+ /* Rebuild additional data for all groups */
+ for (parentgroup=sess->ssi.items; parentgroup; parentgroup=parentgroup->next)
+ if (parentgroup->type == AIM_SSI_TYPE_GROUP)
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+ /* Send a mod snac for all groups */
+ i = 0;
+ for (cur=sess->ssi.items; cur; cur=cur->next)
+ if (cur->type == AIM_SSI_TYPE_GROUP)
+ i++;
+ if (i > 0) {
+ /* Allocate an array of pointers to each of the groups */
+ struct aim_ssi_item **groups;
+ if (!(groups = g_new0(struct aim_ssi_item *, i)))
+ return -ENOMEM;
+
+ for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
+ if (cur->type == AIM_SSI_TYPE_GROUP)
+ groups[i] = cur;
+
+ aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_MOD);
+ g_free(groups);
+ }
+
+ /* Send a del snac for any empty groups */
+ i = 0;
+ for (cur=sess->ssi.items; cur; cur=cur->next)
+ if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
+ i++;
+ if (i > 0) {
+ /* Allocate an array of pointers to each of the groups */
+ struct aim_ssi_item **groups;
+ if (!(groups = g_new0(struct aim_ssi_item *, i)))
+ return -ENOMEM;
+
+ for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
+ if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
+ groups[i] = cur;
+
+ aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_DEL);
+ g_free(groups);
+ }
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Add an array of screen names to the given group.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn The name of the group to which you want to add these names.
+ * @param sn An array of null terminated strings of the names you want to add.
+ * @param num The number of screen names you are adding (size of the sn array).
+ * @param flags 1 - Add with TLV(0x66)
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags)
+{
+ struct aim_ssi_item *parentgroup, **newitems;
+ guint16 i;
+
+ if (!sess || !conn || !gn || !sn || !num)
+ return -EINVAL;
+
+ /* Look up the parent group */
+ if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
+ aim_ssi_addgroups(sess, conn, &gn, 1);
+ if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
+ return -ENOMEM;
+ }
+
+ /* Allocate an array of pointers to each of the new items */
+ if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+ return -ENOMEM;
+
+ /* Add items to the local list, and index them in the array */
+ for (i=0; i<num; i++)
+ if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
+ g_free(newitems);
+ return -ENOMEM;
+ } else if (flags & 1) {
+ aim_tlvlist_t *tl = NULL;
+ aim_addtlvtochain_noval(&tl, 0x66);
+ newitems[i]->data = tl;
+ }
+
+ /* Send the add item SNAC */
+ if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+ g_free(newitems);
+ return -i;
+ }
+
+ /* Free the array of pointers to each of the new items */
+ g_free(newitems);
+
+ /* Rebuild the additional data in the parent group */
+ if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
+ return i;
+
+ /* Send the mod item SNAC */
+ if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD )))
+ return i;
+
+ /* Begin sending SSI SNACs */
+ if (!(i = aim_ssi_dispatch(sess, conn)))
+ return i;
+
+ return 0;
+}
+
+/**
+ * Add the master group (the group containing all groups). This is called by
+ * aim_ssi_addgroups, if necessary.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
+{
+ struct aim_ssi_item *newitem;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ /* Add the item to the local list, and keep a pointer to it */
+ if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP)))
+ return -ENOMEM;
+
+ /* If there are any existing groups (technically there shouldn't be, but */
+ /* just in case) then add their group ID#'s to the additional data */
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
+
+ /* Send the add item SNAC */
+ aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Add an array of groups to the list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn An array of null terminated strings of the names you want to add.
+ * @param num The number of groups names you are adding (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
+{
+ struct aim_ssi_item *parentgroup, **newitems;
+ guint16 i;
+
+ if (!sess || !conn || !gn || !num)
+ return -EINVAL;
+
+ /* Look up the parent group */
+ if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
+ aim_ssi_addmastergroup(sess, conn);
+ if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+ return -ENOMEM;
+ }
+
+ /* Allocate an array of pointers to each of the new items */
+ if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+ return -ENOMEM;
+
+ /* Add items to the local list, and index them in the array */
+ for (i=0; i<num; i++)
+ if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) {
+ g_free(newitems);
+ return -ENOMEM;
+ }
+
+ /* Send the add item SNAC */
+ if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+ g_free(newitems);
+ return -i;
+ }
+
+ /* Free the array of pointers to each of the new items */
+ g_free(newitems);
+
+ /* Rebuild the additional data in the parent group */
+ if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
+ return i;
+
+ /* Send the mod item SNAC */
+ if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
+ return i;
+
+ /* Begin sending SSI SNACs */
+ if (!(i = aim_ssi_dispatch(sess, conn)))
+ return i;
+
+ return 0;
+}
+
+/**
+ * Add an array of a certain type of item to the list. This can be used for
+ * permit buddies, deny buddies, ICQ's ignore buddies, and probably other
+ * types, also.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param sn An array of null terminated strings of the names you want to add.
+ * @param num The number of groups names you are adding (size of the sn array).
+ * @param type The type of item you want to add. See the AIM_SSI_TYPE_BLEH
+ * #defines in aim.h.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type)
+{
+ struct aim_ssi_item **newitems;
+ guint16 i;
+
+ if (!sess || !conn || !sn || !num)
+ return -EINVAL;
+
+ /* Allocate an array of pointers to each of the new items */
+ if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+ return -ENOMEM;
+
+ /* Add items to the local list, and index them in the array */
+ for (i=0; i<num; i++)
+ if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
+ g_free(newitems);
+ return -ENOMEM;
+ }
+
+ /* Send the add item SNAC */
+ if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+ g_free(newitems);
+ return -i;
+ }
+
+ /* Free the array of pointers to each of the new items */
+ g_free(newitems);
+
+ /* Begin sending SSI SNACs */
+ if (!(i = aim_ssi_dispatch(sess, conn)))
+ return i;
+
+ return 0;
+}
+
+/**
+ * Move a buddy from one group to another group. This basically just deletes the
+ * buddy and re-adds it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param oldgn The group that the buddy is currently in.
+ * @param newgn The group that the buddy should be moved in to.
+ * @param sn The name of the buddy to be moved.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn)
+{
+ struct aim_ssi_item **groups, *buddy, *cur;
+ guint16 i;
+
+ if (!sess || !conn || !oldgn || !newgn || !sn)
+ return -EINVAL;
+
+ /* Look up the buddy */
+ if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY)))
+ return -ENOMEM;
+
+ /* Allocate an array of pointers to the two groups */
+ if (!(groups = g_new0(struct aim_ssi_item *, 2)))
+ return -ENOMEM;
+
+ /* Look up the old parent group */
+ if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
+ g_free(groups);
+ return -ENOMEM;
+ }
+
+ /* Look up the new parent group */
+ if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
+ g_free(groups);
+ return -ENOMEM;
+ }
+
+ /* Send the delete item SNAC */
+ aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL);
+
+ /* Put the buddy in the new group */
+ buddy->gid = groups[1]->gid;
+
+ /* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */
+ buddy->bid = 0;
+ do {
+ buddy->bid += 0x0001;
+ for (cur=sess->ssi.items, i=0; ((cur) && (!i)); cur=cur->next)
+ if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && aim_sncmp(cur->name, buddy->name))
+ i=1;
+ } while (i);
+
+ /* Rebuild the additional data in the two parent groups */
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
+
+ /* Send the add item SNAC */
+ aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD);
+
+ /* Send the mod item SNAC */
+ aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD);
+
+ /* Free the temporary array */
+ g_free(groups);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Delete an array of screen names from the given group.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn The name of the group from which you want to delete these names.
+ * @param sn An array of null terminated strings of the names you want to delete.
+ * @param num The number of screen names you are deleting (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num)
+{
+ struct aim_ssi_item *cur, *parentgroup, **delitems;
+ int i;
+
+ if (!sess || !conn || !gn || !sn || !num)
+ return -EINVAL;
+
+ /* Look up the parent group */
+ if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
+ return -EINVAL;
+
+ /* Allocate an array of pointers to each of the items to be deleted */
+ delitems = g_new0(struct aim_ssi_item *, num);
+
+ /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+ for (i=0; i<num; i++) {
+ if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
+ g_free(delitems);
+ return -EINVAL;
+ }
+
+ /* Remove the delitems from the item list */
+ if (sess->ssi.items == delitems[i]) {
+ sess->ssi.items = sess->ssi.items->next;
+ } else {
+ for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+ if (cur->next)
+ cur->next = cur->next->next;
+ }
+ }
+
+ /* Send the del item SNAC */
+ aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+ /* Free the items */
+ for (i=0; i<num; i++) {
+ if (delitems[i]->name)
+ g_free(delitems[i]->name);
+ if (delitems[i]->data)
+ aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+ g_free(delitems[i]);
+ }
+ g_free(delitems);
+
+ /* Rebuild the additional data in the parent group */
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+ /* Send the mod item SNAC */
+ aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
+
+ /* Delete the group, but only if it's empty */
+ if (!parentgroup->data)
+ aim_ssi_delgroups(sess, conn, &parentgroup->name, 1);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Delete the master group from the item list. There can be only one.
+ * Er, so just find the one master group and delete it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn)
+{
+ struct aim_ssi_item *cur, *delitem;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ /* Make delitem a pointer to the aim_ssi_item to be deleted */
+ if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+ return -EINVAL;
+
+ /* Remove delitem from the item list */
+ if (sess->ssi.items == delitem) {
+ sess->ssi.items = sess->ssi.items->next;
+ } else {
+ for (cur=sess->ssi.items; (cur->next && (cur->next!=delitem)); cur=cur->next);
+ if (cur->next)
+ cur->next = cur->next->next;
+ }
+
+ /* Send the del item SNAC */
+ aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL);
+
+ /* Free the item */
+ if (delitem->name)
+ g_free(delitem->name);
+ if (delitem->data)
+ aim_freetlvchain((aim_tlvlist_t **)&delitem->data);
+ g_free(delitem);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Delete an array of groups.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn An array of null terminated strings of the groups you want to delete.
+ * @param num The number of groups you are deleting (size of the gn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) {
+ struct aim_ssi_item *cur, *parentgroup, **delitems;
+ int i;
+
+ if (!sess || !conn || !gn || !num)
+ return -EINVAL;
+
+ /* Look up the parent group */
+ if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+ return -EINVAL;
+
+ /* Allocate an array of pointers to each of the items to be deleted */
+ delitems = g_new0(struct aim_ssi_item *, num);
+
+ /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+ for (i=0; i<num; i++) {
+ if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
+ g_free(delitems);
+ return -EINVAL;
+ }
+
+ /* Remove the delitems from the item list */
+ if (sess->ssi.items == delitems[i]) {
+ sess->ssi.items = sess->ssi.items->next;
+ } else {
+ for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+ if (cur->next)
+ cur->next = cur->next->next;
+ }
+ }
+
+ /* Send the del item SNAC */
+ aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+ /* Free the items */
+ for (i=0; i<num; i++) {
+ if (delitems[i]->name)
+ g_free(delitems[i]->name);
+ if (delitems[i]->data)
+ aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+ g_free(delitems[i]);
+ }
+ g_free(delitems);
+
+ /* Rebuild the additional data in the parent group */
+ aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+ /* Send the mod item SNAC */
+ aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
+
+ /* Delete the group, but only if it's empty */
+ if (!parentgroup->data)
+ aim_ssi_delmastergroup(sess, conn);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Delete an array of a certain type of item from the list. This can be
+ * used for permit buddies, deny buddies, ICQ's ignore buddies, and
+ * probably other types, also.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param sn An array of null terminated strings of the items you want to delete.
+ * @param num The number of items you are deleting (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type) {
+ struct aim_ssi_item *cur, **delitems;
+ int i;
+
+ if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY))
+ return -EINVAL;
+
+ /* Allocate an array of pointers to each of the items to be deleted */
+ delitems = g_new0(struct aim_ssi_item *, num);
+
+ /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+ for (i=0; i<num; i++) {
+ if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
+ g_free(delitems);
+ return -EINVAL;
+ }
+
+ /* Remove the delitems from the item list */
+ if (sess->ssi.items == delitems[i]) {
+ sess->ssi.items = sess->ssi.items->next;
+ } else {
+ for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+ if (cur->next)
+ cur->next = cur->next->next;
+ }
+ }
+
+ /* Send the del item SNAC */
+ aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+ /* Free the items */
+ for (i=0; i<num; i++) {
+ if (delitems[i]->name)
+ g_free(delitems[i]->name);
+ if (delitems[i]->data)
+ aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+ g_free(delitems[i]);
+ }
+ g_free(delitems);
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Stores your permit/deny setting on the server, and starts using it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param permdeny Your permit/deny setting. Can be one of the following:
+ * 1 - Allow all users
+ * 2 - Block all users
+ * 3 - Allow only the users below
+ * 4 - Block only the users below
+ * 5 - Allow only users on my buddy list
+ * @param vismask A bitmask of the class of users to whom you want to be
+ * visible. See the AIM_FLAG_BLEH #defines in aim.h
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask) {
+ struct aim_ssi_item *cur; //, *tmp;
+// guint16 j;
+ aim_tlv_t *tlv;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ /* Look up the permit/deny settings item */
+ cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO);
+
+ if (cur) {
+ /* The permit/deny item exists */
+ if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) {
+ /* Just change the value of the x00ca TLV */
+ if (tlv->length != 1) {
+ tlv->length = 1;
+ g_free(tlv->value);
+ tlv->value = (guint8 *)g_malloc(sizeof(guint8));
+ }
+ tlv->value[0] = permdeny;
+ } else {
+ /* Need to add the x00ca TLV to the TLV chain */
+ aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
+ }
+
+ if (cur->data && (tlv = aim_gettlv(cur->data, 0x00cb, 1))) {
+ /* Just change the value of the x00cb TLV */
+ if (tlv->length != 4) {
+ tlv->length = 4;
+ g_free(tlv->value);
+ tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
+ }
+ aimutil_put32(tlv->value, vismask);
+ } else {
+ /* Need to add the x00cb TLV to the TLV chain */
+ aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
+ }
+
+ /* Send the mod item SNAC */
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
+ } else {
+ /* Need to add the permit/deny item */
+ if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
+ return -ENOMEM;
+ aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
+ aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+ }
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/**
+ * Stores your setting for whether you should show up as idle or not.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param presence I think it's a bitmask, but I only know what one of the bits is:
+ * 0x00000400 - Allow others to see your idle time
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence) {
+ struct aim_ssi_item *cur; //, *tmp;
+// guint16 j;
+ aim_tlv_t *tlv;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ /* Look up the item */
+ cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
+
+ if (cur) {
+ /* The item exists */
+ if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
+ /* Just change the value of the x00c9 TLV */
+ if (tlv->length != 4) {
+ tlv->length = 4;
+ g_free(tlv->value);
+ tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
+ }
+ aimutil_put32(tlv->value, presence);
+ } else {
+ /* Need to add the x00c9 TLV to the TLV chain */
+ aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
+ }
+
+ /* Send the mod item SNAC */
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
+ } else {
+ /* Need to add the item */
+ if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
+ return -ENOMEM;
+ aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
+ aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+ }
+
+ /* Begin sending SSI SNACs */
+ aim_ssi_dispatch(sess, conn);
+
+ return 0;
+}
+
+/*
+ * Request SSI Rights.
+ */
+int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
+}
+
+/*
+ * SSI Rights Information.
+ */
+static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx);
+
+ return ret;
+}
+
+/*
+ * Request SSI Data.
+ *
+ * The data will only be sent if it is newer than the posted local
+ * timestamp and revision.
+ *
+ * Note that the client should never increment the revision, only the server.
+ *
+ */
+int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
+ aimbs_put32(&fr->data, localstamp);
+ aimbs_put16(&fr->data, localrev);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0);
+
+ aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid);
+
+ aim_tx_enqueue(sess, fr);
+
+ return 0;
+}
+
+/*
+ * SSI Data.
+ */
+static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+ struct aim_ssi_item *cur = NULL;
+ guint8 fmtver; /* guess */
+ guint16 revision;
+ guint32 timestamp;
+
+ /* When you set the version for the SSI family to 2-4, the beginning of this changes.
+ * Instead of the version and then the revision, there is "0x0006" and then a type
+ * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier. Also,
+ * the SNAC flags go from 0x0000 to 0x8000. I guess the 0x0006 is the length of the
+ * TLV(s) that follow. The rights SNAC does the same thing, with the differing flag
+ * and everything.
+ */
+
+ fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */
+ revision = aimbs_get16(bs); /* # of times ssi data has been modified */
+ if (revision != 0)
+ sess->ssi.revision = revision;
+
+ for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
+
+ while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
+ guint16 namelen, tbslen;
+
+ if (!sess->ssi.items) {
+ if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1)))
+ return -ENOMEM;
+ cur = sess->ssi.items;
+ } else {
+ if (!(cur->next = g_new0(struct aim_ssi_item, 1)))
+ return -ENOMEM;
+ cur = cur->next;
+ }
+
+ if ((namelen = aimbs_get16(bs)))
+ cur->name = aimbs_getstr(bs, namelen);
+ cur->gid = aimbs_get16(bs);
+ cur->bid = aimbs_get16(bs);
+ cur->type = aimbs_get16(bs);
+
+ if ((tbslen = aimbs_get16(bs))) {
+ aim_bstream_t tbs;
+
+ aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
+ cur->data = (void *)aim_readtlvchain(&tbs);
+ aim_bstream_advance(bs, tbslen);
+ }
+ }
+
+ timestamp = aimbs_get32(bs);
+ if (timestamp != 0)
+ sess->ssi.timestamp = timestamp;
+ sess->ssi.received_data = 1;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
+
+ return ret;
+}
+
+/*
+ * SSI Data Enable Presence.
+ *
+ * Should be sent after receiving 13/6 or 13/f to tell the server you
+ * are ready to begin using the list. It will promptly give you the
+ * presence information for everyone in your list and put your permit/deny
+ * settings into effect.
+ *
+ */
+int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
+}
+
+/*
+ * Stuff for SSI authorizations. The code used to work with the old im_ch4
+ * messages, but those are supposed to be obsolete. This is probably
+ * ICQ-specific.
+ */
+
+/**
+ * Request authorization to add someone to the server-side buddy list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param uin The contact's ICQ UIN.
+ * @param reason The reason string to send with the request.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_auth_request( aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason )
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int snaclen;
+
+ snaclen = 10 + 1 + strlen( uin ) + 2 + strlen( reason ) + 2;
+
+ if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
+ return -ENOMEM;
+
+ snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0 );
+ aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid );
+
+ aimbs_put8( &fr->data, strlen( uin ) );
+ aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
+ aimbs_put16( &fr->data, strlen( reason ) );
+ aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
+ aimbs_put16( &fr->data, 0 );
+
+ aim_tx_enqueue( sess, fr );
+
+ return( 0 );
+}
+
+/**
+ * Reply to an authorization request to add someone to the server-side buddy list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param uin The contact's ICQ UIN.
+ * @param yesno 1 == Permit, 0 == Deny
+ * @param reason The reason string to send with the request.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_auth_reply( aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason )
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int snaclen;
+
+ snaclen = 10 + 1 + strlen( uin ) + 3 + strlen( reason );
+
+ if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
+ return -ENOMEM;
+
+ snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0 );
+ aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid );
+
+ aimbs_put8( &fr->data, strlen( uin ) );
+ aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
+ aimbs_put8( &fr->data, yesno );
+ aimbs_put16( &fr->data, strlen( reason ) );
+ aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
+
+ aim_tx_enqueue( sess, fr );
+
+ return( 0 );
+}
+
+
+/*
+ * SSI Add/Mod/Del Item(s).
+ *
+ * Sends the SNAC to add, modify, or delete an item from the server-stored
+ * information. These 3 SNACs all have an identical structure. The only
+ * difference is the subtype that is set for the SNAC.
+ *
+ */
+int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype)
+{
+ aim_frame_t *fr;
+ aim_snacid_t snacid;
+ int i, snaclen, listlen;
+ char *list = NULL;
+
+ if (!sess || !conn || !items || !num)
+ return -EINVAL;
+
+ snaclen = 10; /* For family, subtype, flags, and SNAC ID */
+ listlen = 0;
+ for (i=0; i<num; i++) {
+ snaclen += 10; /* For length, GID, BID, type, and length */
+ if (items[i]->name) {
+ snaclen += strlen(items[i]->name);
+
+ if (subtype == AIM_CB_SSI_ADD) {
+ list = g_realloc(list, listlen + strlen(items[i]->name) + 1);
+ strcpy(list + listlen, items[i]->name);
+ listlen += strlen(items[i]->name) + 1;
+ }
+ } else {
+ if (subtype == AIM_CB_SSI_ADD) {
+ list = g_realloc(list, listlen + 1);
+ list[listlen] = '\0';
+ listlen ++;
+ }
+ }
+ if (items[i]->data)
+ snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
+ }
+
+ if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
+ return -ENOMEM;
+
+ snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0);
+ aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
+
+ g_free(list);
+
+ for (i=0; i<num; i++) {
+ aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
+ if (items[i]->name)
+ aimbs_putraw(&fr->data, (guint8 *)items[i]->name, strlen(items[i]->name));
+ aimbs_put16(&fr->data, items[i]->gid);
+ aimbs_put16(&fr->data, items[i]->bid);
+ aimbs_put16(&fr->data, items[i]->type);
+ aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
+ if (items[i]->data)
+ aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
+ }
+
+ aim_ssi_enqueue(sess, conn, fr);
+
+ return 0;
+}
+
+/*
+ * SSI Add/Mod/Del Ack.
+ *
+ * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
+ *
+ */
+static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+ aim_snac_t *origsnac;
+
+ sess->ssi.waiting_for_ack = 0;
+ aim_ssi_dispatch(sess, rx->conn);
+
+ origsnac = aim_remsnac(sess, snac->id);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx, origsnac);
+
+ if (origsnac) {
+ g_free(origsnac->data);
+ g_free(origsnac);
+ }
+
+ return ret;
+}
+
+/*
+ * SSI Begin Data Modification.
+ *
+ * Tells the server you're going to start modifying data.
+ *
+ */
+int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
+}
+
+/*
+ * SSI End Data Modification.
+ *
+ * Tells the server you're done modifying data.
+ *
+ */
+int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
+{
+ return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
+}
+
+/*
+ * SSI Data Unchanged.
+ *
+ * Response to aim_ssi_reqdata() if the server-side data is not newer than
+ * posted local stamp/revision.
+ *
+ */
+static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ int ret = 0;
+ aim_rxcallback_t userfunc;
+
+ sess->ssi.received_data = 1;
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ ret = userfunc(sess, rx);
+
+ return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
+ return parserights(sess, mod, rx, snac, bs);
+ else if (snac->subtype == AIM_CB_SSI_LIST)
+ return parsedata(sess, mod, rx, snac, bs);
+ else if (snac->subtype == AIM_CB_SSI_SRVACK)
+ return parseack(sess, mod, rx, snac, bs);
+ else if (snac->subtype == AIM_CB_SSI_NOLIST)
+ return parsedataunchanged(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+ aim_ssi_freelist(sess);
+
+ return;
+}
+
+int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = AIM_CB_FAM_SSI;
+ mod->version = 0x0003;
+ mod->toolid = 0x0110;
+ mod->toolversion = 0x0629;
+ mod->flags = 0;
+ strncpy(mod->name, "ssi", sizeof(mod->name));
+ mod->snachandler = snachandler;
+ mod->shutdown = ssi_shutdown;
+
+ return 0;
+}
diff --git a/protocols/oscar/ssi.h b/protocols/oscar/ssi.h
new file mode 100644
index 00000000..94b18d60
--- /dev/null
+++ b/protocols/oscar/ssi.h
@@ -0,0 +1,78 @@
+#ifndef __OSCAR_SSI_H__
+#define __OSCAR_SSI_H__
+
+#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
+
+/*
+ * SNAC Family: Server-Stored Buddy Lists
+ */
+#define AIM_CB_SSI_ERROR 0x0001
+#define AIM_CB_SSI_REQRIGHTS 0x0002
+#define AIM_CB_SSI_RIGHTSINFO 0x0003
+#define AIM_CB_SSI_REQFULLLIST 0x0004
+#define AIM_CB_SSI_REQLIST 0x0005
+#define AIM_CB_SSI_LIST 0x0006
+#define AIM_CB_SSI_ACTIVATE 0x0007
+#define AIM_CB_SSI_ADD 0x0008
+#define AIM_CB_SSI_MOD 0x0009
+#define AIM_CB_SSI_DEL 0x000A
+#define AIM_CB_SSI_SRVACK 0x000E
+#define AIM_CB_SSI_NOLIST 0x000F
+#define AIM_CB_SSI_EDITSTART 0x0011
+#define AIM_CB_SSI_EDITSTOP 0x0012
+#define AIM_CB_SSI_SENDAUTHREQ 0x0018
+#define AIM_CB_SSI_SERVAUTHREQ 0x0019
+#define AIM_CB_SSI_SENDAUTHREP 0x001A
+#define AIM_CB_SSI_SERVAUTHREP 0x001B
+
+
+#define AIM_SSI_TYPE_BUDDY 0x0000
+#define AIM_SSI_TYPE_GROUP 0x0001
+#define AIM_SSI_TYPE_PERMIT 0x0002
+#define AIM_SSI_TYPE_DENY 0x0003
+#define AIM_SSI_TYPE_PDINFO 0x0004
+#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005
+
+struct aim_ssi_item {
+ char *name;
+ guint16 gid;
+ guint16 bid;
+ guint16 type;
+ void *data;
+ struct aim_ssi_item *next;
+};
+
+/* These build the actual SNACs and queue them to be sent */
+int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev);
+int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype);
+int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn);
+
+/* These handle the local variables */
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type);
+struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn);
+int aim_ssi_getpermdeny(struct aim_ssi_item *list);
+guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
+
+/* Send packets */
+int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags);
+int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num);
+int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type);
+int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn);
+int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num);
+int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num);
+int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type);
+int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask);
+int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence);
+int aim_ssi_auth_request(aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason);
+int aim_ssi_auth_reply(aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason);
+
+#endif /* __OSCAR_SSI_H__ */
diff --git a/protocols/oscar/stats.c b/protocols/oscar/stats.c
new file mode 100644
index 00000000..affd82fe
--- /dev/null
+++ b/protocols/oscar/stats.c
@@ -0,0 +1,38 @@
+
+#include <aim.h>
+
+static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+ guint16 interval;
+ aim_rxcallback_t userfunc;
+
+ interval = aimbs_get16(bs);
+
+ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+ return userfunc(sess, rx, interval);
+
+ return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+ if (snac->subtype == 0x0002)
+ return reportinterval(sess, mod, rx, snac, bs);
+
+ return 0;
+}
+
+int stats_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+ mod->family = 0x000b;
+ mod->version = 0x0001;
+ mod->toolid = 0x0104;
+ mod->toolversion = 0x0001;
+ mod->flags = 0;
+ strncpy(mod->name, "stats", sizeof(mod->name));
+ mod->snachandler = snachandler;
+
+ return 0;
+}
diff --git a/protocols/oscar/tlv.c b/protocols/oscar/tlv.c
new file mode 100644
index 00000000..74d177ad
--- /dev/null
+++ b/protocols/oscar/tlv.c
@@ -0,0 +1,600 @@
+#include <aim.h>
+
+static aim_tlv_t *createtlv(void)
+{
+ return g_new0(aim_tlv_t, 1);
+}
+
+static void freetlv(aim_tlv_t **oldtlv)
+{
+
+ if (!oldtlv || !*oldtlv)
+ return;
+
+ g_free((*oldtlv)->value);
+ g_free(*oldtlv);
+ *oldtlv = NULL;
+
+ return;
+}
+
+/**
+ * aim_readtlvchain - Read a TLV chain from a buffer.
+ * @buf: Input buffer
+ * @maxlen: Length of input buffer
+ *
+ * Reads and parses a series of TLV patterns from a data buffer; the
+ * returned structure is manipulatable with the rest of the TLV
+ * routines. When done with a TLV chain, aim_freetlvchain() should
+ * be called to free the dynamic substructures.
+ *
+ * XXX There should be a flag setable here to have the tlvlist contain
+ * bstream references, so that at least the ->value portion of each
+ * element doesn't need to be malloc/memcpy'd. This could prove to be
+ * just as effecient as the in-place TLV parsing used in a couple places
+ * in libfaim.
+ *
+ */
+aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs)
+{
+ aim_tlvlist_t *list = NULL, *cur;
+ guint16 type, length;
+
+ while (aim_bstream_empty(bs)) {
+
+ type = aimbs_get16(bs);
+ length = aimbs_get16(bs);
+
+#if 0 /* temporarily disabled until I know if they're still doing it or not */
+ /*
+ * Okay, so now AOL has decided that any TLV of
+ * type 0x0013 can only be two bytes, despite
+ * what the actual given length is. So here
+ * we dump any invalid TLVs of that sort. Hopefully
+ * theres no special cases to this special case.
+ * - mid (30jun2000)
+ */
+ if ((type == 0x0013) && (length != 0x0002))
+ length = 0x0002;
+#else
+ if (0)
+ ;
+#endif
+ else {
+
+ cur = g_new0(aim_tlvlist_t, 1);
+
+ cur->tlv = createtlv();
+ cur->tlv->type = type;
+ if ((cur->tlv->length = length))
+ cur->tlv->value = aimbs_getraw(bs, length);
+
+ cur->next = list;
+ list = cur;
+ }
+ }
+
+ return list;
+}
+
+/**
+ * aim_freetlvchain - Free a TLV chain structure
+ * @list: Chain to be freed
+ *
+ * Walks the list of TLVs in the passed TLV chain and
+ * frees each one. Note that any references to this data
+ * should be removed before calling this.
+ *
+ */
+void aim_freetlvchain(aim_tlvlist_t **list)
+{
+ aim_tlvlist_t *cur;
+
+ if (!list || !*list)
+ return;
+
+ for (cur = *list; cur; ) {
+ aim_tlvlist_t *tmp;
+
+ freetlv(&cur->tlv);
+
+ tmp = cur->next;
+ g_free(cur);
+ cur = tmp;
+ }
+
+ list = NULL;
+
+ return;
+}
+
+/**
+ * aim_counttlvchain - Count the number of TLVs in a chain
+ * @list: Chain to be counted
+ *
+ * Returns the number of TLVs stored in the passed chain.
+ *
+ */
+int aim_counttlvchain(aim_tlvlist_t **list)
+{
+ aim_tlvlist_t *cur;
+ int count;
+
+ if (!list || !*list)
+ return 0;
+
+ for (cur = *list, count = 0; cur; cur = cur->next)
+ count++;
+
+ return count;
+}
+
+/**
+ * aim_sizetlvchain - Count the number of bytes in a TLV chain
+ * @list: Chain to be sized
+ *
+ * Returns the number of bytes that would be needed to
+ * write the passed TLV chain to a data buffer.
+ *
+ */
+int aim_sizetlvchain(aim_tlvlist_t **list)
+{
+ aim_tlvlist_t *cur;
+ int size;
+
+ if (!list || !*list)
+ return 0;
+
+ for (cur = *list, size = 0; cur; cur = cur->next)
+ size += (4 + cur->tlv->length);
+
+ return size;
+}
+
+/**
+ * aim_addtlvtochain_str - Add a string to a TLV chain
+ * @list: Desination chain (%NULL pointer if empty)
+ * @type: TLV type
+ * @str: String to add
+ * @len: Length of string to add (not including %NULL)
+ *
+ * Adds the passed string as a TLV element of the passed type
+ * to the TLV chain.
+ *
+ */
+int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v)
+{
+ aim_tlvlist_t *newtlv, *cur;
+
+ if (!list)
+ return 0;
+
+ if (!(newtlv = g_new0(aim_tlvlist_t, 1)))
+ return 0;
+
+ if (!(newtlv->tlv = createtlv())) {
+ g_free(newtlv);
+ return 0;
+ }
+ newtlv->tlv->type = t;
+ if ((newtlv->tlv->length = l)) {
+ newtlv->tlv->value = (guint8 *)g_malloc(newtlv->tlv->length);
+ memcpy(newtlv->tlv->value, v, newtlv->tlv->length);
+ }
+
+ if (!*list)
+ *list = newtlv;
+ else {
+ for(cur = *list; cur->next; cur = cur->next)
+ ;
+ cur->next = newtlv;
+ }
+
+ return newtlv->tlv->length;
+}
+
+/**
+ * aim_addtlvtochain8 - Add a 8bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a one-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v)
+{
+ guint8 v8[1];
+
+ aimutil_put8(v8, v);
+
+ return aim_addtlvtochain_raw(list, t, 1, v8);
+}
+
+/**
+ * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a two-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v)
+{
+ guint8 v16[2];
+
+ aimutil_put16(v16, v);
+
+ return aim_addtlvtochain_raw(list, t, 2, v16);
+}
+
+/**
+ * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a four-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 t, const guint32 v)
+{
+ guint8 v32[4];
+
+ aimutil_put32(v32, v);
+
+ return aim_addtlvtochain_raw(list, t, 4, v32);
+}
+
+/**
+ * aim_addtlvtochain_availmsg - Add a ICQ availability message to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a available message to a TLV chain
+ *
+ */
+int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 t, const char *msg)
+{
+ int ret;
+ guint16 unknown_data = 0x00;
+ guint8 add_data_len = 4;
+ guint16 msg_len = strlen(msg);
+ guint8 total_len = strlen(msg) + add_data_len;
+ guint8 *data, *cur;
+ guint8 alloc_len = msg_len + (3*sizeof(guint16)) + (2*sizeof(guint8));
+ data = cur = g_malloc(alloc_len);
+
+ cur += aimutil_put16(cur, 2);
+ cur += aimutil_put8(cur, add_data_len);
+ cur += aimutil_put8(cur, total_len);
+ cur += aimutil_put16(cur, msg_len);
+ cur += aimutil_putstr(cur, msg, msg_len);
+ cur += aimutil_put16(cur, unknown_data);
+
+ ret = aim_addtlvtochain_raw(list, t, alloc_len, data);
+ g_free(data);
+
+ return ret;
+}
+
+/**
+ * aim_addtlvtochain_caps - Add a capability block to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @caps: Bitfield of capability flags to send
+ *
+ * Adds a block of capability blocks to a TLV chain. The bitfield
+ * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
+ *
+ */
+int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps)
+{
+ guint8 buf[16*16]; /* XXX icky fixed length buffer */
+ aim_bstream_t bs;
+
+ if (!caps)
+ return 0; /* nothing there anyway */
+
+ aim_bstream_init(&bs, buf, sizeof(buf));
+
+ aim_putcap(&bs, caps);
+
+ return aim_addtlvtochain_raw(list, t, aim_bstream_curpos(&bs), buf);
+}
+
+int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui)
+{
+ guint8 buf[1024]; /* bleh */
+ aim_bstream_t bs;
+
+ aim_bstream_init(&bs, buf, sizeof(buf));
+
+ aim_putuserinfo(&bs, ui);
+
+ return aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+}
+
+/**
+ * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ *
+ * Adds a TLV with a zero length to a TLV chain.
+ *
+ */
+int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 t)
+{
+ return aim_addtlvtochain_raw(list, t, 0, NULL);
+}
+
+/*
+ * Note that the inner TLV chain will not be modifiable as a tlvchain once
+ * it is written using this. Or rather, it can be, but updates won't be
+ * made to this.
+ *
+ * XXX should probably support sublists for real.
+ *
+ * This is so neat.
+ *
+ */
+int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
+{
+ guint8 *buf;
+ int buflen;
+ aim_bstream_t bs;
+
+ buflen = aim_sizetlvchain(tl);
+
+ if (buflen <= 0)
+ return 0;
+
+ if (!(buf = g_malloc(buflen)))
+ return 0;
+
+ aim_bstream_init(&bs, buf, buflen);
+
+ aim_writetlvchain(&bs, tl);
+
+ aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+ g_free(buf);
+
+ return buflen;
+}
+
+/**
+ * aim_writetlvchain - Write a TLV chain into a data buffer.
+ * @buf: Destination buffer
+ * @buflen: Maximum number of bytes that will be written to buffer
+ * @list: Source TLV chain
+ *
+ * Copies a TLV chain into a raw data buffer, writing only the number
+ * of bytes specified. This operation does not free the chain;
+ * aim_freetlvchain() must still be called to free up the memory used
+ * by the chain structures.
+ *
+ * XXX clean this up, make better use of bstreams
+ */
+int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list)
+{
+ int goodbuflen;
+ aim_tlvlist_t *cur;
+
+ /* do an initial run to test total length */
+ for (cur = *list, goodbuflen = 0; cur; cur = cur->next) {
+ goodbuflen += 2 + 2; /* type + len */
+ goodbuflen += cur->tlv->length;
+ }
+
+ if (goodbuflen > aim_bstream_empty(bs))
+ return 0; /* not enough buffer */
+
+ /* do the real write-out */
+ for (cur = *list; cur; cur = cur->next) {
+ aimbs_put16(bs, cur->tlv->type);
+ aimbs_put16(bs, cur->tlv->length);
+ if (cur->tlv->length)
+ aimbs_putraw(bs, cur->tlv->value, cur->tlv->length);
+ }
+
+ return 1; /* XXX this is a nonsensical return */
+}
+
+
+/**
+ * aim_gettlv - Grab the Nth TLV of type type in the TLV list list.
+ * @list: Source chain
+ * @type: Requested TLV type
+ * @nth: Index of TLV of type to get
+ *
+ * Returns a pointer to an aim_tlv_t of the specified type;
+ * %NULL on error. The @nth parameter is specified starting at %1.
+ * In most cases, there will be no more than one TLV of any type
+ * in a chain.
+ *
+ */
+aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+ aim_tlvlist_t *cur;
+ int i;
+
+ for (cur = list, i = 0; cur; cur = cur->next) {
+ if (cur && cur->tlv) {
+ if (cur->tlv->type == t)
+ i++;
+ if (i >= n)
+ return cur->tlv;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * aim_gettlv_str - Retrieve the Nth TLV in chain as a string.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a %NULL-
+ * terminated string instead of an aim_tlv_t. This is a
+ * dynamic buffer and must be freed by the caller.
+ *
+ */
+char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+ aim_tlv_t *tlv;
+ char *newstr;
+
+ if (!(tlv = aim_gettlv(list, t, n)))
+ return NULL;
+
+ newstr = (char *) g_malloc(tlv->length + 1);
+ memcpy(newstr, tlv->value, tlv->length);
+ *(newstr + tlv->length) = '\0';
+
+ return newstr;
+}
+
+/**
+ * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a
+ * 8bit integer instead of an aim_tlv_t.
+ *
+ */
+guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+ aim_tlv_t *tlv;
+
+ if (!(tlv = aim_gettlv(list, t, n)))
+ return 0; /* erm */
+ return aimutil_get8(tlv->value);
+}
+
+/**
+ * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a
+ * 16bit integer instead of an aim_tlv_t.
+ *
+ */
+guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+ aim_tlv_t *tlv;
+
+ if (!(tlv = aim_gettlv(list, t, n)))
+ return 0; /* erm */
+ return aimutil_get16(tlv->value);
+}
+
+/**
+ * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a
+ * 32bit integer instead of an aim_tlv_t.
+ *
+ */
+guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+ aim_tlv_t *tlv;
+
+ if (!(tlv = aim_gettlv(list, t, n)))
+ return 0; /* erm */
+ return aimutil_get32(tlv->value);
+}
+
+#if 0
+/**
+ * aim_puttlv_8 - Write a one-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a one-byte integer value portion.
+ *
+ */
+int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v)
+{
+ guint8 v8[1];
+
+ aimutil_put8(v8, v);
+
+ return aim_puttlv_raw(buf, t, 1, v8);
+}
+
+/**
+ * aim_puttlv_16 - Write a two-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a two-byte integer value portion.
+ *
+ */
+int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v)
+{
+ guint8 v16[2];
+
+ aimutil_put16(v16, v);
+
+ return aim_puttlv_raw(buf, t, 2, v16);
+}
+
+
+/**
+ * aim_puttlv_32 - Write a four-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a four-byte integer value portion.
+ *
+ */
+int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v)
+{
+ guint8 v32[4];
+
+ aimutil_put32(v32, v);
+
+ return aim_puttlv_raw(buf, t, 4, v32);
+}
+
+/**
+ * aim_puttlv_raw - Write a raw TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @l: Length of string
+ * @v: String to write
+ *
+ * Writes a TLV with a raw value portion. (Only the first @l
+ * bytes of the passed buffer will be written, which should not
+ * include a terminating NULL.)
+ *
+ */
+int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v)
+{
+ int i;
+
+ i = aimutil_put16(buf, t);
+ i += aimutil_put16(buf+i, l);
+ if (l)
+ memcpy(buf+i, v, l);
+ i += l;
+
+ return i;
+}
+#endif
+
diff --git a/protocols/oscar/txqueue.c b/protocols/oscar/txqueue.c
new file mode 100644
index 00000000..51415089
--- /dev/null
+++ b/protocols/oscar/txqueue.c
@@ -0,0 +1,440 @@
+/*
+ * aim_txqueue.c
+ *
+ * Herein lies all the mangement routines for the transmit (Tx) queue.
+ *
+ */
+
+#include <aim.h>
+#include "im.h"
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ * Allocate a new tx frame.
+ *
+ * This is more for looks than anything else.
+ *
+ * Right now, that is. If/when we implement a pool of transmit
+ * frames, this will become the request-an-unused-frame part.
+ *
+ * framing = AIM_FRAMETYPE_OFT/FLAP
+ * chan = channel for FLAP, hdrtype for OFT
+ *
+ */
+aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen)
+{
+ aim_frame_t *fr;
+
+ if (!conn) {
+ do_error_dialog(sess->aux_data, "no connection specified", "Gaim");
+ return NULL;
+ }
+
+ /* For sanity... */
+ if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) ||
+ (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
+ if (framing != AIM_FRAMETYPE_OFT) {
+ do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for rendezvous connection", "Gaim");
+ return NULL;
+ }
+ } else {
+ if (framing != AIM_FRAMETYPE_FLAP) {
+ do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for FLAP connection", "Gaim");
+ return NULL;
+ }
+ }
+
+ if (!(fr = (aim_frame_t *)g_new0(aim_frame_t,1)))
+ return NULL;
+
+ fr->conn = conn;
+
+ fr->hdrtype = framing;
+
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+
+ fr->hdr.flap.type = chan;
+
+ } else if (fr->hdrtype == AIM_FRAMETYPE_OFT) {
+
+ fr->hdr.oft.type = chan;
+ fr->hdr.oft.hdr2len = 0; /* this will get setup by caller */
+
+ } else
+ do_error_dialog(sess->aux_data, "unknown framing", "Gaim");
+
+ if (datalen > 0) {
+ guint8 *data;
+
+ if (!(data = (unsigned char *)g_malloc(datalen))) {
+ aim_frame_destroy(fr);
+ return NULL;
+ }
+
+ aim_bstream_init(&fr->data, data, datalen);
+ }
+
+ return fr;
+}
+
+/*
+ * aim_tx_enqeue__queuebased()
+ *
+ * The overall purpose here is to enqueue the passed in command struct
+ * into the outgoing (tx) queue. Basically...
+ * 1) Make a scope-irrelevent copy of the struct
+ * 3) Mark as not-sent-yet
+ * 4) Enqueue the struct into the list
+ * 6) Return
+ *
+ * Note that this is only used when doing queue-based transmitting;
+ * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
+ *
+ */
+static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr)
+{
+
+ if (!fr->conn) {
+ do_error_dialog(sess->aux_data, "WARNING: enqueueing packet with no connection", "Gaim");
+ fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+ }
+
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+ /* assign seqnum -- XXX should really not assign until hardxmit */
+ fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+ }
+
+ fr->handled = 0; /* not sent yet */
+
+ /* see overhead note in aim_rxqueue counterpart */
+ if (!sess->queue_outgoing)
+ sess->queue_outgoing = fr;
+ else {
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_outgoing; cur->next; cur = cur->next)
+ ;
+ cur->next = fr;
+ }
+
+ return 0;
+}
+
+/*
+ * aim_tx_enqueue__immediate()
+ *
+ * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
+ * the whole queue mess when you want immediate writes to happen.
+ *
+ * Basically the same as its __queuebased couterpart, however
+ * instead of doing a list append, it just calls aim_tx_sendframe()
+ * right here.
+ *
+ */
+static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr)
+{
+
+ if (!fr->conn) {
+ do_error_dialog(sess->aux_data, "packet has no connection", "Gaim");
+ aim_frame_destroy(fr);
+ return 0;
+ }
+
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+ fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+
+ fr->handled = 0; /* not sent yet */
+
+ aim_tx_sendframe(sess, fr);
+
+ aim_frame_destroy(fr);
+
+ return 0;
+}
+
+int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
+{
+
+ if (what == AIM_TX_QUEUED)
+ sess->tx_enqueue = &aim_tx_enqueue__queuebased;
+ else if (what == AIM_TX_IMMEDIATE)
+ sess->tx_enqueue = &aim_tx_enqueue__immediate;
+ else if (what == AIM_TX_USER) {
+ if (!func)
+ return -EINVAL;
+ sess->tx_enqueue = func;
+ } else
+ return -EINVAL; /* unknown action */
+
+ return 0;
+}
+
+int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr)
+{
+
+ /*
+ * If we want to send a connection thats inprogress, we have to force
+ * them to use the queue based version. Otherwise, use whatever they
+ * want.
+ */
+ if (fr && fr->conn &&
+ (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
+ return aim_tx_enqueue__queuebased(sess, fr);
+ }
+
+ return (*sess->tx_enqueue)(sess, fr);
+}
+
+/*
+ * aim_get_next_txseqnum()
+ *
+ * This increments the tx command count, and returns the seqnum
+ * that should be stamped on the next FLAP packet sent. This is
+ * normally called during the final step of packet preparation
+ * before enqueuement (in aim_tx_enqueue()).
+ *
+ */
+flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn)
+{
+ flap_seqnum_t ret;
+
+ ret = ++conn->seqnum;
+
+ return ret;
+}
+
+static int aim_send(int fd, const void *buf, size_t count)
+{
+ int left, cur;
+
+ for (cur = 0, left = count; left; ) {
+ int ret;
+
+ ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
+ if (ret == -1)
+ return -1;
+ else if (ret == 0)
+ return cur;
+
+ cur += ret;
+ left -= ret;
+ }
+
+ return cur;
+}
+
+static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
+{
+ int wrote = 0;
+ if (!bs || !conn || (count < 0))
+ return -EINVAL;
+
+ if (count > aim_bstream_empty(bs))
+ count = aim_bstream_empty(bs); /* truncate to remaining space */
+
+ if (count) {
+ if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) &&
+ (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+ /* I strongly suspect that this is a horrible thing to do
+ * and I feel really guilty doing it. */
+ const char *sn = aim_directim_getsn(conn);
+ aim_rxcallback_t userfunc;
+ while (count - wrote > 1024) {
+ wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
+ if ((userfunc=aim_callhandler(conn->sessv, conn,
+ AIM_CB_FAM_SPECIAL,
+ AIM_CB_SPECIAL_IMAGETRANSFER)))
+ userfunc(conn->sessv, NULL, sn,
+ count-wrote>1024 ? ((double)wrote / count) : 1);
+ }
+ }
+ if (count - wrote) {
+ wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
+ }
+
+ }
+
+ bs->offset += wrote;
+
+ return wrote;
+}
+
+static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
+{
+ aim_bstream_t obs;
+ guint8 *obs_raw;
+ int payloadlen, err = 0, obslen;
+
+ payloadlen = aim_bstream_curpos(&fr->data);
+
+ if (!(obs_raw = g_malloc(6 + payloadlen)))
+ return -ENOMEM;
+
+ aim_bstream_init(&obs, obs_raw, 6 + payloadlen);
+
+ /* FLAP header */
+ aimbs_put8(&obs, 0x2a);
+ aimbs_put8(&obs, fr->hdr.flap.type);
+ aimbs_put16(&obs, fr->hdr.flap.seqnum);
+ aimbs_put16(&obs, payloadlen);
+
+ /* payload */
+ aim_bstream_rewind(&fr->data);
+ aimbs_putbs(&obs, &fr->data, payloadlen);
+
+ obslen = aim_bstream_curpos(&obs);
+ aim_bstream_rewind(&obs);
+ if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
+ err = -errno;
+
+ g_free(obs_raw); /* XXX aim_bstream_free */
+
+ fr->handled = 1;
+ fr->conn->lastactivity = time(NULL);
+
+ return err;
+}
+
+static int sendframe_oft(aim_session_t *sess, aim_frame_t *fr)
+{
+ aim_bstream_t hbs;
+ guint8 *hbs_raw;
+ int hbslen;
+ int err = 0;
+
+ hbslen = 8 + fr->hdr.oft.hdr2len;
+ if (!(hbs_raw = g_malloc(hbslen)))
+ return -1;
+
+ aim_bstream_init(&hbs, hbs_raw, hbslen);
+
+ aimbs_putraw(&hbs, fr->hdr.oft.magic, 4);
+ aimbs_put16(&hbs, fr->hdr.oft.hdr2len + 8);
+ aimbs_put16(&hbs, fr->hdr.oft.type);
+ aimbs_putraw(&hbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+ aim_bstream_rewind(&hbs);
+
+
+ if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) {
+
+ err = -errno;
+
+ } else if (aim_bstream_curpos(&fr->data)) {
+ int len;
+
+ len = aim_bstream_curpos(&fr->data);
+ aim_bstream_rewind(&fr->data);
+
+ if (aim_bstream_send(&fr->data, fr->conn, len) != len)
+ err = -errno;
+ }
+
+ g_free(hbs_raw); /* XXX aim_bstream_free */
+
+ fr->handled = 1;
+ fr->conn->lastactivity = time(NULL);
+
+
+ return err;
+
+
+}
+
+int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
+{
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+ return sendframe_flap(sess, fr);
+ else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
+ return sendframe_oft(sess, fr);
+ return -1;
+}
+
+int aim_tx_flushqueue(aim_session_t *sess)
+{
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+
+ if (cur->handled)
+ continue; /* already been sent */
+
+ if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
+ continue;
+
+ /*
+ * And now for the meager attempt to force transmit
+ * latency and avoid missed messages.
+ */
+ if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
+ /*
+ * XXX should be a break! we dont want to block the
+ * upper layers
+ *
+ * XXX or better, just do this right.
+ *
+ */
+ sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
+ }
+
+ /* XXX this should call the custom "queuing" function!! */
+ aim_tx_sendframe(sess, cur);
+ }
+
+ /* purge sent commands from queue */
+ aim_tx_purgequeue(sess);
+
+ return 0;
+}
+
+/*
+ * aim_tx_purgequeue()
+ *
+ * This is responsable for removing sent commands from the transmit
+ * queue. This is not a required operation, but it of course helps
+ * reduce memory footprint at run time!
+ *
+ */
+void aim_tx_purgequeue(aim_session_t *sess)
+{
+ aim_frame_t *cur, **prev;
+
+ for (prev = &sess->queue_outgoing; (cur = *prev); ) {
+
+ if (cur->handled) {
+ *prev = cur->next;
+
+ aim_frame_destroy(cur);
+
+ } else
+ prev = &cur->next;
+ }
+
+ return;
+}
+
+/**
+ * aim_tx_cleanqueue - get rid of packets waiting for tx on a dying conn
+ * @sess: session
+ * @conn: connection that's dying
+ *
+ * for now this simply marks all packets as sent and lets them
+ * disappear without warning.
+ *
+ */
+void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+ if (cur->conn == conn)
+ cur->handled = 1;
+ }
+
+ return;
+}
+
+
diff --git a/protocols/proxy.c b/protocols/proxy.c
new file mode 100644
index 00000000..b0196e12
--- /dev/null
+++ b/protocols/proxy.c
@@ -0,0 +1,672 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* this is a little piece of code to handle proxy connection */
+/* it is intended to : 1st handle http proxy, using the CONNECT command
+ , 2nd provide an easy way to add socks support */
+
+#define BITLBEE_CORE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#else
+#include "sock.h"
+#define ETIMEDOUT WSAETIMEDOUT
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include "nogaim.h"
+#include "proxy.h"
+
+#define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
+#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+#define GAIM_ERR_COND (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+
+/*FIXME*
+ #ifndef _WIN32
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ closesocket(fd);
+ g_free(phb);
+ return -1;
+ }
+ fcntl(fd, F_SETFL, 0);
+#endif*/
+
+char proxyhost[128] = "";
+int proxyport = 0;
+int proxytype = PROXY_NONE;
+char proxyuser[128] = "";
+char proxypass[128] = "";
+
+struct PHB {
+ GaimInputFunction func, proxy_func;
+ gpointer data, proxy_data;
+ char *host;
+ int port;
+ int fd;
+ gint inpa;
+};
+
+typedef struct _GaimIOClosure {
+ GaimInputFunction function;
+ guint result;
+ gpointer data;
+} GaimIOClosure;
+
+
+
+static struct sockaddr_in *gaim_gethostbyname(char *host, int port)
+{
+ static struct sockaddr_in sin;
+
+ if (!inet_aton(host, &sin.sin_addr)) {
+ struct hostent *hp;
+ if (!(hp = gethostbyname(host))) {
+ return NULL;
+ }
+ memset(&sin, 0, sizeof(struct sockaddr_in));
+ memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+ sin.sin_family = hp->h_addrtype;
+ } else
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+
+ return &sin;
+}
+
+static void gaim_io_destroy(gpointer data)
+{
+ g_free(data);
+}
+
+#ifdef PROXYPROFILER
+struct proxyprofiler
+{
+ GaimInputFunction function;
+ gpointer data;
+
+ int count;
+
+ struct proxyprofiler *next;
+} *pp = NULL;
+
+void proxyprofiler_dump()
+{
+ struct proxyprofiler *l;
+ char s[128];
+ FILE *fp;
+
+ sprintf( s, "proxyprofiler.%d", (int) getpid() );
+ fp = fopen( s, "w" );
+
+ fprintf( fp, "%-18s %-18s %10s\n", "Function", "Data", "Count" );
+ for( l = pp; l; l = l->next )
+ fprintf( fp, "0x%-16x 0x%-16x %10d\n", (int) l->function, (int) l->data, l->count );
+
+ fclose( fp );
+}
+#endif
+
+static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ GaimIOClosure *closure = data;
+ GaimInputCondition gaim_cond = 0;
+
+#ifdef PROXYPROFILER
+ struct proxyprofiler *l;
+
+ for( l = pp; l; l = l->next )
+ {
+ if( closure->function == l->function && closure->data == l->data )
+ break;
+ }
+ if( l )
+ {
+ l->count ++;
+ }
+ else
+ {
+ l = g_new0( struct proxyprofiler, 1 );
+ l->function = closure->function;
+ l->data = closure->data;
+ l->count = 1;
+
+ l->next = pp;
+ pp = l;
+ }
+#endif
+
+ count_io_event(source, "proxy");
+
+ if (condition & GAIM_READ_COND)
+ gaim_cond |= GAIM_INPUT_READ;
+ if (condition & GAIM_WRITE_COND)
+ gaim_cond |= GAIM_INPUT_WRITE;
+// if (condition & GAIM_ERR_COND)
+// fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) );
+
+ closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
+
+ return TRUE;
+}
+
+static void no_one_calls(gpointer data, gint source, GaimInputCondition cond)
+{
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ len = sizeof(error);
+
+#ifndef _WIN32
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ closesocket(source);
+ gaim_input_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
+ else {
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb);
+ }
+ return;
+ }
+ fcntl(source, F_SETFL, 0);
+#endif
+ gaim_input_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
+ else {
+ phb->func(phb->data, source, GAIM_INPUT_READ);
+ g_free(phb);
+ }
+}
+
+static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb)
+{
+ struct sockaddr_in *sin;
+ int fd = -1;
+
+ if (!(sin = gaim_gethostbyname(host, port))) {
+ g_free(phb);
+ return -1;
+ }
+
+ if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
+ g_free(phb);
+ return -1;
+ }
+
+ sock_make_nonblocking(fd);
+
+ if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+ if (sockerr_again()) {
+ phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb);
+ phb->fd = fd;
+ } else {
+ closesocket(fd);
+ g_free(phb);
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+
+/* Connecting to HTTP proxies */
+
+#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
+#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
+
+static void http_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+ int nlc = 0;
+ int pos = 0;
+ struct PHB *phb = data;
+ char inputline[8192];
+
+ gaim_input_remove(phb->inpa);
+
+ while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
+ if (inputline[pos - 1] == '\n')
+ nlc++;
+ else if (inputline[pos - 1] != '\r')
+ nlc = 0;
+ }
+ inputline[pos] = '\0';
+
+ if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
+ (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
+ phb->func(phb->data, source, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+}
+
+static void http_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+ char cmd[384];
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ gaim_input_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+#ifdef F_SETFL
+ fcntl(source, F_SETFL, 0);
+#endif
+
+ g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
+ phb->host, phb->port);
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ if (proxyuser && *proxyuser) {
+ char *t1, *t2;
+ t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
+ t2 = tobase64(t1);
+ g_free(t1);
+ g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
+ g_free(t2);
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+ }
+
+ g_snprintf(cmd, sizeof(cmd), "\r\n");
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb);
+}
+
+static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = http_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS4 proxies */
+
+static void s4_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char packet[12];
+ struct PHB *phb = data;
+
+ gaim_input_remove(phb->inpa);
+
+ memset(packet, 0, sizeof(packet));
+ if (read(source, packet, 9) >= 4 && packet[1] == 90) {
+ phb->func(phb->data, source, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+}
+
+static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char packet[12];
+ struct hostent *hp;
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ gaim_input_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+#ifdef F_SETFL
+ fcntl(source, F_SETFL, 0);
+#endif
+
+ /* XXX does socks4 not support host name lookups by the proxy? */
+ if (!(hp = gethostbyname(phb->host))) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ packet[0] = 4;
+ packet[1] = 1;
+ packet[2] = phb->port >> 8;
+ packet[3] = phb->port & 0xff;
+ packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
+ packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
+ packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
+ packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
+ packet[8] = 0;
+ if (write(source, packet, 9) != 9) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
+}
+
+static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = s4_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS5 proxies */
+
+static void s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ gaim_input_remove(phb->inpa);
+
+ if (read(source, buf, 10) < 10) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+ if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->func(phb->data, source, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+}
+
+static void s5_sendconnect(gpointer data, gint source)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+ int hlen = strlen(phb->host);
+
+ buf[0] = 0x05;
+ buf[1] = 0x01; /* CONNECT */
+ buf[2] = 0x00; /* reserved */
+ buf[3] = 0x03; /* address type -- host name */
+ buf[4] = hlen;
+ memcpy(buf + 5, phb->host, hlen);
+ buf[5 + strlen(phb->host)] = phb->port >> 8;
+ buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
+
+ if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
+}
+
+static void s5_readauth(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ gaim_input_remove(phb->inpa);
+
+ if (read(source, buf, 2) < 2) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ s5_sendconnect(phb, source);
+}
+
+static void s5_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ gaim_input_remove(phb->inpa);
+
+ if (read(source, buf, 2) < 2) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ if (buf[1] == 0x02) {
+ unsigned int i = strlen(proxyuser), j = strlen(proxypass);
+ buf[0] = 0x01; /* version 1 */
+ buf[1] = i;
+ memcpy(buf + 2, proxyuser, i);
+ buf[2 + i] = j;
+ memcpy(buf + 2 + i + 1, proxypass, j);
+ if (write(source, buf, 3 + i + j) < 3 + i + j) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
+ } else {
+ s5_sendconnect(phb, source);
+ }
+}
+
+static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+ unsigned char buf[512];
+ int i;
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ gaim_input_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+#ifdef F_SETFL
+ fcntl(source, F_SETFL, 0);
+#endif
+
+ i = 0;
+ buf[0] = 0x05; /* SOCKS version 5 */
+ if (proxyuser[0]) {
+ buf[1] = 0x02; /* two methods */
+ buf[2] = 0x00; /* no authentication */
+ buf[3] = 0x02; /* username/password authentication */
+ i = 4;
+ } else {
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ i = 3;
+ }
+
+ if (write(source, buf, i) < i) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
+}
+
+static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = s5_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Export functions */
+
+gint gaim_input_add(gint source, GaimInputCondition condition, GaimInputFunction function, gpointer data)
+{
+ GaimIOClosure *closure = g_new0(GaimIOClosure, 1);
+ GIOChannel *channel;
+ GIOCondition cond = 0;
+
+ closure->function = function;
+ closure->data = data;
+
+ if (condition & GAIM_INPUT_READ)
+ cond |= GAIM_READ_COND;
+ if (condition & GAIM_INPUT_WRITE)
+ cond |= GAIM_WRITE_COND;
+
+ channel = g_io_channel_unix_new(source);
+ closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
+ gaim_io_invoke, closure, gaim_io_destroy);
+
+ g_io_channel_unref(channel);
+ return closure->result;
+}
+
+void gaim_input_remove(gint tag)
+{
+ if (tag > 0)
+ g_source_remove(tag);
+}
+
+int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data)
+{
+ struct PHB *phb;
+
+ if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+ return -1;
+ }
+
+ phb = g_new0(struct PHB, 1);
+ phb->func = func;
+ phb->data = data;
+
+#ifndef _WIN32
+ sethostent(1);
+#endif
+
+ if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
+ return proxy_connect_none(host, port, phb);
+ else if (proxytype == PROXY_HTTP)
+ return proxy_connect_http(host, port, phb);
+ else if (proxytype == PROXY_SOCKS4)
+ return proxy_connect_socks4(host, port, phb);
+ else if (proxytype == PROXY_SOCKS5)
+ return proxy_connect_socks5(host, port, phb);
+
+ if (phb->host) g_free(phb);
+ g_free(phb);
+ return -1;
+}
diff --git a/protocols/proxy.h b/protocols/proxy.h
new file mode 100644
index 00000000..7c34fc40
--- /dev/null
+++ b/protocols/proxy.h
@@ -0,0 +1,60 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* this is the export part of the proxy.c file. it does a little
+ prototype-ing stuff and redefine some net function to mask them
+ with some kind of transparent layer */
+
+#ifndef _PROXY_H_
+#define _PROXY_H_
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+#include <glib.h>
+#include <gmodule.h>
+
+#define PROXY_NONE 0
+#define PROXY_HTTP 1
+#define PROXY_SOCKS4 2
+#define PROXY_SOCKS5 3
+
+extern char proxyhost[128];
+extern int proxyport;
+extern int proxytype;
+extern char proxyuser[128];
+extern char proxypass[128];
+
+typedef enum {
+ GAIM_INPUT_READ = 1 << 0,
+ GAIM_INPUT_WRITE = 1 << 1
+} GaimInputCondition;
+typedef void (*GaimInputFunction)(gpointer, gint, GaimInputCondition);
+
+G_MODULE_EXPORT gint gaim_input_add(int, GaimInputCondition, GaimInputFunction, gpointer);
+G_MODULE_EXPORT void gaim_input_remove(gint);
+
+G_MODULE_EXPORT int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data);
+
+#endif /* _PROXY_H_ */
diff --git a/protocols/sha.c b/protocols/sha.c
new file mode 100644
index 00000000..895505a1
--- /dev/null
+++ b/protocols/sha.c
@@ -0,0 +1,173 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is SHA 180-1 Reference Implementation (Compact version)
+ *
+ * The Initial Developer of the Original Code is Paul Kocher of
+ * Cryptography Research. Portions created by Paul Kocher are
+ * Copyright (C) 1995-9 by Cryptography Research, Inc. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ */
+
+#define BITLBEE_CORE
+#include "nogaim.h"
+
+static void shaHashBlock(SHA_CTX *ctx);
+
+void shaInit(SHA_CTX *ctx) {
+ int i;
+
+ ctx->lenW = 0;
+ ctx->sizeHi = ctx->sizeLo = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants)
+ */
+ ctx->H[0] = 0x67452301L;
+ ctx->H[1] = 0xefcdab89L;
+ ctx->H[2] = 0x98badcfeL;
+ ctx->H[3] = 0x10325476L;
+ ctx->H[4] = 0xc3d2e1f0L;
+
+ for (i = 0; i < 80; i++)
+ ctx->W[i] = 0;
+}
+
+
+void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len) {
+ int i;
+
+ /* Read the data into W and process blocks as they get full
+ */
+ for (i = 0; i < len; i++) {
+ ctx->W[ctx->lenW / 4] <<= 8;
+ ctx->W[ctx->lenW / 4] |= (guint32)dataIn[i];
+ if ((++ctx->lenW) % 64 == 0) {
+ shaHashBlock(ctx);
+ ctx->lenW = 0;
+ }
+ ctx->sizeLo += 8;
+ ctx->sizeHi += (ctx->sizeLo < 8);
+ }
+}
+
+
+void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]) {
+ unsigned char pad0x80 = 0x80;
+ unsigned char pad0x00 = 0x00;
+ unsigned char padlen[8];
+ int i;
+
+ /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
+ */
+ padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
+ padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
+ padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
+ padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
+ padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
+ padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
+ padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
+ padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
+ shaUpdate(ctx, &pad0x80, 1);
+ while (ctx->lenW != 56)
+ shaUpdate(ctx, &pad0x00, 1);
+ shaUpdate(ctx, padlen, 8);
+
+ /* Output hash
+ */
+ for (i = 0; i < 20; i++) {
+ hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
+ ctx->H[i / 4] <<= 8;
+ }
+
+ /*
+ * Re-initialize the context (also zeroizes contents)
+ */
+ shaInit(ctx);
+}
+
+
+void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) {
+ SHA_CTX ctx;
+
+ shaInit(&ctx);
+ shaUpdate(&ctx, dataIn, len);
+ shaFinal(&ctx, hashout);
+}
+
+
+#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
+
+static void shaHashBlock(SHA_CTX *ctx) {
+ int t;
+ guint32 A,B,C,D,E,TEMP;
+
+ for (t = 16; t <= 79; t++)
+ ctx->W[t] =
+ SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ for (t = 0; t <= 19; t++) {
+ TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 20; t <= 39; t++) {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 40; t <= 59; t++) {
+ TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 60; t <= 79; t++) {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * This code added by Thomas "temas" Muldowney for Jabber compatability
+ *
+ *---------------------------------------------------------------------------*/
+char *shahash(char *str)
+{
+ static char final[41];
+ char *pos;
+ unsigned char hashval[20];
+ int x;
+
+ if(!str || strlen(str) == 0)
+ return NULL;
+
+ shaBlock((unsigned char *)str, strlen(str), hashval);
+
+ pos = final;
+ for(x=0;x<20;x++)
+ {
+ g_snprintf(pos, 3, "%02x", hashval[x]);
+ pos += 2;
+ }
+ return (char *)final;
+}
diff --git a/protocols/sha.h b/protocols/sha.h
new file mode 100644
index 00000000..e8152b1b
--- /dev/null
+++ b/protocols/sha.h
@@ -0,0 +1,21 @@
+#ifndef __SHA_H__
+#define __SHA_H__
+
+#include <gmodule.h>
+
+G_MODULE_EXPORT int strprintsha(char *dest, int *hashval);
+
+typedef struct {
+ guint32 H[5];
+ guint32 W[80];
+ int lenW;
+ guint32 sizeHi,sizeLo;
+} SHA_CTX;
+
+G_MODULE_EXPORT void shaInit(SHA_CTX *ctx);
+G_MODULE_EXPORT void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len);
+G_MODULE_EXPORT void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]);
+G_MODULE_EXPORT void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]);
+G_MODULE_EXPORT char *shahash(char *str);
+
+#endif
diff --git a/protocols/ssl_bogus.c b/protocols/ssl_bogus.c
new file mode 100644
index 00000000..226997db
--- /dev/null
+++ b/protocols/ssl_bogus.c
@@ -0,0 +1,50 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - dummy version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "ssl_client.h"
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+ return( NULL );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ return( -1 );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ return( -1 );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+}
+
+int ssl_getfd( void *conn )
+{
+ return( -1 );
+}
diff --git a/protocols/ssl_client.h b/protocols/ssl_client.h
new file mode 100644
index 00000000..a829a7b1
--- /dev/null
+++ b/protocols/ssl_client.h
@@ -0,0 +1,35 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <glib.h>
+#include "proxy.h"
+
+typedef void (*SslInputFunction)(gpointer, void*, GaimInputCondition);
+
+G_MODULE_EXPORT void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data );
+G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
+G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
+G_MODULE_EXPORT void ssl_disconnect( void *conn_ );
+G_MODULE_EXPORT int ssl_getfd( void *conn );
diff --git a/protocols/ssl_gnutls.c b/protocols/ssl_gnutls.c
new file mode 100644
index 00000000..535d126e
--- /dev/null
+++ b/protocols/ssl_gnutls.c
@@ -0,0 +1,136 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - GnuTLS version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <gnutls/gnutls.h>
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include "stdlib.h"
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+ SslInputFunction func;
+ gpointer data;
+ int fd;
+ gboolean established;
+
+ gnutls_session session;
+ gnutls_certificate_credentials xcred;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+
+ conn->fd = proxy_connect( host, port, ssl_connected, conn );
+ conn->func = func;
+ conn->data = data;
+
+ if( conn->fd < 0 )
+ {
+ g_free( conn );
+ return( NULL );
+ }
+
+ if( !initialized )
+ {
+ gnutls_global_init();
+ initialized = TRUE;
+ atexit( gnutls_global_deinit );
+ }
+
+ gnutls_certificate_allocate_credentials( &conn->xcred );
+ gnutls_init( &conn->session, GNUTLS_CLIENT );
+ gnutls_set_default_priority( conn->session );
+ gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
+
+ return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ goto ssl_connected_failure;
+
+ gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd );
+
+ if( gnutls_handshake( conn->session ) < 0 )
+ goto ssl_connected_failure;
+
+ conn->established = TRUE;
+ conn->func( conn->data, conn, cond );
+ return;
+
+ssl_connected_failure:
+ conn->func( conn->data, NULL, cond );
+
+ gnutls_deinit( conn->session );
+ gnutls_certificate_free_credentials( conn->xcred );
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( gnutls_record_recv( ((struct scd*)conn)->session, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( gnutls_record_send( ((struct scd*)conn)->session, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+ struct scd *conn = conn_;
+
+ if( conn->established )
+ gnutls_bye( conn->session, GNUTLS_SHUT_WR );
+
+ closesocket( conn->fd );
+
+ gnutls_deinit( conn->session );
+ gnutls_certificate_free_credentials( conn->xcred );
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
diff --git a/protocols/ssl_nss.c b/protocols/ssl_nss.c
new file mode 100644
index 00000000..0815f952
--- /dev/null
+++ b/protocols/ssl_nss.c
@@ -0,0 +1,180 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - NSS version */
+
+/* Copyright 2004 Jelmer Vernooij */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "bitlbee.h"
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include <nspr.h>
+#include <prio.h>
+#include <sslproto.h>
+#include <nss.h>
+#include <private/pprio.h>
+#include <ssl.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+ SslInputFunction func;
+ gpointer data;
+ int fd;
+ PRFileDesc *prfd;
+ gboolean established;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver)
+{
+ return SECSuccess;
+}
+
+static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
+{
+ PRErrorCode err;
+
+ if(!arg) return SECFailure;
+
+ *(PRErrorCode *)arg = err = PORT_GetError();
+
+ switch(err) {
+ case SEC_ERROR_INVALID_AVA:
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_CERT:
+ case SEC_ERROR_CERT_VALID:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ case SEC_ERROR_CRL_EXPIRED:
+ case SEC_ERROR_CRL_BAD_SIGNATURE:
+ case SEC_ERROR_EXTENSION_VALUE_INVALID:
+ case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+ return SECSuccess;
+
+ default:
+ return SECFailure;
+ }
+}
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+
+ conn->fd = proxy_connect( host, port, ssl_connected, conn );
+ conn->func = func;
+ conn->data = data;
+
+ if( conn->fd < 0 )
+ {
+ g_free( conn );
+ return( NULL );
+ }
+
+ if( !initialized )
+ {
+ PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ NSS_NoDB_Init(NULL);
+ NSS_SetDomesticPolicy();
+ }
+
+
+ return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ goto ssl_connected_failure;
+
+
+
+
+ conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
+ SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
+ SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+ SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL);
+ SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB());
+ SSL_ResetHandshake(conn->prfd, PR_FALSE);
+
+ if (SSL_ForceHandshake(conn->prfd)) {
+ goto ssl_connected_failure;
+ }
+
+
+ conn->established = TRUE;
+ conn->func( conn->data, conn, cond );
+ return;
+
+ ssl_connected_failure:
+
+ conn->func( conn->data, NULL, cond );
+
+ PR_Close( conn -> prfd );
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+ struct scd *conn = conn_;
+
+ PR_Close( conn->prfd );
+ closesocket( conn->fd );
+
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
diff --git a/protocols/ssl_openssl.c b/protocols/ssl_openssl.c
new file mode 100644
index 00000000..d2a7e1fe
--- /dev/null
+++ b/protocols/ssl_openssl.c
@@ -0,0 +1,158 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - GnuTLS version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+ SslInputFunction func;
+ gpointer data;
+ int fd;
+ gboolean established;
+
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+ SSL_METHOD *meth;
+
+ conn->fd = proxy_connect( host, port, ssl_connected, conn );
+ conn->func = func;
+ conn->data = data;
+
+ if( conn->fd < 0 )
+ {
+ g_free( conn );
+ return( NULL );
+ }
+
+ if( !initialized )
+ {
+ initialized = TRUE;
+ SSLeay_add_ssl_algorithms();
+ }
+
+ meth = TLSv1_client_method();
+ conn->ssl_ctx = SSL_CTX_new( meth );
+ if( conn->ssl_ctx == NULL )
+ {
+ conn->fd = -1;
+ return( NULL );
+ }
+
+ conn->ssl = SSL_new( conn->ssl_ctx );
+ if( conn->ssl == NULL )
+ {
+ conn->fd = -1;
+ return( NULL );
+ }
+
+ return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ goto ssl_connected_failure;
+
+ SSL_set_fd( conn->ssl, conn->fd );
+
+ if( SSL_connect( conn->ssl ) < 0 )
+ goto ssl_connected_failure;
+
+ conn->established = TRUE;
+ conn->func( conn->data, conn, cond );
+ return;
+
+ssl_connected_failure:
+ conn->func( conn->data, NULL, cond );
+
+ if( conn->ssl )
+ {
+ SSL_shutdown( conn->ssl );
+ SSL_free( conn->ssl );
+ }
+ if( conn->ssl_ctx )
+ {
+ SSL_CTX_free( conn->ssl_ctx );
+ }
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( SSL_read( ((struct scd*)conn)->ssl, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( SSL_write( ((struct scd*)conn)->ssl, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+ struct scd *conn = conn_;
+
+ if( conn->established )
+ SSL_shutdown( conn->ssl );
+
+ closesocket( conn->fd );
+
+ SSL_free( conn->ssl );
+ SSL_CTX_free( conn->ssl_ctx );
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
diff --git a/protocols/util.c b/protocols/util.c
new file mode 100644
index 00000000..073ee36d
--- /dev/null
+++ b/protocols/util.c
@@ -0,0 +1,415 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * nogaim
+ *
+ * Gaim without gaim - for BitlBee
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * (and possibly other members of the Gaim team)
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Parts from util.c from gaim needed by nogaim */
+#define BITLBEE_CORE
+#include "nogaim.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <time.h>
+
+char *utf8_to_str(const char *in)
+{
+ int n = 0, i = 0;
+ int inlen;
+ char *result;
+
+ if (!in)
+ return NULL;
+
+ inlen = strlen(in);
+
+ result = g_malloc(inlen + 1);
+
+ while (n <= inlen - 1) {
+ long c = (long)in[n];
+ if (c < 0x80)
+ result[i++] = (char)c;
+ else {
+ if ((c & 0xC0) == 0xC0)
+ result[i++] =
+ (char)(((c & 0x03) << 6) | (((unsigned char)in[++n]) & 0x3F));
+ else if ((c & 0xE0) == 0xE0) {
+ if (n + 2 <= inlen) {
+ result[i] =
+ (char)(((c & 0xF) << 4) | (((unsigned char)in[++n]) & 0x3F));
+ result[i] =
+ (char)(((unsigned char)result[i]) |
+ (((unsigned char)in[++n]) & 0x3F));
+ i++;
+ } else
+ n += 2;
+ } else if ((c & 0xF0) == 0xF0)
+ n += 3;
+ else if ((c & 0xF8) == 0xF8)
+ n += 4;
+ else if ((c & 0xFC) == 0xFC)
+ n += 5;
+ }
+ n++;
+ }
+ result[i] = '\0';
+
+ return result;
+}
+
+char *str_to_utf8(const char *in)
+{
+ int n = 0, i = 0;
+ int inlen;
+ char *result = NULL;
+
+ if (!in)
+ return NULL;
+
+ inlen = strlen(in);
+
+ result = g_malloc(inlen * 2 + 1);
+
+ while (n < inlen) {
+ long c = (long)in[n];
+ if (c == 27) {
+ n += 2;
+ if (in[n] == 'x')
+ n++;
+ if (in[n] == '3')
+ n++;
+ n += 2;
+ continue;
+ }
+ /* why are we removing newlines and carriage returns?
+ if ((c == 0x0D) || (c == 0x0A)) {
+ n++;
+ continue;
+ }
+ */
+ if (c < 128)
+ result[i++] = (char)c;
+ else {
+ result[i++] = (char)((c >> 6) | 192);
+ result[i++] = (char)((c & 63) | 128);
+ }
+ n++;
+ }
+ result[i] = '\0';
+
+ return result;
+}
+
+void strip_linefeed(gchar *text)
+{
+ int i, j;
+ gchar *text2 = g_malloc(strlen(text) + 1);
+
+ for (i = 0, j = 0; text[i]; i++)
+ if (text[i] != '\r')
+ text2[j++] = text[i];
+ text2[j] = '\0';
+
+ strcpy(text, text2);
+ g_free(text2);
+}
+
+char *add_cr(char *text)
+{
+ char *ret = NULL;
+ int count = 0, j;
+ unsigned int i;
+
+ if (text[0] == '\n')
+ count++;
+ for (i = 1; i < strlen(text); i++)
+ if (text[i] == '\n' && text[i - 1] != '\r')
+ count++;
+
+ if (count == 0)
+ return g_strdup(text);
+
+ ret = g_malloc0(strlen(text) + count + 1);
+
+ i = 0; j = 0;
+ if (text[i] == '\n')
+ ret[j++] = '\r';
+ ret[j++] = text[i++];
+ for (; i < strlen(text); i++) {
+ if (text[i] == '\n' && text[i - 1] != '\r')
+ ret[j++] = '\r';
+ ret[j++] = text[i];
+ }
+
+ return ret;
+}
+
+static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/";
+
+/* XXX Find bug */
+char *tobase64(const char *text)
+{
+ char *out = NULL;
+ const char *c;
+ unsigned int tmp = 0;
+ int len = 0, n = 0;
+
+ c = text;
+
+ while (*c) {
+ tmp = tmp << 8;
+ tmp += *c;
+ n++;
+
+ if (n == 3) {
+ out = g_realloc(out, len + 4);
+ out[len] = alphabet[(tmp >> 18) & 0x3f];
+ out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+ out[len + 2] = alphabet[(tmp >> 6) & 0x3f];
+ out[len + 3] = alphabet[tmp & 0x3f];
+ len += 4;
+ tmp = 0;
+ n = 0;
+ }
+ c++;
+ }
+ switch (n) {
+
+ case 2:
+ tmp <<= 8;
+ out = g_realloc(out, len + 5);
+ out[len] = alphabet[(tmp >> 18) & 0x3f];
+ out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+ out[len + 2] = alphabet[(tmp >> 6) & 0x3f];
+ out[len + 3] = '=';
+ out[len + 4] = 0;
+ break;
+ case 1:
+ tmp <<= 16;
+ out = g_realloc(out, len + 5);
+ out[len] = alphabet[(tmp >> 18) & 0x3f];
+ out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+ out[len + 2] = '=';
+ out[len + 3] = '=';
+ out[len + 4] = 0;
+ break;
+ case 0:
+ out = g_realloc(out, len + 1);
+ out[len] = 0;
+ break;
+ }
+ return out;
+}
+
+char *normalize(const char *s)
+{
+ static char buf[BUF_LEN];
+ char *t, *u;
+ int x = 0;
+
+ g_return_val_if_fail((s != NULL), NULL);
+
+ u = t = g_strdup(s);
+
+ strcpy(t, s);
+ g_strdown(t);
+
+ while (*t && (x < BUF_LEN - 1)) {
+ if (*t != ' ') {
+ buf[x] = *t;
+ x++;
+ }
+ t++;
+ }
+ buf[x] = '\0';
+ g_free(u);
+ return buf;
+}
+
+time_t get_time(int year, int month, int day, int hour, int min, int sec)
+{
+ struct tm tm;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
+ return mktime(&tm);
+}
+
+typedef struct htmlentity
+{
+ char code[8];
+ char is;
+} htmlentity_t;
+
+/* FIXME: This is ISO8859-1(5) centric, so might cause problems with other charsets. */
+
+static htmlentity_t ent[] =
+{
+ { "lt", '<' },
+ { "gt", '>' },
+ { "amp", '&' },
+ { "quot", '"' },
+ { "aacute", 'á' },
+ { "eacute", 'é' },
+ { "iacute", 'é' },
+ { "oacute", 'ó' },
+ { "uacute", 'ú' },
+ { "agrave", 'à' },
+ { "egrave", 'è' },
+ { "igrave", 'ì' },
+ { "ograve", 'ò' },
+ { "ugrave", 'ù' },
+ { "acirc", 'â' },
+ { "ecirc", 'ê' },
+ { "icirc", 'î' },
+ { "ocirc", 'ô' },
+ { "ucirc", 'û' },
+ { "nbsp", ' ' },
+ { "", 0 }
+};
+
+void strip_html( char *in )
+{
+ char *start = in;
+ char *out = g_malloc( strlen( in ) + 1 );
+ char *s = out, *cs;
+ int i, matched;
+
+ memset( out, 0, strlen( in ) + 1 );
+
+ while( *in )
+ {
+ if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
+ {
+ /* If in points at a < and in+1 points at a letter or a slash, this is probably
+ a HTML-tag. Try to find a closing > and continue there. If the > can't be
+ found, assume that it wasn't a HTML-tag after all. */
+
+ cs = in;
+
+ while( *in && *in != '>' )
+ in ++;
+
+ if( *in )
+ {
+ if( g_strncasecmp( cs+1, "br", 2) == 0 )
+ *(s++) = '\n';
+ in ++;
+ }
+ else
+ {
+ in = cs;
+ *(s++) = *(in++);
+ }
+ }
+ else if( *in == '&' )
+ {
+ cs = ++in;
+ while( *in && isalpha( *in ) )
+ in ++;
+
+ if( *in == ';' ) in ++;
+ matched = 0;
+
+ for( i = 0; *ent[i].code; i ++ )
+ if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
+ {
+ *(s++) = ent[i].is;
+ matched = 1;
+ break;
+ }
+
+ /* None of the entities were matched, so return the string */
+ if( !matched )
+ {
+ in = cs - 1;
+ *(s++) = *(in++);
+ }
+ }
+ else
+ {
+ *(s++) = *(in++);
+ }
+ }
+
+ strcpy( start, out );
+ g_free( out );
+}
+
+char *escape_html( const char *html )
+{
+ const char *c = html;
+ GString *ret;
+ char *str;
+
+ if( html == NULL )
+ return( NULL );
+ if( g_strncasecmp( html, "<html>", 6 ) == 0 )
+ return( g_strdup( html ) );
+
+ ret = g_string_new( "" );
+
+ while( *c )
+ {
+ switch( *c )
+ {
+ case '&':
+ ret = g_string_append( ret, "&amp;" );
+ break;
+ case '<':
+ ret = g_string_append( ret, "&lt;" );
+ break;
+ case '>':
+ ret = g_string_append( ret, "&gt;" );
+ break;
+ case '"':
+ ret = g_string_append( ret, "&quot;" );
+ break;
+ default:
+ ret = g_string_append_c( ret, *c );
+ }
+ c ++;
+ }
+
+ str = ret->str;
+ g_string_free( ret, FALSE );
+ return( str );
+}
+
+void info_string_append(GString *str, char *newline, char *name, char *value)
+{
+ if( value && value[0] )
+ g_string_sprintfa( str, "%s%s: %s", newline, name, value );
+}
diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile
new file mode 100644
index 00000000..39a655a3
--- /dev/null
+++ b/protocols/yahoo/Makefile
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_list.o yahoo_util.o
+
+CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB
+LFLAGS += -r
+
+# [SH] Phony targets
+all: yahooo.o
+
+.PHONY: all clean distclean
+
+clean:
+ rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+yahooo.o: $(objects)
+ @echo '*' Linking yahooo.o
+ @$(LD) $(LFLAGS) $(objects) -o yahooo.o
diff --git a/protocols/yahoo/crypt.c b/protocols/yahoo/crypt.c
new file mode 100644
index 00000000..00eed70b
--- /dev/null
+++ b/protocols/yahoo/crypt.c
@@ -0,0 +1,207 @@
+/* One way encryption based on MD5 sum.
+ Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+ The GNU C 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.
+
+ The GNU C 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* warmenhoven took this file and made it work with the md5.[ch] we
+ * already had. isn't that lovely. people should just use linux or
+ * freebsd, crypt works properly on those systems. i hate solaris */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_STRING_H
+# include <string.h>
+#elif HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <stdlib.h>
+#include "yahoo_util.h"
+
+#include "md5.h"
+
+/* Define our magic string to mark salt for MD5 "encryption"
+ replacement. This is meant to be the same as for other MD5 based
+ encryption implementations. */
+static const char md5_salt_prefix[] = "$1$";
+
+/* Table with characters for base64 transformation. */
+static const char b64t[64] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+char *yahoo_crypt(char *key, char *salt)
+{
+ char *buffer = NULL;
+ int buflen = 0;
+ int needed = 3 + strlen (salt) + 1 + 26 + 1;
+
+ md5_byte_t alt_result[16];
+ md5_state_t ctx;
+ md5_state_t alt_ctx;
+ size_t salt_len;
+ size_t key_len;
+ size_t cnt;
+ char *cp;
+
+ if (buflen < needed) {
+ buflen = needed;
+ if ((buffer = realloc(buffer, buflen)) == NULL)
+ return NULL;
+ }
+
+ /* Find beginning of salt string. The prefix should normally always
+ be present. Just in case it is not. */
+ if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
+ /* Skip salt prefix. */
+ salt += sizeof (md5_salt_prefix) - 1;
+
+ salt_len = MIN (strcspn (salt, "$"), 8);
+ key_len = strlen (key);
+
+ /* Prepare for the real work. */
+ md5_init(&ctx);
+
+ /* Add the key string. */
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Because the SALT argument need not always have the salt prefix we
+ add it separately. */
+ md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
+
+ /* The last part is the salt string. This must be at most 8
+ characters and it ends at the first `$' character (for
+ compatibility which existing solutions). */
+ md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The
+ final result will be added to the first context. */
+ md5_init(&alt_ctx);
+
+ /* Add key. */
+ md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+ /* Add salt. */
+ md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Add key again. */
+ md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+ /* Now get result of this (16 bytes) and add it to the other
+ context. */
+ md5_finish(&alt_ctx, alt_result);
+
+ /* Add for any character in the key one byte of the alternate sum. */
+ for (cnt = key_len; cnt > 16; cnt -= 16)
+ md5_append(&ctx, alt_result, 16);
+ md5_append(&ctx, alt_result, cnt);
+
+ /* For the following code we need a NUL byte. */
+ alt_result[0] = '\0';
+
+ /* The original implementation now does something weird: for every 1
+ bit in the key the first 0 is added to the buffer, for every 0
+ bit the first character of the key. This does not seem to be
+ what was intended but we have to follow this to be compatible. */
+ for (cnt = key_len; cnt > 0; cnt >>= 1)
+ md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
+
+ /* Create intermediate result. */
+ md5_finish(&ctx, alt_result);
+
+ /* Now comes another weirdness. In fear of password crackers here
+ comes a quite long loop which just processes the output of the
+ previous round again. We cannot ignore this here. */
+ for (cnt = 0; cnt < 1000; ++cnt) {
+ /* New context. */
+ md5_init(&ctx);
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0)
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+ else
+ md5_append(&ctx, alt_result, 16);
+
+ /* Add salt for numbers not divisible by 3. */
+ if (cnt % 3 != 0)
+ md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Add key for numbers not divisible by 7. */
+ if (cnt % 7 != 0)
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0)
+ md5_append(&ctx, alt_result, 16);
+ else
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Create intermediate result. */
+ md5_finish(&ctx, alt_result);
+ }
+
+ /* Now we can construct the result string. It consists of three
+ parts. */
+
+ strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
+ cp = buffer + strlen(buffer);
+ buflen -= sizeof (md5_salt_prefix);
+
+ strncpy(cp, salt, MIN ((size_t) buflen, salt_len));
+ cp = cp + strlen(cp);
+ buflen -= MIN ((size_t) buflen, salt_len);
+
+ if (buflen > 0) {
+ *cp++ = '$';
+ --buflen;
+ }
+
+#define b64_from_24bit(B2, B1, B0, N) \
+ do { \
+ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
+ int n = (N); \
+ while (n-- > 0 && buflen > 0) { \
+ *cp++ = b64t[w & 0x3f]; \
+ --buflen; \
+ w >>= 6; \
+ }\
+ } while (0)
+
+ b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
+ b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
+ b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
+ b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
+ b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
+ b64_from_24bit (0, 0, alt_result[11], 2);
+ if (buflen <= 0) {
+ FREE(buffer);
+ } else
+ *cp = '\0'; /* Terminate the string. */
+
+ /* Clear the buffer for the intermediate result so that people
+ attaching to processes or reading core dumps cannot get any
+ information. We do it in this way to clear correct_words[]
+ inside the MD5 implementation as well. */
+ md5_init(&ctx);
+ md5_finish(&ctx, alt_result);
+ memset (&ctx, '\0', sizeof (ctx));
+ memset (&alt_ctx, '\0', sizeof (alt_ctx));
+
+ return buffer;
+}
diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c
new file mode 100644
index 00000000..62ceb0af
--- /dev/null
+++ b/protocols/yahoo/libyahoo2.c
@@ -0,0 +1,4591 @@
+/*
+ * libyahoo2: libyahoo2.c
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
+ *
+ * Much of this code was taken and adapted from the yahoo module for
+ * gaim released under the GNU GPL. This code is also released under the
+ * GNU GPL.
+ *
+ * This code is derivitive of Gaim <http://gaim.sourceforge.net>
+ * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * 1998-1999, Adam Fritzler <afritz@marko.net>
+ * 1998-2002, Rob Flynn <rob@marko.net>
+ * 2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
+ * 2001-2002, Brian Macke <macke@strangelove.net>
+ * 2001, Anand Biligiri S <abiligiri@users.sf.net>
+ * 2001, Valdis Kletnieks
+ * 2002, Sean Egan <bj91704@binghamton.edu>
+ * 2002, Toby Gray <toby.gray@ntlworld.com>
+ *
+ * This library also uses code from other libraries, namely:
+ * Portions from libfaim copyright 1998, 1999 Adam Fritzler
+ * <afritz@auk.cx>
+ * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
+ * <hiro-y@kcn.ne.jp>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include <sys/types.h>
+
+#ifdef __MINGW32__
+# include <winsock2.h>
+# define write(a,b,c) send(a,b,c,0)
+# define read(a,b,c) recv(a,b,c,0)
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sha.h"
+#include "md5.h"
+#include "yahoo2.h"
+#include "yahoo_httplib.h"
+#include "yahoo_util.h"
+#include "yahoo_fn.h"
+
+#include "yahoo2_callbacks.h"
+#include "yahoo_debug.h"
+#if defined(__MINGW32__) && !defined(HAVE_GLIB)
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+#ifdef USE_STRUCT_CALLBACKS
+struct yahoo_callbacks *yc=NULL;
+
+void yahoo_register_callbacks(struct yahoo_callbacks * tyc)
+{
+ yc = tyc;
+}
+
+#define YAHOO_CALLBACK(x) yc->x
+#else
+#define YAHOO_CALLBACK(x) x
+#endif
+
+int yahoo_log_message(char * fmt, ...)
+{
+ char out[1024];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(out, sizeof(out), fmt, ap);
+ va_end(ap);
+ return YAHOO_CALLBACK(ext_yahoo_log)("%s", out);
+}
+
+int yahoo_connect(char * host, int port)
+{
+ return YAHOO_CALLBACK(ext_yahoo_connect)(host, port);
+}
+
+static enum yahoo_log_level log_level = YAHOO_LOG_NONE;
+
+enum yahoo_log_level yahoo_get_log_level()
+{
+ return log_level;
+}
+
+int yahoo_set_log_level(enum yahoo_log_level level)
+{
+ enum yahoo_log_level l = log_level;
+ log_level = level;
+ return l;
+}
+
+/* default values for servers */
+static char pager_host[] = "scs.msg.yahoo.com";
+static int pager_port = 5050;
+static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0};
+static char filetransfer_host[]="filetransfer.msg.yahoo.com";
+static int filetransfer_port=80;
+static char webcam_host[]="webcam.yahoo.com";
+static int webcam_port=5100;
+static char webcam_description[]="";
+static char local_host[]="";
+static int conn_type=Y_WCM_DSL;
+
+static char profile_url[] = "http://profiles.yahoo.com/";
+
+enum yahoo_service { /* these are easier to see in hex */
+ YAHOO_SERVICE_LOGON = 1,
+ YAHOO_SERVICE_LOGOFF,
+ YAHOO_SERVICE_ISAWAY,
+ YAHOO_SERVICE_ISBACK,
+ YAHOO_SERVICE_IDLE, /* 5 (placemarker) */
+ YAHOO_SERVICE_MESSAGE,
+ YAHOO_SERVICE_IDACT,
+ YAHOO_SERVICE_IDDEACT,
+ YAHOO_SERVICE_MAILSTAT,
+ YAHOO_SERVICE_USERSTAT, /* 0xa */
+ YAHOO_SERVICE_NEWMAIL,
+ YAHOO_SERVICE_CHATINVITE,
+ YAHOO_SERVICE_CALENDAR,
+ YAHOO_SERVICE_NEWPERSONALMAIL,
+ YAHOO_SERVICE_NEWCONTACT,
+ YAHOO_SERVICE_ADDIDENT, /* 0x10 */
+ YAHOO_SERVICE_ADDIGNORE,
+ YAHOO_SERVICE_PING,
+ YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */
+ YAHOO_SERVICE_SYSMESSAGE = 0x14,
+ YAHOO_SERVICE_PASSTHROUGH2 = 0x16,
+ YAHOO_SERVICE_CONFINVITE = 0x18,
+ YAHOO_SERVICE_CONFLOGON,
+ YAHOO_SERVICE_CONFDECLINE,
+ YAHOO_SERVICE_CONFLOGOFF,
+ YAHOO_SERVICE_CONFADDINVITE,
+ YAHOO_SERVICE_CONFMSG,
+ YAHOO_SERVICE_CHATLOGON,
+ YAHOO_SERVICE_CHATLOGOFF,
+ YAHOO_SERVICE_CHATMSG = 0x20,
+ YAHOO_SERVICE_GAMELOGON = 0x28,
+ YAHOO_SERVICE_GAMELOGOFF,
+ YAHOO_SERVICE_GAMEMSG = 0x2a,
+ YAHOO_SERVICE_FILETRANSFER = 0x46,
+ YAHOO_SERVICE_VOICECHAT = 0x4A,
+ YAHOO_SERVICE_NOTIFY,
+ YAHOO_SERVICE_VERIFY,
+ YAHOO_SERVICE_P2PFILEXFER,
+ YAHOO_SERVICE_PEERTOPEER = 0x4F, /* Checks if P2P possible */
+ YAHOO_SERVICE_WEBCAM,
+ YAHOO_SERVICE_AUTHRESP = 0x54,
+ YAHOO_SERVICE_LIST,
+ YAHOO_SERVICE_AUTH = 0x57,
+ YAHOO_SERVICE_ADDBUDDY = 0x83,
+ YAHOO_SERVICE_REMBUDDY,
+ YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/
+ YAHOO_SERVICE_REJECTCONTACT,
+ YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
+ YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
+ YAHOO_SERVICE_CHATGOTO,
+ YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */
+ YAHOO_SERVICE_CHATLEAVE,
+ YAHOO_SERVICE_CHATEXIT = 0x9b,
+ YAHOO_SERVICE_CHATLOGOUT = 0xa0,
+ YAHOO_SERVICE_CHATPING,
+ YAHOO_SERVICE_COMMENT = 0xa8
+};
+
+struct yahoo_pair {
+ int key;
+ char *value;
+};
+
+struct yahoo_packet {
+ unsigned short int service;
+ unsigned int status;
+ unsigned int id;
+ YList *hash;
+};
+
+struct yahoo_search_state {
+ int lsearch_type;
+ char *lsearch_text;
+ int lsearch_gender;
+ int lsearch_agerange;
+ int lsearch_photo;
+ int lsearch_yahoo_only;
+ int lsearch_nstart;
+ int lsearch_nfound;
+ int lsearch_ntotal;
+};
+
+struct data_queue {
+ unsigned char *queue;
+ int len;
+};
+
+struct yahoo_input_data {
+ struct yahoo_data *yd;
+ struct yahoo_webcam *wcm;
+ struct yahoo_webcam_data *wcd;
+ struct yahoo_search_state *ys;
+
+ int fd;
+ enum yahoo_connection_type type;
+
+ unsigned char *rxqueue;
+ int rxlen;
+ int read_tag;
+
+ YList *txqueues;
+ int write_tag;
+};
+
+struct yahoo_server_settings {
+ char *pager_host;
+ int pager_port;
+ char *filetransfer_host;
+ int filetransfer_port;
+ char *webcam_host;
+ int webcam_port;
+ char *webcam_description;
+ char *local_host;
+ int conn_type;
+};
+
+static void * _yahoo_default_server_settings()
+{
+ struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1);
+
+ yss->pager_host = strdup(pager_host);
+ yss->pager_port = pager_port;
+ yss->filetransfer_host = strdup(filetransfer_host);
+ yss->filetransfer_port = filetransfer_port;
+ yss->webcam_host = strdup(webcam_host);
+ yss->webcam_port = webcam_port;
+ yss->webcam_description = strdup(webcam_description);
+ yss->local_host = strdup(local_host);
+ yss->conn_type = conn_type;
+
+ return yss;
+}
+
+static void * _yahoo_assign_server_settings(va_list ap)
+{
+ struct yahoo_server_settings *yss = _yahoo_default_server_settings();
+ char *key;
+ char *svalue;
+ int nvalue;
+
+ while(1) {
+ key = va_arg(ap, char *);
+ if(key == NULL)
+ break;
+
+ if(!strcmp(key, "pager_host")) {
+ svalue = va_arg(ap, char *);
+ free(yss->pager_host);
+ yss->pager_host = strdup(svalue);
+ } else if(!strcmp(key, "pager_port")) {
+ nvalue = va_arg(ap, int);
+ yss->pager_port = nvalue;
+ } else if(!strcmp(key, "filetransfer_host")) {
+ svalue = va_arg(ap, char *);
+ free(yss->filetransfer_host);
+ yss->filetransfer_host = strdup(svalue);
+ } else if(!strcmp(key, "filetransfer_port")) {
+ nvalue = va_arg(ap, int);
+ yss->filetransfer_port = nvalue;
+ } else if(!strcmp(key, "webcam_host")) {
+ svalue = va_arg(ap, char *);
+ free(yss->webcam_host);
+ yss->webcam_host = strdup(svalue);
+ } else if(!strcmp(key, "webcam_port")) {
+ nvalue = va_arg(ap, int);
+ yss->webcam_port = nvalue;
+ } else if(!strcmp(key, "webcam_description")) {
+ svalue = va_arg(ap, char *);
+ free(yss->webcam_description);
+ yss->webcam_description = strdup(svalue);
+ } else if(!strcmp(key, "local_host")) {
+ svalue = va_arg(ap, char *);
+ free(yss->local_host);
+ yss->local_host = strdup(svalue);
+ } else if(!strcmp(key, "conn_type")) {
+ nvalue = va_arg(ap, int);
+ yss->conn_type = nvalue;
+ } else {
+ WARNING(("Unknown key passed to yahoo_init, "
+ "perhaps you didn't terminate the list "
+ "with NULL"));
+ }
+ }
+
+ return yss;
+}
+
+static void yahoo_free_server_settings(struct yahoo_server_settings *yss)
+{
+ if(!yss)
+ return;
+
+ free(yss->pager_host);
+ free(yss->filetransfer_host);
+ free(yss->webcam_host);
+ free(yss->webcam_description);
+ free(yss->local_host);
+
+ free(yss);
+}
+
+static YList *conns=NULL;
+static YList *inputs=NULL;
+static int last_id=0;
+
+static void add_to_list(struct yahoo_data *yd)
+{
+ conns = y_list_prepend(conns, yd);
+}
+static struct yahoo_data * find_conn_by_id(int id)
+{
+ YList *l;
+ for(l = conns; l; l = y_list_next(l)) {
+ struct yahoo_data *yd = l->data;
+ if(yd->client_id == id)
+ return yd;
+ }
+ return NULL;
+}
+static void del_from_list(struct yahoo_data *yd)
+{
+ conns = y_list_remove(conns, yd);
+}
+
+/* call repeatedly to get the next one */
+/*
+static struct yahoo_input_data * find_input_by_id(int id)
+{
+ YList *l;
+ for(l = inputs; l; l = y_list_next(l)) {
+ struct yahoo_input_data *yid = l->data;
+ if(yid->yd->client_id == id)
+ return yid;
+ }
+ return NULL;
+}
+*/
+
+static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who)
+{
+ YList *l;
+ LOG(("find_input_by_id_and_webcam_user"));
+ for(l = inputs; l; l = y_list_next(l)) {
+ struct yahoo_input_data *yid = l->data;
+ if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id
+ && yid->wcm &&
+ ((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) ||
+ !(yid->wcm->user && !who)))
+ return yid;
+ }
+ return NULL;
+}
+
+static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type)
+{
+ YList *l;
+ LOG(("find_input_by_id_and_type"));
+ for(l = inputs; l; l = y_list_next(l)) {
+ struct yahoo_input_data *yid = l->data;
+ if(yid->type == type && yid->yd->client_id == id)
+ return yid;
+ }
+ return NULL;
+}
+
+static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd)
+{
+ YList *l;
+ LOG(("find_input_by_id_and_fd"));
+ for(l = inputs; l; l = y_list_next(l)) {
+ struct yahoo_input_data *yid = l->data;
+ if(yid->fd == fd && yid->yd->client_id == id)
+ return yid;
+ }
+ return NULL;
+}
+
+static int count_inputs_with_id(int id)
+{
+ int c=0;
+ YList *l;
+ LOG(("counting %d", id));
+ for(l = inputs; l; l = y_list_next(l)) {
+ struct yahoo_input_data *yid = l->data;
+ if(yid->yd->client_id == id)
+ c++;
+ }
+ LOG(("%d", c));
+ return c;
+}
+
+
+extern char *yahoo_crypt(char *, char *);
+
+/* Free a buddy list */
+static void yahoo_free_buddies(YList * list)
+{
+ YList *l;
+
+ for(l = list; l; l = l->next)
+ {
+ struct yahoo_buddy *bud = l->data;
+ if(!bud)
+ continue;
+
+ FREE(bud->group);
+ FREE(bud->id);
+ FREE(bud->real_name);
+ if(bud->yab_entry) {
+ FREE(bud->yab_entry->fname);
+ FREE(bud->yab_entry->lname);
+ FREE(bud->yab_entry->nname);
+ FREE(bud->yab_entry->id);
+ FREE(bud->yab_entry->email);
+ FREE(bud->yab_entry->hphone);
+ FREE(bud->yab_entry->wphone);
+ FREE(bud->yab_entry->mphone);
+ FREE(bud->yab_entry);
+ }
+ FREE(bud);
+ l->data = bud = NULL;
+ }
+
+ y_list_free(list);
+}
+
+/* Free an identities list */
+static void yahoo_free_identities(YList * list)
+{
+ while (list) {
+ YList *n = list;
+ FREE(list->data);
+ list = y_list_remove_link(list, list);
+ y_list_free_1(n);
+ }
+}
+
+/* Free webcam data */
+static void yahoo_free_webcam(struct yahoo_webcam *wcm)
+{
+ if (wcm) {
+ FREE(wcm->user);
+ FREE(wcm->server);
+ FREE(wcm->key);
+ FREE(wcm->description);
+ FREE(wcm->my_ip);
+ }
+ FREE(wcm);
+}
+
+static void yahoo_free_data(struct yahoo_data *yd)
+{
+ FREE(yd->user);
+ FREE(yd->password);
+ FREE(yd->cookie_y);
+ FREE(yd->cookie_t);
+ FREE(yd->cookie_c);
+ FREE(yd->login_cookie);
+ FREE(yd->login_id);
+
+ yahoo_free_buddies(yd->buddies);
+ yahoo_free_buddies(yd->ignore);
+ yahoo_free_identities(yd->identities);
+
+ yahoo_free_server_settings(yd->server_settings);
+
+ FREE(yd);
+}
+
+#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
+
+static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service,
+ enum yahoo_status status, int id)
+{
+ struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1);
+
+ pkt->service = service;
+ pkt->status = status;
+ pkt->id = id;
+
+ return pkt;
+}
+
+static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value)
+{
+ struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
+ pair->key = key;
+ pair->value = strdup(value);
+ pkt->hash = y_list_append(pkt->hash, pair);
+}
+
+static int yahoo_packet_length(struct yahoo_packet *pkt)
+{
+ YList *l;
+
+ int len = 0;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ int tmp = pair->key;
+ do {
+ tmp /= 10;
+ len++;
+ } while (tmp);
+ len += 2;
+ len += strlen(pair->value);
+ len += 2;
+ }
+
+ return len;
+}
+
+#define yahoo_put16(buf, data) ( \
+ (*(buf) = (unsigned char)((data)>>8)&0xff), \
+ (*((buf)+1) = (unsigned char)(data)&0xff), \
+ 2)
+#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
+#define yahoo_put32(buf, data) ( \
+ (*((buf)) = (unsigned char)((data)>>24)&0xff), \
+ (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+ (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+ (*((buf)+3) = (unsigned char)(data)&0xff), \
+ 4)
+#define yahoo_get32(buf) ((((*(buf) )&0xff)<<24) + \
+ (((*((buf)+1))&0xff)<<16) + \
+ (((*((buf)+2))&0xff)<< 8) + \
+ (((*((buf)+3))&0xff)))
+
+static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len)
+{
+ int pos = 0;
+
+ while (pos + 1 < len) {
+ char *key, *value = NULL;
+ int accept;
+ int x;
+
+ struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
+
+ key = malloc(len + 1);
+ x = 0;
+ while (pos + 1 < len) {
+ if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
+ break;
+ key[x++] = data[pos++];
+ }
+ key[x] = 0;
+ pos += 2;
+ pair->key = strtol(key, NULL, 10);
+ free(key);
+
+ /* Libyahoo2 developer(s) don't seem to have the time to fix
+ this problem, so for now try to work around it:
+
+ Sometimes we receive an invalid packet with not any more
+ data at this point. I don't know how to handle this in a
+ clean way, but let's hope this is clean enough: */
+
+ if (pos + 1 < len) {
+ accept = x;
+ /* if x is 0 there was no key, so don't accept it */
+ if (accept)
+ value = malloc(len - pos + 1);
+ x = 0;
+ while (pos + 1 < len) {
+ if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
+ break;
+ if (accept)
+ value[x++] = data[pos++];
+ }
+ if (accept)
+ value[x] = 0;
+ pos += 2;
+ } else {
+ accept = 0;
+ }
+
+ if (accept) {
+ pair->value = strdup(value);
+ FREE(value);
+ pkt->hash = y_list_append(pkt->hash, pair);
+ DEBUG_MSG(("Key: %d \tValue: %s", pair->key, pair->value));
+ } else {
+ FREE(pair);
+ }
+ }
+}
+
+static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data)
+{
+ YList *l;
+ int pos = 0;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ unsigned char buf[100];
+
+ snprintf((char *)buf, sizeof(buf), "%d", pair->key);
+ strcpy((char *)data + pos, (char *)buf);
+ pos += strlen((char *)buf);
+ data[pos++] = 0xc0;
+ data[pos++] = 0x80;
+
+ strcpy((char *)data + pos, pair->value);
+ pos += strlen(pair->value);
+ data[pos++] = 0xc0;
+ data[pos++] = 0x80;
+ }
+}
+
+static void yahoo_dump_unhandled(struct yahoo_packet *pkt)
+{
+ YList *l;
+
+ NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status));
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ NOTICE(("\t%d => %s", pair->key, pair->value));
+ }
+}
+
+
+static void yahoo_packet_dump(unsigned char *data, int len)
+{
+ if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if ((i % 8 == 0) && i)
+ YAHOO_CALLBACK(ext_yahoo_log)(" ");
+ if ((i % 16 == 0) && i)
+ YAHOO_CALLBACK(ext_yahoo_log)("\n");
+ YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]);
+ }
+ YAHOO_CALLBACK(ext_yahoo_log)("\n");
+ for (i = 0; i < len; i++) {
+ if ((i % 8 == 0) && i)
+ YAHOO_CALLBACK(ext_yahoo_log)(" ");
+ if ((i % 16 == 0) && i)
+ YAHOO_CALLBACK(ext_yahoo_log)("\n");
+ if (isprint(data[i]))
+ YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]);
+ else
+ YAHOO_CALLBACK(ext_yahoo_log)(" . ");
+ }
+ YAHOO_CALLBACK(ext_yahoo_log)("\n");
+ }
+}
+
+static char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789._";
+static void to_y64(unsigned char *out, const unsigned char *in, int inlen)
+/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
+{
+ for (; inlen >= 3; inlen -= 3)
+ {
+ *out++ = base64digits[in[0] >> 2];
+ *out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ *out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)];
+ *out++ = base64digits[in[2] & 0x3f];
+ in += 3;
+ }
+ if (inlen > 0)
+ {
+ unsigned char fragment;
+
+ *out++ = base64digits[in[0] >> 2];
+ fragment = (in[0] << 4) & 0x30;
+ if (inlen > 1)
+ fragment |= in[1] >> 4;
+ *out++ = base64digits[fragment];
+ *out++ = (inlen < 2) ? '-'
+ : base64digits[(in[1] << 2) & 0x3c];
+ *out++ = '-';
+ }
+ *out = '\0';
+}
+
+static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length)
+{
+ struct data_queue *tx = y_new0(struct data_queue, 1);
+ tx->queue = y_new0(unsigned char, length);
+ tx->len = length;
+ memcpy(tx->queue, data, length);
+
+ yid->txqueues = y_list_append(yid->txqueues, tx);
+
+ if(!yid->write_tag)
+ yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid);
+}
+
+static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad)
+{
+ int pktlen = yahoo_packet_length(pkt);
+ int len = YAHOO_PACKET_HDRLEN + pktlen;
+
+ unsigned char *data;
+ int pos = 0;
+
+ if (yid->fd < 0)
+ return;
+
+ data = y_new0(unsigned char, len + 1);
+
+ memcpy(data + pos, "YMSG", 4); pos += 4;
+ pos += yahoo_put16(data + pos, 0x0a00);
+ pos += yahoo_put16(data + pos, 0x0000);
+ pos += yahoo_put16(data + pos, pktlen + extra_pad);
+ pos += yahoo_put16(data + pos, pkt->service);
+ pos += yahoo_put32(data + pos, pkt->status);
+ pos += yahoo_put32(data + pos, pkt->id);
+
+ yahoo_packet_write(pkt, data + pos);
+
+ yahoo_packet_dump(data, len);
+
+ yahoo_add_to_send_queue(yid, data, len);
+ FREE(data);
+}
+
+static void yahoo_packet_free(struct yahoo_packet *pkt)
+{
+ while (pkt->hash) {
+ struct yahoo_pair *pair = pkt->hash->data;
+ YList *tmp;
+ FREE(pair->value);
+ FREE(pair);
+ tmp = pkt->hash;
+ pkt->hash = y_list_remove_link(pkt->hash, pkt->hash);
+ y_list_free_1(tmp);
+ }
+ FREE(pkt);
+}
+
+static int yahoo_send_data(int fd, void *data, int len)
+{
+ int ret;
+ int e;
+
+ if (fd < 0)
+ return -1;
+
+ yahoo_packet_dump(data, len);
+
+ do {
+ ret = write(fd, data, len);
+ } while(ret == -1 && errno==EINTR);
+ e=errno;
+
+ if (ret == -1) {
+ LOG(("wrote data: ERR %s", strerror(errno)));
+ } else {
+ LOG(("wrote data: OK"));
+ }
+
+ errno=e;
+ return ret;
+}
+
+void yahoo_close(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return;
+
+ del_from_list(yd);
+
+ yahoo_free_data(yd);
+ if(id == last_id)
+ last_id--;
+}
+
+static void yahoo_input_close(struct yahoo_input_data *yid)
+{
+ inputs = y_list_remove(inputs, yid);
+
+ LOG(("yahoo_input_close(read)"));
+ YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag);
+ LOG(("yahoo_input_close(write)"));
+ YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag);
+ yid->read_tag = yid->write_tag = 0;
+ if(yid->fd)
+ close(yid->fd);
+ yid->fd = 0;
+ FREE(yid->rxqueue);
+ if(count_inputs_with_id(yid->yd->client_id) == 0) {
+ LOG(("closing %d", yid->yd->client_id));
+ yahoo_close(yid->yd->client_id);
+ }
+ yahoo_free_webcam(yid->wcm);
+ if(yid->wcd)
+ FREE(yid->wcd);
+ if(yid->ys) {
+ FREE(yid->ys->lsearch_text);
+ FREE(yid->ys);
+ }
+ FREE(yid);
+}
+
+static int is_same_bud(const void * a, const void * b) {
+ const struct yahoo_buddy *subject = a;
+ const struct yahoo_buddy *object = b;
+
+ return strcmp(subject->id, object->id);
+}
+
+static YList * bud_str2list(char *rawlist)
+{
+ YList * l = NULL;
+
+ char **lines;
+ char **split;
+ char **buddies;
+ char **tmp, **bud;
+
+ lines = y_strsplit(rawlist, "\n", -1);
+ for (tmp = lines; *tmp; tmp++) {
+ struct yahoo_buddy *newbud;
+
+ split = y_strsplit(*tmp, ":", 2);
+ if (!split)
+ continue;
+ if (!split[0] || !split[1]) {
+ y_strfreev(split);
+ continue;
+ }
+ buddies = y_strsplit(split[1], ",", -1);
+
+ for (bud = buddies; bud && *bud; bud++) {
+ newbud = y_new0(struct yahoo_buddy, 1);
+ newbud->id = strdup(*bud);
+ newbud->group = strdup(split[0]);
+
+ if(y_list_find_custom(l, newbud, is_same_bud)) {
+ FREE(newbud->id);
+ FREE(newbud->group);
+ FREE(newbud);
+ continue;
+ }
+
+ newbud->real_name = NULL;
+
+ l = y_list_append(l, newbud);
+
+ NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
+ }
+
+ y_strfreev(buddies);
+ y_strfreev(split);
+ }
+ y_strfreev(lines);
+
+ return l;
+}
+
+static char * getcookie(char *rawcookie)
+{
+ char * cookie=NULL;
+ char * tmpcookie;
+ char * cookieend;
+
+ if (strlen(rawcookie) < 2)
+ return NULL;
+
+ tmpcookie = strdup(rawcookie+2);
+ cookieend = strchr(tmpcookie, ';');
+
+ if(cookieend)
+ *cookieend = '\0';
+
+ cookie = strdup(tmpcookie);
+ FREE(tmpcookie);
+ /* cookieend=NULL; not sure why this was there since the value is not preserved in the stack -dd */
+
+ return cookie;
+}
+
+static char * getlcookie(char *cookie)
+{
+ char *tmp;
+ char *tmpend;
+ char *login_cookie = NULL;
+
+ tmpend = strstr(cookie, "n=");
+ if(tmpend) {
+ tmp = strdup(tmpend+2);
+ tmpend = strchr(tmp, '&');
+ if(tmpend)
+ *tmpend='\0';
+ login_cookie = strdup(tmp);
+ FREE(tmp);
+ }
+
+ return login_cookie;
+}
+
+static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *msg = NULL;
+ char *from = NULL;
+ int stat = 0;
+ int accept = 0;
+ char *ind = NULL;
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 4)
+ from = pair->value;
+ if (pair->key == 49)
+ msg = pair->value;
+ if (pair->key == 13)
+ stat = atoi(pair->value);
+ if (pair->key == 14)
+ ind = pair->value;
+ if (pair->key == 16) { /* status == -1 */
+ NOTICE((pair->value));
+ return;
+ }
+
+ }
+
+ if (!msg)
+ return;
+
+ if (!strncasecmp(msg, "TYPING", strlen("TYPING")))
+ YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, from, stat);
+ else if (!strncasecmp(msg, "GAME", strlen("GAME")))
+ YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, from, stat);
+ else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE")))
+ {
+ if (!strcmp(ind, " ")) {
+ YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, from);
+ } else {
+ accept = atoi(ind);
+ /* accept the invitation (-1 = deny 1 = accept) */
+ if (accept < 0)
+ accept = 0;
+ YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, from, accept);
+ }
+ }
+ else
+ LOG(("Got unknown notification: %s", msg));
+}
+
+static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *from=NULL;
+ char *to=NULL;
+ char *msg=NULL;
+ char *url=NULL;
+ long expires=0;
+
+ char *service=NULL;
+
+ char *filename=NULL;
+ unsigned long filesize=0L;
+
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 4)
+ from = pair->value;
+ if (pair->key == 5)
+ to = pair->value;
+ if (pair->key == 14)
+ msg = pair->value;
+ if (pair->key == 20)
+ url = pair->value;
+ if (pair->key == 38)
+ expires = atol(pair->value);
+
+ if (pair->key == 27)
+ filename = pair->value;
+ if (pair->key == 28)
+ filesize = atol(pair->value);
+
+ if (pair->key == 49)
+ service = pair->value;
+ }
+
+ if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
+ if(strcmp("FILEXFER", service) != 0) {
+ WARNING(("unhandled service 0x%02x", pkt->service));
+ yahoo_dump_unhandled(pkt);
+ return;
+ }
+ }
+
+ if(msg) {
+ char *tmp;
+ tmp = strchr(msg, '\006');
+ if(tmp)
+ *tmp = '\0';
+ }
+ if(url && from)
+ YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, from, url, expires, msg, filename, filesize);
+
+}
+
+static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *msg = NULL;
+ char *host = NULL;
+ char *who = NULL;
+ char *room = NULL;
+ char *id = NULL;
+ int utf8 = 0;
+ YList *members = NULL;
+ YList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 50)
+ host = pair->value;
+
+ if (pair->key == 52) { /* invite */
+ members = y_list_append(members, strdup(pair->value));
+ }
+ if (pair->key == 53) /* logon */
+ who = pair->value;
+ if (pair->key == 54) /* decline */
+ who = pair->value;
+ if (pair->key == 55) /* unavailable (status == 2) */
+ who = pair->value;
+ if (pair->key == 56) /* logoff */
+ who = pair->value;
+
+ if (pair->key == 57)
+ room = pair->value;
+
+ if (pair->key == 58) /* join message */
+ msg = pair->value;
+ if (pair->key == 14) /* decline/conf message */
+ msg = pair->value;
+
+ if (pair->key == 13)
+ ;
+ if (pair->key == 16) /* error */
+ msg = pair->value;
+
+ if (pair->key == 1) /* my id */
+ id = pair->value;
+ if (pair->key == 3) /* message sender */
+ who = pair->value;
+
+ if (pair->key == 97)
+ utf8 = atoi(pair->value);
+ }
+
+ if(!room)
+ return;
+
+ if(host) {
+ for(l = members; l; l = l->next) {
+ char * w = l->data;
+ if(!strcmp(w, host))
+ break;
+ }
+ if(!l)
+ members = y_list_append(members, strdup(host));
+ }
+ /* invite, decline, join, left, message -> status == 1 */
+
+ switch(pkt->service) {
+ case YAHOO_SERVICE_CONFINVITE:
+ if(pkt->status == 2)
+ ;
+ else if(members)
+ YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
+ else if(msg)
+ YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0);
+ break;
+ case YAHOO_SERVICE_CONFADDINVITE:
+ if(pkt->status == 2)
+ ;
+ else
+ YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
+ break;
+ case YAHOO_SERVICE_CONFDECLINE:
+ if(who)
+ YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, who, room, msg);
+ break;
+ case YAHOO_SERVICE_CONFLOGON:
+ if(who)
+ YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, who, room);
+ break;
+ case YAHOO_SERVICE_CONFLOGOFF:
+ if(who)
+ YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, who, room);
+ break;
+ case YAHOO_SERVICE_CONFMSG:
+ if(who)
+ YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, who, room, msg, utf8);
+ break;
+ }
+}
+
+static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char *msg = NULL;
+ char *who = NULL;
+ char *room = NULL;
+ char *topic = NULL;
+ YList *members = NULL;
+ struct yahoo_chat_member *currentmember = NULL;
+ int msgtype = 1;
+ int utf8 = 0;
+ int firstjoin = 0;
+ int membercount = 0;
+ int chaterr=0;
+ YList *l;
+
+ yahoo_dump_unhandled(pkt);
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ if (pair->key == 104) {
+ /* Room name */
+ room = pair->value;
+ }
+
+ if (pair->key == 105) {
+ /* Room topic */
+ topic = pair->value;
+ }
+
+ if (pair->key == 108) {
+ /* Number of members in this packet */
+ membercount = atoi(pair->value);
+ }
+
+ if (pair->key == 109) {
+ /* message sender */
+ who = pair->value;
+
+ if (pkt->service == YAHOO_SERVICE_CHATJOIN) {
+ currentmember = y_new0(struct yahoo_chat_member, 1);
+ currentmember->id = strdup(pair->value);
+ members = y_list_append(members, currentmember);
+ }
+ }
+
+ if (pair->key == 110) {
+ /* age */
+ if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+ currentmember->age = atoi(pair->value);
+ }
+
+ if (pair->key == 113) {
+ /* attribs */
+ if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+ currentmember->attribs = atoi(pair->value);
+ }
+
+ if (pair->key == 141) {
+ /* alias */
+ if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+ currentmember->alias = strdup(pair->value);
+ }
+
+ if (pair->key == 142) {
+ /* location */
+ if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+ currentmember->location = strdup(pair->value);
+ }
+
+
+ if (pair->key == 130) {
+ /* first join */
+ firstjoin = 1;
+ }
+
+ if (pair->key == 117) {
+ /* message */
+ msg = pair->value;
+ }
+
+ if (pair->key == 124) {
+ /* Message type */
+ msgtype = atoi(pair->value);
+ }
+ if (pair->key == 114) {
+ /* message error not sure what all the pair values mean */
+ /* but -1 means no session in room */
+ chaterr= atoi(pair->value);
+ }
+ }
+
+ if(!room) {
+ if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */
+ YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id);
+ return ;
+ }
+ if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) {
+ YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id);
+ return ;
+ }
+
+ WARNING(("We didn't get a room name, ignoring packet"));
+ return;
+ }
+
+ switch(pkt->service) {
+ case YAHOO_SERVICE_CHATJOIN:
+ if(y_list_length(members) != membercount) {
+ WARNING(("Count of members doesn't match No. of members we got"));
+ }
+ if(firstjoin && members) {
+ YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, room, topic, members, yid->fd);
+ } else if(who) {
+ if(y_list_length(members) != 1) {
+ WARNING(("Got more than 1 member on a normal join"));
+ }
+ /* this should only ever have one, but just in case */
+ while(members) {
+ YList *n = members->next;
+ currentmember = members->data;
+ YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, room, currentmember);
+ y_list_free_1(members);
+ members=n;
+ }
+ }
+ break;
+ case YAHOO_SERVICE_CHATEXIT:
+ if(who) {
+ YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, room, who);
+ }
+ break;
+ case YAHOO_SERVICE_COMMENT:
+ if(who) {
+ YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, who, room, msg, msgtype, utf8);
+ }
+ break;
+ }
+}
+
+static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ YList *l;
+ YList * messages = NULL;
+
+ struct m {
+ int i_31;
+ int i_32;
+ char *to;
+ char *from;
+ long tm;
+ char *msg;
+ int utf8;
+ } *message = y_new0(struct m, 1);
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 1 || pair->key == 4)
+ {
+ if(!message->from)
+ message->from = pair->value;
+ }
+ else if (pair->key == 5)
+ message->to = pair->value;
+ else if (pair->key == 15)
+ message->tm = strtol(pair->value, NULL, 10);
+ else if (pair->key == 97)
+ message->utf8 = atoi(pair->value);
+ /* user message */ /* sys message */
+ else if (pair->key == 14 || pair->key == 16)
+ message->msg = pair->value;
+ else if (pair->key == 31) {
+ if(message->i_31) {
+ messages = y_list_append(messages, message);
+ message = y_new0(struct m, 1);
+ }
+ message->i_31 = atoi(pair->value);
+ }
+ else if (pair->key == 32)
+ message->i_32 = atoi(pair->value);
+ else
+ LOG(("yahoo_process_message: status: %d, key: %d, value: %s",
+ pkt->status, pair->key, pair->value));
+ }
+
+ messages = y_list_append(messages, message);
+
+ for (l = messages; l; l=l->next) {
+ message = l->data;
+ if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) {
+ YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg);
+ } else if (pkt->status <= 2 || pkt->status == 5) {
+ YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->from, message->msg, message->tm, pkt->status, message->utf8);
+ } else if (pkt->status == 0xffffffff) {
+ YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0);
+ }
+ free(message);
+ }
+
+ y_list_free(messages);
+}
+
+
+static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ YList *l;
+ struct yahoo_data *yd = yid->yd;
+ char *name = NULL;
+ int state = 0;
+ int away = 0;
+ int idle = 0;
+ char *msg = NULL;
+
+ if(pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
+ return;
+ }
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 0: /* we won't actually do anything with this */
+ NOTICE(("key %d:%s", pair->key, pair->value));
+ break;
+ case 1: /* we don't get the full buddy list here. */
+ if (!yd->logged_in) {
+ yd->logged_in = TRUE;
+ if(yd->current_status < 0)
+ yd->current_status = yd->initial_status;
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+ }
+ break;
+ case 8: /* how many online buddies we have */
+ NOTICE(("key %d:%s", pair->key, pair->value));
+ break;
+ case 7: /* the current buddy */
+ name = pair->value;
+ break;
+ case 10: /* state */
+ state = strtol(pair->value, NULL, 10);
+ break;
+ case 19: /* custom status message */
+ msg = pair->value;
+ break;
+ case 47: /* is it an away message or not */
+ away = atoi(pair->value);
+ break;
+ case 137: /* seconds idle */
+ idle = atoi(pair->value);
+ break;
+ case 11: /* what is this? */
+ NOTICE(("key %d:%s", pair->key, pair->value));
+ break;
+ case 17: /* in chat? */
+ break;
+ case 13: /* in pager? */
+ if (pkt->service == YAHOO_SERVICE_LOGOFF || strtol(pair->value, NULL, 10) == 0) {
+ YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, YAHOO_STATUS_OFFLINE, NULL, 1);
+ break;
+ }
+ if (state == YAHOO_STATUS_AVAILABLE) {
+ YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, 0);
+ } else if (state == YAHOO_STATUS_CUSTOM) {
+ YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
+ } else {
+ YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, idle);
+ }
+
+ break;
+ case 60:
+ /* sometimes going offline makes this 2, but invisible never sends it */
+ NOTICE(("key %d:%s", pair->key, pair->value));
+ break;
+ case 16: /* Custom error message */
+ YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0);
+ break;
+ default:
+ WARNING(("unknown status key %d:%s", pair->key, pair->value));
+ break;
+ }
+ }
+}
+
+static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ YList *l;
+
+ if (!yd->logged_in) {
+ yd->logged_in = TRUE;
+ if(yd->current_status < 0)
+ yd->current_status = yd->initial_status;
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+ }
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch(pair->key) {
+ case 87: /* buddies */
+ if(!yd->rawbuddylist)
+ yd->rawbuddylist = strdup(pair->value);
+ else {
+ yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
+ }
+ break;
+
+ case 88: /* ignore list */
+ if(!yd->ignorelist)
+ yd->ignorelist = strdup("Ignore:");
+ yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
+ break;
+
+ case 89: /* identities */
+ {
+ char **identities = y_strsplit(pair->value, ",", -1);
+ int i;
+ for(i=0; identities[i]; i++)
+ yd->identities = y_list_append(yd->identities,
+ strdup(identities[i]));
+ y_strfreev(identities);
+ }
+ YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
+ break;
+ case 59: /* cookies */
+ if(yd->ignorelist) {
+ yd->ignore = bud_str2list(yd->ignorelist);
+ FREE(yd->ignorelist);
+ YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
+ }
+ if(yd->rawbuddylist) {
+ yd->buddies = bud_str2list(yd->rawbuddylist);
+ FREE(yd->rawbuddylist);
+ YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+ }
+
+ if(pair->value[0]=='Y') {
+ FREE(yd->cookie_y);
+ FREE(yd->login_cookie);
+
+ yd->cookie_y = getcookie(pair->value);
+ yd->login_cookie = getlcookie(yd->cookie_y);
+
+ } else if(pair->value[0]=='T') {
+ FREE(yd->cookie_t);
+ yd->cookie_t = getcookie(pair->value);
+
+ } else if(pair->value[0]=='C') {
+ FREE(yd->cookie_c);
+ yd->cookie_c = getcookie(pair->value);
+ }
+
+ if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
+ YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
+
+ break;
+ case 3: /* my id */
+ case 90: /* 1 */
+ case 100: /* 0 */
+ case 101: /* NULL */
+ case 102: /* NULL */
+ case 93: /* 86400/1440 */
+ break;
+ }
+ }
+}
+
+static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+
+ if(pkt->status != 0x01) {
+ DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status));
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, "");
+ return;
+ }
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+
+}
+
+static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid,
+ const char *seed, const char *sn)
+{
+ struct yahoo_data *yd = yid->yd;
+
+ /* So, Yahoo has stopped supporting its older clients in India, and
+ * undoubtedly will soon do so in the rest of the world.
+ *
+ * The new clients use this authentication method. I warn you in
+ * advance, it's bizzare, convoluted, inordinately complicated.
+ * It's also no more secure than crypt() was. The only purpose this
+ * scheme could serve is to prevent third part clients from connecting
+ * to their servers.
+ *
+ * Sorry, Yahoo.
+ */
+
+ struct yahoo_packet *pack;
+
+ md5_byte_t result[16];
+ md5_state_t ctx;
+ char *crypt_result;
+ unsigned char *password_hash = malloc(25);
+ unsigned char *crypt_hash = malloc(25);
+ unsigned char *hash_string_p = malloc(50 + strlen(sn));
+ unsigned char *hash_string_c = malloc(50 + strlen(sn));
+
+ char checksum;
+
+ int sv;
+
+ unsigned char *result6 = malloc(25);
+ unsigned char *result96 = malloc(25);
+
+ sv = seed[15];
+ sv = (sv % 8) % 5;
+
+ md5_init(&ctx);
+ md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password));
+ md5_finish(&ctx, result);
+ to_y64(password_hash, result, 16);
+
+ md5_init(&ctx);
+ crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");
+ md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
+ md5_finish(&ctx, result);
+ to_y64(crypt_hash, result, 16);
+ free(crypt_result);
+
+ switch (sv) {
+ case 0:
+ checksum = seed[seed[7] % 16];
+ snprintf((char *)hash_string_p, strlen(sn) + 50,
+ "%c%s%s%s", checksum, password_hash, yd->user, seed);
+ snprintf((char *)hash_string_c, strlen(sn) + 50,
+ "%c%s%s%s", checksum, crypt_hash, yd->user, seed);
+ break;
+ case 1:
+ checksum = seed[seed[9] % 16];
+ snprintf((char *)hash_string_p, strlen(sn) + 50,
+ "%c%s%s%s", checksum, yd->user, seed, password_hash);
+ snprintf((char *)hash_string_c, strlen(sn) + 50,
+ "%c%s%s%s", checksum, yd->user, seed, crypt_hash);
+ break;
+ case 2:
+ checksum = seed[seed[15] % 16];
+ snprintf((char *)hash_string_p, strlen(sn) + 50,
+ "%c%s%s%s", checksum, seed, password_hash, yd->user);
+ snprintf((char *)hash_string_c, strlen(sn) + 50,
+ "%c%s%s%s", checksum, seed, crypt_hash, yd->user);
+ break;
+ case 3:
+ checksum = seed[seed[1] % 16];
+ snprintf((char *)hash_string_p, strlen(sn) + 50,
+ "%c%s%s%s", checksum, yd->user, password_hash, seed);
+ snprintf((char *)hash_string_c, strlen(sn) + 50,
+ "%c%s%s%s", checksum, yd->user, crypt_hash, seed);
+ break;
+ case 4:
+ checksum = seed[seed[3] % 16];
+ snprintf((char *)hash_string_p, strlen(sn) + 50,
+ "%c%s%s%s", checksum, password_hash, seed, yd->user);
+ snprintf((char *)hash_string_c, strlen(sn) + 50,
+ "%c%s%s%s", checksum, crypt_hash, seed, yd->user);
+ break;
+ }
+
+ md5_init(&ctx);
+ md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p));
+ md5_finish(&ctx, result);
+ to_y64(result6, result, 16);
+
+ md5_init(&ctx);
+ md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c));
+ md5_finish(&ctx, result);
+ to_y64(result96, result, 16);
+
+ pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+ yahoo_packet_hash(pack, 0, yd->user);
+ yahoo_packet_hash(pack, 6, (char *)result6);
+ yahoo_packet_hash(pack, 96, (char *)result96);
+ yahoo_packet_hash(pack, 1, yd->user);
+
+ yahoo_send_packet(yid, pack, 0);
+
+ FREE(result6);
+ FREE(result96);
+ FREE(password_hash);
+ FREE(crypt_hash);
+ FREE(hash_string_p);
+ FREE(hash_string_c);
+
+ yahoo_packet_free(pack);
+
+}
+
+/*
+ * New auth protocol cracked by Cerulean Studios and sent in to Gaim
+ */
+static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn)
+{
+ struct yahoo_packet *pack = NULL;
+ struct yahoo_data *yd = yid->yd;
+
+ md5_byte_t result[16];
+ md5_state_t ctx;
+
+ SHA_CTX ctx1;
+ SHA_CTX ctx2;
+
+ char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ";
+ char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop";
+
+ char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5";
+ char *operand_lookup = "+|&%/*^-";
+ char *delimit_lookup = ",;";
+
+ unsigned char *password_hash = malloc(25);
+ unsigned char *crypt_hash = malloc(25);
+ char *crypt_result = NULL;
+ unsigned char pass_hash_xor1[64];
+ unsigned char pass_hash_xor2[64];
+ unsigned char crypt_hash_xor1[64];
+ unsigned char crypt_hash_xor2[64];
+ unsigned char chal[7];
+ char resp_6[100];
+ char resp_96[100];
+
+ unsigned char digest1[20];
+ unsigned char digest2[20];
+ unsigned char magic_key_char[4];
+ const unsigned char *magic_ptr;
+
+ unsigned int magic[64];
+ unsigned int magic_work=0;
+
+ char comparison_src[20];
+
+ int x, j, i;
+ int cnt = 0;
+ int magic_cnt = 0;
+ int magic_len;
+ int depth =0, table =0;
+
+ memset(&pass_hash_xor1, 0, 64);
+ memset(&pass_hash_xor2, 0, 64);
+ memset(&crypt_hash_xor1, 0, 64);
+ memset(&crypt_hash_xor2, 0, 64);
+ memset(&digest1, 0, 20);
+ memset(&digest2, 0, 20);
+ memset(&magic, 0, 64);
+ memset(&resp_6, 0, 100);
+ memset(&resp_96, 0, 100);
+ memset(&magic_key_char, 0, 4);
+
+ /*
+ * Magic: Phase 1. Generate what seems to be a 30
+ * byte value (could change if base64
+ * ends up differently? I don't remember and I'm
+ * tired, so use a 64 byte buffer.
+ */
+
+ magic_ptr = (unsigned char *)seed;
+
+ while (*magic_ptr != (int)NULL) {
+ char *loc;
+
+ /* Ignore parentheses. */
+
+ if (*magic_ptr == '(' || *magic_ptr == ')') {
+ magic_ptr++;
+ continue;
+ }
+
+ /* Characters and digits verify against
+ the challenge lookup.
+ */
+
+ if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) {
+ loc = strchr(challenge_lookup, *magic_ptr);
+ if (!loc) {
+ /* This isn't good */
+ continue;
+ }
+
+ /* Get offset into lookup table and lsh 3. */
+
+ magic_work = loc - challenge_lookup;
+ magic_work <<= 3;
+
+ magic_ptr++;
+ continue;
+ } else {
+ unsigned int local_store;
+
+ loc = strchr(operand_lookup, *magic_ptr);
+ if (!loc) {
+ /* Also not good. */
+ continue;
+ }
+
+ local_store = loc - operand_lookup;
+
+ /* Oops; how did this happen? */
+ if (magic_cnt >= 64)
+ break;
+
+ magic[magic_cnt++] = magic_work | local_store;
+ magic_ptr++;
+ continue;
+ }
+ }
+
+ magic_len = magic_cnt;
+ magic_cnt = 0;
+
+ /* Magic: Phase 2. Take generated magic value and
+ * sprinkle fairy dust on the values. */
+
+ for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) {
+ unsigned char byte1;
+ unsigned char byte2;
+
+ /* Bad. Abort.
+ */
+ if (magic_cnt >= magic_len) {
+ WARNING(("magic_cnt(%d) magic_len(%d)", magic_cnt, magic_len))
+ break;
+ }
+
+ byte1 = magic[magic_cnt];
+ byte2 = magic[magic_cnt+1];
+
+ byte1 *= 0xcd;
+ byte1 ^= byte2;
+
+ magic[magic_cnt+1] = byte1;
+ }
+
+ /* Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic
+ * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key
+ * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets
+ * into particular functions we'll later call to potentially alter the magic key.
+ *
+ * %-)
+ */
+
+ magic_cnt = 1;
+ x = 0;
+
+ do {
+ unsigned int bl = 0;
+ unsigned int cl = magic[magic_cnt++];
+
+ if (magic_cnt >= magic_len)
+ break;
+
+ if (cl > 0x7F) {
+ if (cl < 0xe0)
+ bl = cl = (cl & 0x1f) << 6;
+ else {
+ bl = magic[magic_cnt++];
+ cl = (cl & 0x0f) << 6;
+ bl = ((bl & 0x3f) + cl) << 6;
+ }
+
+ cl = magic[magic_cnt++];
+ bl = (cl & 0x3f) + bl;
+ } else
+ bl = cl;
+
+ comparison_src[x++] = (bl & 0xff00) >> 8;
+ comparison_src[x++] = bl & 0xff;
+ } while (x < 20);
+
+ /* Dump magic key into a char for SHA1 action. */
+
+
+ for(x = 0; x < 4; x++)
+ magic_key_char[x] = comparison_src[x];
+
+ /* Compute values for recursive function table! */
+ memcpy( chal, magic_key_char, 4 );
+ x = 1;
+ for( i = 0; i < 0xFFFF && x; i++ )
+ {
+ for( j = 0; j < 5 && x; j++ )
+ {
+ chal[4] = i;
+ chal[5] = i >> 8;
+ chal[6] = j;
+ md5_init( &ctx );
+ md5_append( &ctx, chal, 7 );
+ md5_finish( &ctx, result );
+ if( memcmp( comparison_src + 4, result, 16 ) == 0 )
+ {
+ depth = i;
+ table = j;
+ x = 0;
+ }
+ }
+ }
+
+ /* Transform magic_key_char using transform table */
+ x = magic_key_char[3] << 24 | magic_key_char[2] << 16
+ | magic_key_char[1] << 8 | magic_key_char[0];
+ x = yahoo_xfrm( table, depth, x );
+ x = yahoo_xfrm( table, depth, x );
+ magic_key_char[0] = x & 0xFF;
+ magic_key_char[1] = x >> 8 & 0xFF;
+ magic_key_char[2] = x >> 16 & 0xFF;
+ magic_key_char[3] = x >> 24 & 0xFF;
+
+ /* Get password and crypt hashes as per usual. */
+ md5_init(&ctx);
+ md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password));
+ md5_finish(&ctx, result);
+ to_y64(password_hash, result, 16);
+
+ md5_init(&ctx);
+ crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");
+ md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
+ md5_finish(&ctx, result);
+ to_y64(crypt_hash, result, 16);
+ free(crypt_result);
+
+ /* Our first authentication response is based off
+ * of the password hash. */
+
+ for (x = 0; x < (int)strlen((char *)password_hash); x++)
+ pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36;
+
+ if (cnt < 64)
+ memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt);
+
+ cnt = 0;
+
+ for (x = 0; x < (int)strlen((char *)password_hash); x++)
+ pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c;
+
+ if (cnt < 64)
+ memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+ shaInit(&ctx1);
+ shaInit(&ctx2);
+
+ /* The first context gets the password hash XORed
+ * with 0x36 plus a magic value
+ * which we previously extrapolated from our
+ * challenge. */
+
+ shaUpdate(&ctx1, pass_hash_xor1, 64);
+ if (j >= 3 )
+ ctx1.sizeLo = 0x1ff;
+ shaUpdate(&ctx1, magic_key_char, 4);
+ shaFinal(&ctx1, digest1);
+
+ /* The second context gets the password hash XORed
+ * with 0x5c plus the SHA-1 digest
+ * of the first context. */
+
+ shaUpdate(&ctx2, pass_hash_xor2, 64);
+ shaUpdate(&ctx2, digest1, 20);
+ shaFinal(&ctx2, digest2);
+
+ /* Now that we have digest2, use it to fetch
+ * characters from an alphabet to construct
+ * our first authentication response. */
+
+ for (x = 0; x < 20; x += 2) {
+ unsigned int val = 0;
+ unsigned int lookup = 0;
+ char byte[6];
+
+ memset(&byte, 0, 6);
+
+ /* First two bytes of digest stuffed
+ * together.
+ */
+
+ val = digest2[x];
+ val <<= 8;
+ val += digest2[x+1];
+
+ lookup = (val >> 0x0b);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet1))
+ break;
+ sprintf(byte, "%c", alphabet1[lookup]);
+ strcat(resp_6, byte);
+ strcat(resp_6, "=");
+
+ lookup = (val >> 0x06);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_6, byte);
+
+ lookup = (val >> 0x01);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_6, byte);
+
+ lookup = (val & 0x01);
+ if (lookup >= strlen(delimit_lookup))
+ break;
+ sprintf(byte, "%c", delimit_lookup[lookup]);
+ strcat(resp_6, byte);
+ }
+
+ /* Our second authentication response is based off
+ * of the crypto hash. */
+
+ cnt = 0;
+ memset(&digest1, 0, 20);
+ memset(&digest2, 0, 20);
+
+ for (x = 0; x < (int)strlen((char *)crypt_hash); x++)
+ crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36;
+
+ if (cnt < 64)
+ memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt);
+
+ cnt = 0;
+
+ for (x = 0; x < (int)strlen((char *)crypt_hash); x++)
+ crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c;
+
+ if (cnt < 64)
+ memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+ shaInit(&ctx1);
+ shaInit(&ctx2);
+
+ /* The first context gets the password hash XORed
+ * with 0x36 plus a magic value
+ * which we previously extrapolated from our
+ * challenge. */
+
+ shaUpdate(&ctx1, crypt_hash_xor1, 64);
+ if (j >= 3 )
+ ctx1.sizeLo = 0x1ff;
+ shaUpdate(&ctx1, magic_key_char, 4);
+ shaFinal(&ctx1, digest1);
+
+ /* The second context gets the password hash XORed
+ * with 0x5c plus the SHA-1 digest
+ * of the first context. */
+
+ shaUpdate(&ctx2, crypt_hash_xor2, 64);
+ shaUpdate(&ctx2, digest1, 20);
+ shaFinal(&ctx2, digest2);
+
+ /* Now that we have digest2, use it to fetch
+ * characters from an alphabet to construct
+ * our first authentication response. */
+
+ for (x = 0; x < 20; x += 2) {
+ unsigned int val = 0;
+ unsigned int lookup = 0;
+
+ char byte[6];
+
+ memset(&byte, 0, 6);
+
+ /* First two bytes of digest stuffed
+ * together. */
+
+ val = digest2[x];
+ val <<= 8;
+ val += digest2[x+1];
+
+ lookup = (val >> 0x0b);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet1))
+ break;
+ sprintf(byte, "%c", alphabet1[lookup]);
+ strcat(resp_96, byte);
+ strcat(resp_96, "=");
+
+ lookup = (val >> 0x06);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_96, byte);
+
+ lookup = (val >> 0x01);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_96, byte);
+
+ lookup = (val & 0x01);
+ if (lookup >= strlen(delimit_lookup))
+ break;
+ sprintf(byte, "%c", delimit_lookup[lookup]);
+ strcat(resp_96, byte);
+ }
+
+ pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+ yahoo_packet_hash(pack, 0, sn);
+ yahoo_packet_hash(pack, 6, resp_6);
+ yahoo_packet_hash(pack, 96, resp_96);
+ yahoo_packet_hash(pack, 1, sn);
+ yahoo_send_packet(yid, pack, 0);
+ yahoo_packet_free(pack);
+
+ free(password_hash);
+ free(crypt_hash);
+}
+
+static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char *seed = NULL;
+ char *sn = NULL;
+ YList *l = pkt->hash;
+ int m = 0;
+
+ while (l) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 94)
+ seed = pair->value;
+ if (pair->key == 1)
+ sn = pair->value;
+ if (pair->key == 13)
+ m = atoi(pair->value);
+ l = l->next;
+ }
+
+ if (!seed)
+ return;
+
+ switch (m) {
+ case 0:
+ yahoo_process_auth_pre_0x0b(yid, seed, sn);
+ break;
+ case 1:
+ yahoo_process_auth_0x0b(yid, seed, sn);
+ break;
+ default:
+ /* call error */
+ WARNING(("unknown auth type %d", m));
+ yahoo_process_auth_0x0b(yid, seed, sn);
+ break;
+ }
+}
+
+static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *login_id;
+ char *handle;
+ char *url=NULL;
+ int login_status=0;
+
+ YList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 0)
+ login_id = pair->value;
+ else if (pair->key == 1)
+ handle = pair->value;
+ else if (pair->key == 20)
+ url = pair->value;
+ else if (pair->key == 66)
+ login_status = atoi(pair->value);
+ }
+
+ if(pkt->status == 0xffffffff) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url);
+ /* yahoo_logoff(yd->client_id);*/
+ }
+}
+
+static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *who = NULL;
+ char *email = NULL;
+ char *subj = NULL;
+ int count = 0;
+ YList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 9)
+ count = strtol(pair->value, NULL, 10);
+ else if (pair->key == 43)
+ who = pair->value;
+ else if (pair->key == 42)
+ email = pair->value;
+ else if (pair->key == 18)
+ subj = pair->value;
+ else
+ LOG(("key: %d => value: %s", pair->key, pair->value));
+ }
+
+ if (who && email && subj) {
+ char from[1024];
+ snprintf(from, sizeof(from), "%s (%s)", who, email);
+ YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count);
+ } else if(count > 0)
+ YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count);
+}
+
+static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *id = NULL;
+ char *who = NULL;
+ char *msg = NULL;
+ char *name = NULL;
+ long tm = 0L;
+ int state = YAHOO_STATUS_AVAILABLE;
+ int online = FALSE;
+ int away = 0;
+
+ YList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 1)
+ id = pair->value;
+ else if (pair->key == 3)
+ who = pair->value;
+ else if (pair->key == 14)
+ msg = pair->value;
+ else if (pair->key == 7)
+ name = pair->value;
+ else if (pair->key == 10)
+ state = strtol(pair->value, NULL, 10);
+ else if (pair->key == 15)
+ tm = strtol(pair->value, NULL, 10);
+ else if (pair->key == 13)
+ online = strtol(pair->value, NULL, 10);
+ else if (pair->key == 47)
+ away = strtol(pair->value, NULL, 10);
+ }
+
+ if (id)
+ YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg);
+ else if (name)
+ YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
+ else if(pkt->status == 0x07)
+ YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg);
+}
+
+static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *who = NULL;
+ char *where = NULL;
+ int status = 0;
+ char *me = NULL;
+
+ struct yahoo_buddy *bud=NULL;
+
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 1)
+ me = pair->value;
+ if (pair->key == 7)
+ who = pair->value;
+ if (pair->key == 65)
+ where = pair->value;
+ if (pair->key == 66)
+ status = strtol(pair->value, NULL, 10);
+ }
+
+ yahoo_dump_unhandled(pkt);
+
+ if(!who)
+ return;
+ if(!where)
+ where = "Unknown";
+
+ bud = y_new0(struct yahoo_buddy, 1);
+ bud->id = strdup(who);
+ bud->group = strdup(where);
+ bud->real_name = NULL;
+
+ yd->buddies = y_list_append(yd->buddies, bud);
+
+/* YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */
+}
+
+static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ struct yahoo_data *yd = yid->yd;
+ char *who = NULL;
+ char *where = NULL;
+ int unk_66 = 0;
+ char *me = NULL;
+ struct yahoo_buddy *bud;
+
+ YList *buddy;
+
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 1)
+ me = pair->value;
+ else if (pair->key == 7)
+ who = pair->value;
+ else if (pair->key == 65)
+ where = pair->value;
+ else if (pair->key == 66)
+ unk_66 = strtol(pair->value, NULL, 10);
+ else
+ DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value));
+ }
+
+ if(!who || !where)
+ return;
+
+ bud = y_new0(struct yahoo_buddy, 1);
+ bud->id = strdup(who);
+ bud->group = strdup(where);
+
+ buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
+
+ FREE(bud->id);
+ FREE(bud->group);
+ FREE(bud);
+
+ if(buddy) {
+ bud = buddy->data;
+ yd->buddies = y_list_remove_link(yd->buddies, buddy);
+ y_list_free_1(buddy);
+
+ FREE(bud->id);
+ FREE(bud->group);
+ FREE(bud->real_name);
+ FREE(bud);
+
+ bud=NULL;
+ }
+}
+
+static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char *who = NULL;
+ int status = 0;
+ char *me = NULL;
+ int un_ignore = 0;
+
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 0)
+ who = pair->value;
+ if (pair->key == 1)
+ me = pair->value;
+ if (pair->key == 13) /* 1 == ignore, 2 == unignore */
+ un_ignore = strtol(pair->value, NULL, 10);
+ if (pair->key == 66)
+ status = strtol(pair->value, NULL, 10);
+ }
+
+
+ /*
+ * status
+ * 0 - ok
+ * 2 - already in ignore list, could not add
+ * 3 - not in ignore list, could not delete
+ * 12 - is a buddy, could not add
+ */
+
+/* if(status)
+ YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, status, who, 0);
+*/
+}
+
+static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char *who = NULL;
+ char *me = NULL;
+ char *room = NULL;
+ char *voice_room = NULL;
+
+ YList *l;
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 4)
+ who = pair->value;
+ if (pair->key == 5)
+ me = pair->value;
+ if (pair->key == 13)
+ voice_room=pair->value;
+ if (pair->key == 57)
+ room=pair->value;
+ }
+
+ NOTICE(("got voice chat invite from %s in %s", who, room));
+ /*
+ * send: s:0 1:me 5:who 57:room 13:1
+ * ???? s:4 5:who 10:99 19:-1615114531
+ * gotr: s:4 5:who 10:99 19:-1615114615
+ * ???? s:1 5:me 4:who 57:room 13:3room
+ * got: s:1 5:me 4:who 57:room 13:1room
+ * rej: s:0 1:me 5:who 57:room 13:3
+ * rejr: s:4 5:who 10:99 19:-1617114599
+ */
+}
+
+static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)
+{
+ struct yahoo_input_data *yid = d;
+ char *who = yid->wcm->user;
+ char *data = NULL;
+ char *packet = NULL;
+ unsigned char magic_nr[] = {0, 1, 0};
+ unsigned char header_len = 8;
+ unsigned int len = 0;
+ unsigned int pos = 0;
+
+ if(error || fd <= 0) {
+ FREE(who);
+ FREE(yid);
+ return;
+ }
+
+ yid->fd = fd;
+ inputs = y_list_prepend(inputs, yid);
+
+ /* send initial packet */
+ if (who)
+ data = strdup("<RVWCFG>");
+ else
+ data = strdup("<RUPCFG>");
+ yahoo_add_to_send_queue(yid, data, strlen(data));
+ FREE(data);
+
+ /* send data */
+ if (who)
+ {
+ data = strdup("g=");
+ data = y_string_append(data, who);
+ data = y_string_append(data, "\r\n");
+ } else {
+ data = strdup("f=1\r\n");
+ }
+ len = strlen(data);
+ packet = y_new0(char, header_len + len);
+ packet[pos++] = header_len;
+ memcpy(packet + pos, magic_nr, sizeof(magic_nr));
+ pos += sizeof(magic_nr);
+ pos += yahoo_put32(packet + pos, len);
+ memcpy(packet + pos, data, len);
+ pos += len;
+ yahoo_add_to_send_queue(yid, packet, pos);
+ FREE(packet);
+ FREE(data);
+
+ yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
+}
+
+static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key)
+{
+ struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
+ struct yahoo_server_settings *yss = y->yd->server_settings;
+
+ yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
+ yid->yd = y->yd;
+ yid->wcm = y_new0(struct yahoo_webcam, 1);
+ yid->wcm->user = who?strdup(who):NULL;
+ yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD;
+ yid->wcm->key = strdup(key);
+
+ YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port,
+ _yahoo_webcam_get_server_connected, yid);
+
+}
+
+static YList *webcam_queue=NULL;
+static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ char *me = NULL;
+ char *key = NULL;
+ char *who = NULL;
+
+ YList *l;
+ yahoo_dump_unhandled(pkt);
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+ if (pair->key == 5)
+ me = pair->value;
+ if (pair->key == 61)
+ key=pair->value;
+ }
+
+ l = webcam_queue;
+ if(!l)
+ return;
+ who = l->data;
+ webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
+ y_list_free_1(l);
+ yahoo_webcam_get_server(yid, who, key);
+ FREE(who);
+}
+
+static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+ DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+ switch (pkt->service)
+ {
+ case YAHOO_SERVICE_USERSTAT:
+ case YAHOO_SERVICE_LOGON:
+ case YAHOO_SERVICE_LOGOFF:
+ case YAHOO_SERVICE_ISAWAY:
+ case YAHOO_SERVICE_ISBACK:
+ case YAHOO_SERVICE_GAMELOGON:
+ case YAHOO_SERVICE_GAMELOGOFF:
+ case YAHOO_SERVICE_IDACT:
+ case YAHOO_SERVICE_IDDEACT:
+ yahoo_process_status(yid, pkt);
+ break;
+ case YAHOO_SERVICE_NOTIFY:
+ yahoo_process_notify(yid, pkt);
+ break;
+ case YAHOO_SERVICE_MESSAGE:
+ case YAHOO_SERVICE_GAMEMSG:
+ case YAHOO_SERVICE_SYSMESSAGE:
+ yahoo_process_message(yid, pkt);
+ break;
+ case YAHOO_SERVICE_NEWMAIL:
+ yahoo_process_mail(yid, pkt);
+ break;
+ case YAHOO_SERVICE_NEWCONTACT:
+ yahoo_process_contact(yid, pkt);
+ break;
+ case YAHOO_SERVICE_LIST:
+ yahoo_process_list(yid, pkt);
+ break;
+ case YAHOO_SERVICE_VERIFY:
+ yahoo_process_verify(yid, pkt);
+ break;
+ case YAHOO_SERVICE_AUTH:
+ yahoo_process_auth(yid, pkt);
+ break;
+ case YAHOO_SERVICE_AUTHRESP:
+ yahoo_process_auth_resp(yid, pkt);
+ break;
+ case YAHOO_SERVICE_CONFINVITE:
+ case YAHOO_SERVICE_CONFADDINVITE:
+ case YAHOO_SERVICE_CONFDECLINE:
+ case YAHOO_SERVICE_CONFLOGON:
+ case YAHOO_SERVICE_CONFLOGOFF:
+ case YAHOO_SERVICE_CONFMSG:
+ yahoo_process_conference(yid, pkt);
+ break;
+ case YAHOO_SERVICE_CHATONLINE:
+ case YAHOO_SERVICE_CHATGOTO:
+ case YAHOO_SERVICE_CHATJOIN:
+ case YAHOO_SERVICE_CHATLEAVE:
+ case YAHOO_SERVICE_CHATEXIT:
+ case YAHOO_SERVICE_CHATLOGOUT:
+ case YAHOO_SERVICE_CHATPING:
+ case YAHOO_SERVICE_COMMENT:
+ yahoo_process_chat(yid, pkt);
+ break;
+ case YAHOO_SERVICE_P2PFILEXFER:
+ case YAHOO_SERVICE_FILETRANSFER:
+ yahoo_process_filetransfer(yid, pkt);
+ break;
+ case YAHOO_SERVICE_ADDBUDDY:
+ yahoo_process_buddyadd(yid, pkt);
+ break;
+ case YAHOO_SERVICE_REMBUDDY:
+ yahoo_process_buddydel(yid, pkt);
+ break;
+ case YAHOO_SERVICE_IGNORECONTACT:
+ yahoo_process_ignore(yid, pkt);
+ break;
+ case YAHOO_SERVICE_VOICECHAT:
+ yahoo_process_voicechat(yid, pkt);
+ break;
+ case YAHOO_SERVICE_WEBCAM:
+ yahoo_process_webcam_key(yid, pkt);
+ break;
+ case YAHOO_SERVICE_IDLE:
+ case YAHOO_SERVICE_MAILSTAT:
+ case YAHOO_SERVICE_CHATINVITE:
+ case YAHOO_SERVICE_CALENDAR:
+ case YAHOO_SERVICE_NEWPERSONALMAIL:
+ case YAHOO_SERVICE_ADDIDENT:
+ case YAHOO_SERVICE_ADDIGNORE:
+ case YAHOO_SERVICE_PING:
+ case YAHOO_SERVICE_GOTGROUPRENAME:
+ case YAHOO_SERVICE_GROUPRENAME:
+ case YAHOO_SERVICE_PASSTHROUGH2:
+ case YAHOO_SERVICE_CHATLOGON:
+ case YAHOO_SERVICE_CHATLOGOFF:
+ case YAHOO_SERVICE_CHATMSG:
+ case YAHOO_SERVICE_REJECTCONTACT:
+ case YAHOO_SERVICE_PEERTOPEER:
+ WARNING(("unhandled service 0x%02x", pkt->service));
+ yahoo_dump_unhandled(pkt);
+ break;
+ default:
+ WARNING(("unknown service 0x%02x", pkt->service));
+ yahoo_dump_unhandled(pkt);
+ break;
+ }
+}
+
+static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)
+{
+ struct yahoo_packet *pkt;
+ struct yahoo_data *yd = yid->yd;
+ int pos = 0;
+ int pktlen;
+
+ if(!yd)
+ return NULL;
+
+ DEBUG_MSG(("rxlen is %d", yid->rxlen));
+ if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
+ DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
+ return NULL;
+ }
+
+ pos += 4; /* YMSG */
+ pos += 2;
+ pos += 2;
+
+ pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2;
+ DEBUG_MSG(("%d bytes to read, rxlen is %d",
+ pktlen, yid->rxlen));
+
+ if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
+ DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
+ return NULL;
+ }
+
+ LOG(("reading packet"));
+ yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
+
+ pkt = yahoo_packet_new(0, 0, 0);
+
+ pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2;
+ pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4;
+ DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
+ pkt->status));
+ pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4;
+
+ yd->session_id = pkt->id;
+
+ yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
+
+ yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
+ DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ if (yid->rxlen>0) {
+ unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN
+ + pktlen, yid->rxlen);
+ FREE(yid->rxqueue);
+ yid->rxqueue = tmp;
+ DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ } else {
+ DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+ FREE(yid->rxqueue);
+ }
+
+ return pkt;
+}
+
+static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len)
+{
+ char *st, *en;
+ char *data = (char *)d;
+ data[len]='\0';
+
+ DEBUG_MSG(("Got yab: %s", data));
+ st = en = strstr(data, "userid=\"");
+ if(st) {
+ st += strlen("userid=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->id = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "fname=\"");
+ if(st) {
+ st += strlen("fname=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->fname = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "lname=\"");
+ if(st) {
+ st += strlen("lname=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->lname = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "nname=\"");
+ if(st) {
+ st += strlen("nname=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->nname = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "email=\"");
+ if(st) {
+ st += strlen("email=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->email = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "hphone=\"");
+ if(st) {
+ st += strlen("hphone=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->hphone = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "wphone=\"");
+ if(st) {
+ st += strlen("wphone=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->wphone = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "mphone=\"");
+ if(st) {
+ st += strlen("mphone=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->mphone = yahoo_xmldecode(st);
+ }
+
+ st = strstr(en, "dbid=\"");
+ if(st) {
+ st += strlen("dbid=\"");
+ en = strchr(st, '"'); *en++ = '\0';
+ yab->dbid = atoi(st);
+ }
+}
+
+static struct yab * yahoo_getyab(struct yahoo_input_data *yid)
+{
+ struct yab *yab = NULL;
+ int pos = 0, end=0;
+ struct yahoo_data *yd = yid->yd;
+
+ if(!yd)
+ return NULL;
+
+ DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+ if(yid->rxlen <= strlen("<record"))
+ return NULL;
+
+ /* start with <record */
+ while(pos < yid->rxlen-strlen("<record")+1
+ && memcmp(yid->rxqueue + pos, "<record", strlen("<record")))
+ pos++;
+
+ if(pos >= yid->rxlen-1)
+ return NULL;
+
+ end = pos+2;
+ /* end with /> */
+ while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>")))
+ end++;
+
+ if(end >= yid->rxlen-1)
+ return NULL;
+
+ yab = y_new0(struct yab, 1);
+ yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos);
+
+
+ yid->rxlen -= end+1;
+ DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ if (yid->rxlen>0) {
+ unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen);
+ FREE(yid->rxqueue);
+ yid->rxqueue = tmp;
+ DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ } else {
+ DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+ FREE(yid->rxqueue);
+ }
+
+
+ return yab;
+}
+
+static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)
+{
+ unsigned int pos=0;
+ unsigned int len=0;
+ unsigned int status=0;
+ char *server=NULL;
+ struct yahoo_data *yd = yid->yd;
+
+ if(!yid || !yd)
+ return NULL;
+
+ DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+ len = yid->rxqueue[pos++];
+ if (yid->rxlen < len)
+ return NULL;
+
+ /* extract status (0 = ok, 6 = webcam not online) */
+ status = yid->rxqueue[pos++];
+
+ if (status == 0)
+ {
+ pos += 2; /* skip next 2 bytes */
+ server = y_memdup(yid->rxqueue+pos, 16);
+ pos += 16;
+ }
+ else if (status == 6)
+ {
+ YAHOO_CALLBACK(ext_yahoo_webcam_closed)
+ (yd->client_id, yid->wcm->user, 4);
+ }
+
+ /* skip rest of the data */
+
+ yid->rxlen -= len;
+ DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ if (yid->rxlen>0) {
+ unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
+ FREE(yid->rxqueue);
+ yid->rxqueue = tmp;
+ DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ } else {
+ DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+ FREE(yid->rxqueue);
+ }
+
+ return server;
+}
+
+static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
+{
+ unsigned char reason=0;
+ unsigned int pos=0;
+ unsigned int begin=0;
+ unsigned int end=0;
+ unsigned int closed=0;
+ unsigned char header_len=0;
+ char *who;
+ int connect=0;
+ struct yahoo_data *yd = yid->yd;
+
+ if(!yd)
+ return -1;
+
+ if(!yid->wcm || !yid->wcd || !yid->rxlen)
+ return -1;
+
+ DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+ /* if we are not reading part of image then read header */
+ if (!yid->wcd->to_read)
+ {
+ header_len=yid->rxqueue[pos++];
+ yid->wcd->packet_type=0;
+
+ if (yid->rxlen < header_len)
+ return 0;
+
+ if (header_len >= 8)
+ {
+ reason = yid->rxqueue[pos++];
+ /* next 2 bytes should always be 05 00 */
+ pos += 2;
+ yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
+ pos += 4;
+ yid->wcd->to_read = yid->wcd->data_size;
+ }
+ if (header_len >= 13)
+ {
+ yid->wcd->packet_type = yid->rxqueue[pos++];
+ yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
+ pos += 4;
+ }
+
+ /* skip rest of header */
+ pos = header_len;
+ }
+
+ begin = pos;
+ pos += yid->wcd->to_read;
+ if (pos > yid->rxlen) pos = yid->rxlen;
+
+ /* if it is not an image then make sure we have the whole packet */
+ if (yid->wcd->packet_type != 0x02) {
+ if ((pos - begin) != yid->wcd->data_size) {
+ yid->wcd->to_read = 0;
+ return 0;
+ } else {
+ yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
+ }
+ }
+
+ DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
+ yid->wcd->data_size));
+
+ /* find out what kind of packet we got */
+ switch (yid->wcd->packet_type)
+ {
+ case 0x00:
+ /* user requests to view webcam (uploading) */
+ if (yid->wcd->data_size &&
+ yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
+ end = begin;
+ while (end <= yid->rxlen &&
+ yid->rxqueue[end++] != 13);
+ if (end > begin)
+ {
+ who = y_memdup(yid->rxqueue + begin, end - begin);
+ who[end - begin - 1] = 0;
+ YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2);
+ FREE(who);
+ }
+ }
+
+ if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
+ /* timestamp/status field */
+ /* 0 = declined viewing permission */
+ /* 1 = accepted viewing permission */
+ if (yid->wcd->timestamp == 0) {
+ YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3);
+ }
+ }
+ break;
+ case 0x01: /* status packets?? */
+ /* timestamp contains status info */
+ /* 00 00 00 01 = we have data?? */
+ break;
+ case 0x02: /* image data */
+ YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id,
+ yid->wcm->user, yid->rxqueue + begin,
+ yid->wcd->data_size, pos - begin,
+ yid->wcd->timestamp);
+ break;
+ case 0x05: /* response packets when uploading */
+ if (!yid->wcd->data_size) {
+ YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp);
+ }
+ break;
+ case 0x07: /* connection is closing */
+ switch(reason)
+ {
+ case 0x01: /* user closed connection */
+ closed = 1;
+ break;
+ case 0x0F: /* user cancelled permission */
+ closed = 2;
+ break;
+ }
+ YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed);
+ break;
+ case 0x0C: /* user connected */
+ case 0x0D: /* user disconnected */
+ if (yid->wcd->data_size) {
+ who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
+ who[pos - begin] = 0;
+ if (yid->wcd->packet_type == 0x0C)
+ connect=1;
+ else
+ connect=0;
+ YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect);
+ FREE(who);
+ }
+ break;
+ case 0x13: /* user data */
+ /* i=user_ip (ip of the user we are viewing) */
+ /* j=user_ext_ip (external ip of the user we */
+ /* are viewing) */
+ break;
+ case 0x17: /* ?? */
+ break;
+ }
+ yid->wcd->to_read -= pos - begin;
+
+ yid->rxlen -= pos;
+ DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ if (yid->rxlen>0) {
+ unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
+ FREE(yid->rxqueue);
+ yid->rxqueue = tmp;
+ DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+ } else {
+ DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+ FREE(yid->rxqueue);
+ }
+
+ /* If we read a complete packet return success */
+ if (!yid->wcd->to_read)
+ return 1;
+
+ return 0;
+}
+
+int yahoo_write_ready(int id, int fd, void *data)
+{
+ struct yahoo_input_data *yid = data;
+ int len;
+ struct data_queue *tx;
+
+ LOG(("write callback: id=%d fd=%d data=%p", id, fd, data));
+ if(!yid || !yid->txqueues)
+ return -2;
+
+ tx = yid->txqueues->data;
+ LOG(("writing %d bytes", tx->len));
+ len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
+
+ if(len == -1 && errno == EAGAIN)
+ return 1;
+
+ if(len <= 0) {
+ int e = errno;
+ DEBUG_MSG(("len == %d (<= 0)", len));
+ while(yid->txqueues) {
+ YList *l=yid->txqueues;
+ tx = l->data;
+ free(tx->queue);
+ free(tx);
+ yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
+ y_list_free_1(l);
+ }
+ LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd));
+ YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
+ yid->write_tag = 0;
+ errno=e;
+ return 0;
+ }
+
+
+ tx->len -= len;
+ if(tx->len > 0) {
+ unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
+ FREE(tx->queue);
+ tx->queue = tmp;
+ } else {
+ YList *l=yid->txqueues;
+ free(tx->queue);
+ free(tx);
+ yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
+ y_list_free_1(l);
+ /*
+ if(!yid->txqueues)
+ LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
+ */
+ if(!yid->txqueues) {
+ LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
+ YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
+ yid->write_tag = 0;
+ }
+ }
+
+ return 1;
+}
+
+static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over)
+{
+ struct yahoo_packet *pkt;
+ struct yahoo_data *yd = yid->yd;
+ int id = yd->client_id;
+
+ if(over)
+ return;
+
+ while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER)
+ && (pkt = yahoo_getdata(yid)) != NULL) {
+
+ yahoo_packet_process(yid, pkt);
+
+ yahoo_packet_free(pkt);
+ }
+}
+
+static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
+{
+}
+
+static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over)
+{
+ if(over)
+ return;
+
+ if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "</content>")) {
+ YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue);
+ }
+}
+
+static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
+{
+ struct yahoo_data *yd = yid->yd;
+ struct yab *yab;
+ YList *buds;
+ int changed=0;
+ int id = yd->client_id;
+
+ if(over)
+ return;
+
+ while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB)
+ && (yab = yahoo_getyab(yid)) != NULL) {
+ if(!yab->id)
+ continue;
+ changed=1;
+ for(buds = yd->buddies; buds; buds=buds->next) {
+ struct yahoo_buddy * bud = buds->data;
+ if(!strcmp(bud->id, yab->id)) {
+ bud->yab_entry = yab;
+ if(yab->nname) {
+ bud->real_name = strdup(yab->nname);
+ } else if(yab->fname && yab->lname) {
+ bud->real_name = y_new0(char,
+ strlen(yab->fname)+
+ strlen(yab->lname)+2
+ );
+ sprintf(bud->real_name, "%s %s",
+ yab->fname, yab->lname);
+ } else if(yab->fname) {
+ bud->real_name = strdup(yab->fname);
+ }
+ break; /* for */
+ }
+ }
+ }
+
+ if(changed)
+ YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+}
+
+static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over)
+{
+ struct yahoo_found_contact *yct=NULL;
+ char *p = (char *)yid->rxqueue, *np, *cp;
+ int k, n;
+ int start=0, found=0, total=0;
+ YList *contacts=NULL;
+ struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER);
+
+ if(!over || !pyid)
+ return;
+
+ if(p && (p=strstr(p, "\r\n\r\n"))) {
+ p += 4;
+
+ for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
+ p++;
+ n = atoi(p);
+ switch(k) {
+ case 0: found = pyid->ys->lsearch_nfound = n; break;
+ case 2: start = pyid->ys->lsearch_nstart = n; break;
+ case 3: total = pyid->ys->lsearch_ntotal = n; break;
+ }
+ }
+
+ if(p)
+ p++;
+
+ k=0;
+ while(p && *p) {
+ cp = p;
+ np = strchr(p, 4);
+
+ if(!np)
+ break;
+ *np = 0;
+ p = np+1;
+
+ switch(k++) {
+ case 1:
+ if(strlen(cp) > 2 && y_list_length(contacts) < total) {
+ yct = y_new0(struct yahoo_found_contact, 1);
+ contacts = y_list_append(contacts, yct);
+ yct->id = cp+2;
+ } else {
+ *p = 0;
+ }
+ break;
+ case 2:
+ yct->online = !strcmp(cp, "2") ? 1 : 0;
+ break;
+ case 3:
+ yct->gender = cp;
+ break;
+ case 4:
+ yct->age = atoi(cp);
+ break;
+ case 5:
+ if(cp != "\005")
+ yct->location = cp;
+ k = 0;
+ break;
+ }
+ }
+ }
+
+ YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts);
+
+ while(contacts) {
+ YList *node = contacts;
+ contacts = y_list_remove_link(contacts, node);
+ free(node->data);
+ y_list_free_1(node);
+ }
+}
+
+static void _yahoo_webcam_connected(int fd, int error, void *d)
+{
+ struct yahoo_input_data *yid = d;
+ struct yahoo_webcam *wcm = yid->wcm;
+ struct yahoo_data *yd = yid->yd;
+ char conn_type[100];
+ char *data=NULL;
+ char *packet=NULL;
+ unsigned char magic_nr[] = {1, 0, 0, 0, 1};
+ unsigned header_len=0;
+ unsigned int len=0;
+ unsigned int pos=0;
+
+ if(error || fd <= 0) {
+ FREE(yid);
+ return;
+ }
+
+ yid->fd = fd;
+ inputs = y_list_prepend(inputs, yid);
+
+ LOG(("Connected"));
+ /* send initial packet */
+ switch (wcm->direction)
+ {
+ case YAHOO_WEBCAM_DOWNLOAD:
+ data = strdup("<REQIMG>");
+ break;
+ case YAHOO_WEBCAM_UPLOAD:
+ data = strdup("<SNDIMG>");
+ break;
+ default:
+ return;
+ }
+ yahoo_add_to_send_queue(yid, data, strlen(data));
+ FREE(data);
+
+ /* send data */
+ switch (wcm->direction)
+ {
+ case YAHOO_WEBCAM_DOWNLOAD:
+ header_len = 8;
+ data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
+ data = y_string_append(data, yd->user);
+ data = y_string_append(data, "\r\nt=");
+ data = y_string_append(data, wcm->key);
+ data = y_string_append(data, "\r\ni=");
+ data = y_string_append(data, wcm->my_ip);
+ data = y_string_append(data, "\r\ng=");
+ data = y_string_append(data, wcm->user);
+ data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
+ snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
+ data = y_string_append(data, conn_type);
+ data = y_string_append(data, "\r\n");
+ break;
+ case YAHOO_WEBCAM_UPLOAD:
+ header_len = 13;
+ data = strdup("a=2\r\nc=us\r\nu=");
+ data = y_string_append(data, yd->user);
+ data = y_string_append(data, "\r\nt=");
+ data = y_string_append(data, wcm->key);
+ data = y_string_append(data, "\r\ni=");
+ data = y_string_append(data, wcm->my_ip);
+ data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
+ snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
+ data = y_string_append(data, conn_type);
+ data = y_string_append(data, "\r\nb=");
+ data = y_string_append(data, wcm->description);
+ data = y_string_append(data, "\r\n");
+ break;
+ }
+
+ len = strlen(data);
+ packet = y_new0(char, header_len + len);
+ packet[pos++] = header_len;
+ packet[pos++] = 0;
+ switch (wcm->direction)
+ {
+ case YAHOO_WEBCAM_DOWNLOAD:
+ packet[pos++] = 1;
+ packet[pos++] = 0;
+ break;
+ case YAHOO_WEBCAM_UPLOAD:
+ packet[pos++] = 5;
+ packet[pos++] = 0;
+ break;
+ }
+
+ pos += yahoo_put32(packet + pos, len);
+ if (wcm->direction == YAHOO_WEBCAM_UPLOAD)
+ {
+ memcpy(packet + pos, magic_nr, sizeof(magic_nr));
+ pos += sizeof(magic_nr);
+ }
+ memcpy(packet + pos, data, len);
+ yahoo_add_to_send_queue(yid, packet, header_len + len);
+ FREE(packet);
+ FREE(data);
+
+ yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
+}
+
+static void yahoo_webcam_connect(struct yahoo_input_data *y)
+{
+ struct yahoo_webcam *wcm = y->wcm;
+ struct yahoo_input_data *yid;
+ struct yahoo_server_settings *yss;
+
+ if (!wcm || !wcm->server || !wcm->key)
+ return;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->type = YAHOO_CONNECTION_WEBCAM;
+ yid->yd = y->yd;
+
+ /* copy webcam data to new connection */
+ yid->wcm = y->wcm;
+ y->wcm = NULL;
+
+ yss = y->yd->server_settings;
+
+ yid->wcd = y_new0(struct yahoo_webcam_data, 1);
+
+ LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
+ YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port,
+ _yahoo_webcam_connected, yid);
+
+}
+
+static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over)
+{
+ char* server;
+ struct yahoo_server_settings *yss;
+
+ if(over)
+ return;
+
+ server = yahoo_getwebcam_master(yid);
+
+ if (server)
+ {
+ yss = yid->yd->server_settings;
+ yid->wcm->server = strdup(server);
+ yid->wcm->port = yss->webcam_port;
+ yid->wcm->conn_type = yss->conn_type;
+ yid->wcm->my_ip = strdup(yss->local_host);
+ if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
+ yid->wcm->description = strdup(yss->webcam_description);
+ yahoo_webcam_connect(yid);
+ FREE(server);
+ }
+}
+
+static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over)
+{
+ int id = yid->yd->client_id;
+ int fd = yid->fd;
+
+ if(over)
+ return;
+
+ /* as long as we still have packets available keep processing them */
+ while (find_input_by_id_and_fd(id, fd)
+ && yahoo_get_webcam_data(yid) == 1);
+}
+
+static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = {
+ yahoo_process_pager_connection,
+ yahoo_process_ft_connection,
+ yahoo_process_yab_connection,
+ yahoo_process_webcam_master_connection,
+ yahoo_process_webcam_connection,
+ yahoo_process_chatcat_connection,
+ yahoo_process_search_connection
+};
+
+int yahoo_read_ready(int id, int fd, void *data)
+{
+ struct yahoo_input_data *yid = data;
+ char buf[1024];
+ int len;
+
+ LOG(("read callback: id=%d fd=%d data=%p", id, fd, data));
+ if(!yid)
+ return -2;
+
+
+ do {
+ len = read(fd, buf, sizeof(buf));
+ } while(len == -1 && errno == EINTR);
+
+ if(len == -1 && errno == EAGAIN) /* we'll try again later */
+ return 1;
+
+ if (len <= 0) {
+ int e = errno;
+ DEBUG_MSG(("len == %d (<= 0)", len));
+
+ if(yid->type == YAHOO_CONNECTION_PAGER) {
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yid->yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+ }
+
+ yahoo_process_connection[yid->type](yid, 1);
+ yahoo_input_close(yid);
+
+ /* no need to return an error, because we've already fixed it */
+ if(len == 0)
+ return 1;
+
+ errno=e;
+ LOG(("read error: %s", strerror(errno)));
+ return -1;
+ }
+
+ yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen);
+ memcpy(yid->rxqueue + yid->rxlen, buf, len);
+ yid->rxlen += len;
+
+ yahoo_process_connection[yid->type](yid, 0);
+
+ return len;
+}
+
+int yahoo_init_with_attributes(const char *username, const char *password, ...)
+{
+ va_list ap;
+ struct yahoo_data *yd;
+
+ yd = y_new0(struct yahoo_data, 1);
+
+ if(!yd)
+ return 0;
+
+ yd->user = strdup(username);
+ yd->password = strdup(password);
+
+ yd->initial_status = -1;
+ yd->current_status = -1;
+
+ yd->client_id = ++last_id;
+
+ add_to_list(yd);
+
+ va_start(ap, password);
+ yd->server_settings = _yahoo_assign_server_settings(ap);
+ va_end(ap);
+
+ return yd->client_id;
+}
+
+int yahoo_init(const char *username, const char *password)
+{
+ return yahoo_init_with_attributes(username, password, NULL);
+}
+
+struct connect_callback_data {
+ struct yahoo_data *yd;
+ int tag;
+ int i;
+};
+
+static void yahoo_connected(int fd, int error, void *data)
+{
+ struct connect_callback_data *ccd = data;
+ struct yahoo_data *yd = ccd->yd;
+ struct yahoo_packet *pkt;
+ struct yahoo_input_data *yid;
+ struct yahoo_server_settings *yss = yd->server_settings;
+
+ if(error) {
+ if(fallback_ports[ccd->i]) {
+ int tag;
+ yss->pager_port = fallback_ports[ccd->i++];
+ tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host,
+ yss->pager_port, yahoo_connected, ccd);
+
+ if(tag > 0)
+ ccd->tag=tag;
+ } else {
+ FREE(ccd);
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+ }
+ return;
+ }
+
+ FREE(ccd);
+
+ /* fd < 0 && error == 0 means connect was cancelled */
+ if(fd < 0)
+ return;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ NOTICE(("Sending initial packet"));
+
+ yahoo_packet_hash(pkt, 1, yd->user);
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->yd = yd;
+ yid->fd = fd;
+ inputs = y_list_prepend(inputs, yid);
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+
+ yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
+}
+
+void yahoo_login(int id, int initial)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct connect_callback_data *ccd;
+ struct yahoo_server_settings *yss;
+ int tag;
+
+ if(!yd)
+ return;
+
+ yss = yd->server_settings;
+
+ yd->initial_status = initial;
+
+ ccd = y_new0(struct connect_callback_data, 1);
+ ccd->yd = yd;
+ tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port,
+ yahoo_connected, ccd);
+
+ /*
+ * if tag <= 0, then callback has already been called
+ * so ccd will have been freed
+ */
+ if(tag > 0)
+ ccd->tag = tag;
+ else if(tag < 0)
+ YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+}
+
+
+int yahoo_get_fd(int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ if(!yid)
+ return 0;
+ else
+ return yid->fd;
+}
+
+void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_packet *pkt = NULL;
+ struct yahoo_data *yd;
+
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
+
+ if(from && strcmp(from, yd->user))
+ yahoo_packet_hash(pkt, 0, yd->user);
+ yahoo_packet_hash(pkt, 1, from?from:yd->user);
+ yahoo_packet_hash(pkt, 5, who);
+ yahoo_packet_hash(pkt, 14, what);
+
+ if(utf8)
+ yahoo_packet_hash(pkt, 97, "1");
+
+ yahoo_packet_hash(pkt, 63, ";0"); /* imvironment name; or ;0 */
+ yahoo_packet_hash(pkt, 64, "0");
+
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_send_typing(int id, const char *from, const char *who, int typ)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+ pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id);
+
+ yahoo_packet_hash(pkt, 5, who);
+ yahoo_packet_hash(pkt, 4, from?from:yd->user);
+ yahoo_packet_hash(pkt, 14, " ");
+ yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
+ yahoo_packet_hash(pkt, 49, "TYPING");
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+ int service;
+ char s[4];
+
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+
+ if (msg) {
+ yd->current_status = YAHOO_STATUS_CUSTOM;
+ } else {
+ yd->current_status = state;
+ }
+
+ if (yd->current_status == YAHOO_STATUS_AVAILABLE)
+ service = YAHOO_SERVICE_ISBACK;
+ else
+ service = YAHOO_SERVICE_ISAWAY;
+ pkt = yahoo_packet_new(service, yd->current_status, yd->session_id);
+ snprintf(s, sizeof(s), "%d", yd->current_status);
+ yahoo_packet_hash(pkt, 10, s);
+ if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+ yahoo_packet_hash(pkt, 19, msg);
+ yahoo_packet_hash(pkt, 47, away?"1":"0");
+ }
+
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_logoff(int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ LOG(("yahoo_logoff: current status: %d", yd->current_status));
+
+ if(yd->current_status != -1) {
+ pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yd->current_status = -1;
+
+ if (pkt) {
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+ }
+ }
+
+
+/* do {
+ yahoo_input_close(yid);
+ } while((yid = find_input_by_id(id)));*/
+
+}
+
+void yahoo_get_list(int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ if (pkt) {
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+ }
+}
+
+static void _yahoo_http_connected(int id, int fd, int error, void *data)
+{
+ struct yahoo_input_data *yid = data;
+ if(fd <= 0) {
+ inputs = y_list_remove(inputs, yid);
+ FREE(yid);
+ return;
+ }
+
+ yid->fd = fd;
+ yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
+}
+
+void yahoo_get_yab(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct yahoo_input_data *yid;
+ char url[1024];
+ char buff[1024];
+
+ if(!yd)
+ return;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->yd = yd;
+ yid->type = YAHOO_CONNECTION_YAB;
+
+ snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0");
+
+ snprintf(buff, sizeof(buff), "Y=%s; T=%s",
+ yd->cookie_y, yd->cookie_t);
+
+ inputs = y_list_prepend(inputs, yid);
+
+ yahoo_http_get(yid->yd->client_id, url, buff,
+ _yahoo_http_connected, yid);
+}
+
+void yahoo_set_yab(int id, struct yab * yab)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct yahoo_input_data *yid;
+ char url[1024];
+ char buff[1024];
+ char *temp;
+ int size = sizeof(url)-1;
+
+ if(!yd)
+ return;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->type = YAHOO_CONNECTION_YAB;
+ yid->yd = yd;
+
+ strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size);
+
+ if(yab->dbid) {
+ /* change existing yab */
+ char tmp[32];
+ strncat(url, "&ee=1&ow=1&id=", size - strlen(url));
+ snprintf(tmp, sizeof(tmp), "%d", yab->dbid);
+ strncat(url, tmp, size - strlen(url));
+ }
+
+ if(yab->fname) {
+ strncat(url, "&fn=", size - strlen(url));
+ temp = yahoo_urlencode(yab->fname);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ if(yab->lname) {
+ strncat(url, "&ln=", size - strlen(url));
+ temp = yahoo_urlencode(yab->lname);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ strncat(url, "&yid=", size - strlen(url));
+ temp = yahoo_urlencode(yab->id);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ if(yab->nname) {
+ strncat(url, "&nn=", size - strlen(url));
+ temp = yahoo_urlencode(yab->nname);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ if(yab->email) {
+ strncat(url, "&e=", size - strlen(url));
+ temp = yahoo_urlencode(yab->email);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ if(yab->hphone) {
+ strncat(url, "&hp=", size - strlen(url));
+ temp = yahoo_urlencode(yab->hphone);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ if(yab->wphone) {
+ strncat(url, "&wp=", size - strlen(url));
+ temp = yahoo_urlencode(yab->wphone);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ if(yab->mphone) {
+ strncat(url, "&mp=", size - strlen(url));
+ temp = yahoo_urlencode(yab->mphone);
+ strncat(url, temp, size - strlen(url));
+ free(temp);
+ }
+ strncat(url, "&pp=0", size - strlen(url));
+
+ snprintf(buff, sizeof(buff), "Y=%s; T=%s",
+ yd->cookie_y, yd->cookie_t);
+
+ inputs = y_list_prepend(inputs, yid);
+
+ yahoo_http_get(yid->yd->client_id, url, buff,
+ _yahoo_http_connected, yid);
+}
+
+void yahoo_set_identity_status(int id, const char * identity, int active)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT,
+ YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 3, identity);
+ if (pkt) {
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+ }
+}
+
+void yahoo_refresh(int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ if (pkt) {
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+ }
+}
+
+void yahoo_keepalive(int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt=NULL;
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_chat_keepalive (int id)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if (!yid)
+ return;
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_send_packet (yid, pkt, 0);
+ yahoo_packet_free (pkt);
+}
+
+void yahoo_add_buddy(int id, const char *who, const char *group)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ if (!yd->logged_in)
+ return;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 65, group);
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_remove_buddy(int id, const char *who, const char *group)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 65, group);
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_reject_buddy(int id, const char *who, const char *msg)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ if (!yd->logged_in)
+ return;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 14, msg);
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_ignore_buddy(int id, const char *who, int unignore)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ if (!yd->logged_in)
+ return;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 13, unignore?"2":"1");
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 65, new_group);
+ yahoo_packet_hash(pkt, 14, " ");
+
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 7, who);
+ yahoo_packet_hash(pkt, 65, old_group);
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_group_rename(int id, const char *old_group, const char *new_group)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt = NULL;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ yahoo_packet_hash(pkt, 1, yd->user);
+ yahoo_packet_hash(pkt, 65, old_group);
+ yahoo_packet_hash(pkt, 67, new_group);
+
+ yahoo_send_packet(yid, pkt, 0);
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ yahoo_packet_hash(pkt, 51, who);
+ yahoo_packet_hash(pkt, 57, room);
+ yahoo_packet_hash(pkt, 58, msg);
+ yahoo_packet_hash(pkt, 13, "0");
+ for(; members; members = members->next) {
+ yahoo_packet_hash(pkt, 52, (char *)members->data);
+ yahoo_packet_hash(pkt, 53, (char *)members->data);
+ }
+ /* 52, 53 -> other members? */
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ yahoo_packet_hash(pkt, 50, yd->user);
+ for(; who; who = who->next) {
+ yahoo_packet_hash(pkt, 52, (char *)who->data);
+ }
+ yahoo_packet_hash(pkt, 57, room);
+ yahoo_packet_hash(pkt, 58, msg);
+ yahoo_packet_hash(pkt, 13, "0");
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_logon(int id, const char *from, YList *who, const char *room)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ for(; who; who = who->next) {
+ yahoo_packet_hash(pkt, 3, (char *)who->data);
+ }
+ yahoo_packet_hash(pkt, 57, room);
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ for(; who; who = who->next) {
+ yahoo_packet_hash(pkt, 3, (char *)who->data);
+ }
+ yahoo_packet_hash(pkt, 57, room);
+ yahoo_packet_hash(pkt, 14, msg);
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ for(; who; who = who->next) {
+ yahoo_packet_hash(pkt, 3, (char *)who->data);
+ }
+ yahoo_packet_hash(pkt, 57, room);
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ for(; who; who = who->next) {
+ yahoo_packet_hash(pkt, 53, (char *)who->data);
+ }
+ yahoo_packet_hash(pkt, 57, room);
+ yahoo_packet_hash(pkt, 14, msg);
+
+ if(utf8)
+ yahoo_packet_hash(pkt, 97, "1");
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_get_chatrooms(int id, int chatroomid)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct yahoo_input_data *yid;
+ char url[1024];
+ char buff[1024];
+
+ if(!yd)
+ return;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->yd = yd;
+ yid->type = YAHOO_CONNECTION_CHATCAT;
+
+ if (chatroomid == 0) {
+ snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0");
+ } else {
+ snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid);
+ }
+
+ snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+
+ inputs = y_list_prepend(inputs, yid);
+
+ yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
+}
+
+void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ yahoo_packet_hash(pkt, 109, yd->user);
+ yahoo_packet_hash(pkt, 6, "abcde");
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ yahoo_packet_hash(pkt, 104, room);
+ yahoo_packet_hash(pkt, 129, roomid);
+ yahoo_packet_hash(pkt, 62, "2"); /* ??? */
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+
+void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+ char buf[2];
+
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+ yahoo_packet_hash(pkt, 104, room);
+ yahoo_packet_hash(pkt, 117, msg);
+
+ snprintf(buf, sizeof(buf), "%d", msgtype);
+ yahoo_packet_hash(pkt, 124, buf);
+
+ if(utf8)
+ yahoo_packet_hash(pkt, 97, "1");
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+
+void yahoo_chat_logoff(int id, const char *from)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_webcam_close_feed(int id, const char *who)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who);
+
+ if(yid)
+ yahoo_input_close(yid);
+}
+
+void yahoo_webcam_get_feed(int id, const char *who)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+
+ /*
+ * add the user to the queue. this is a dirty hack, since
+ * the yahoo server doesn't tell us who's key it's returning,
+ * we have to just hope that it sends back keys in the same
+ * order that we request them.
+ * The queue is popped in yahoo_process_webcam_key
+ */
+ webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL);
+
+ yd = yid->yd;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ yahoo_packet_hash(pkt, 1, yd->user);
+ if (who != NULL)
+ yahoo_packet_hash(pkt, 5, who);
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
+ unsigned char *packet;
+ unsigned char header_len = 13;
+ unsigned int pos = 0;
+
+ if (!yid)
+ return;
+
+ packet = y_new0(unsigned char, header_len);
+
+ packet[pos++] = header_len;
+ packet[pos++] = 0;
+ packet[pos++] = 5; /* version byte?? */
+ packet[pos++] = 0;
+ pos += yahoo_put32(packet + pos, length);
+ packet[pos++] = 2; /* packet type, image */
+ pos += yahoo_put32(packet + pos, timestamp);
+ yahoo_add_to_send_queue(yid, packet, header_len);
+ FREE(packet);
+
+ if (length)
+ yahoo_add_to_send_queue(yid, image, length);
+}
+
+void yahoo_webcam_accept_viewer(int id, const char* who, int accept)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
+ char *packet = NULL;
+ char *data = NULL;
+ unsigned char header_len = 13;
+ unsigned int pos = 0;
+ unsigned int len = 0;
+
+ if (!yid)
+ return;
+
+ data = strdup("u=");
+ data = y_string_append(data, (char*)who);
+ data = y_string_append(data, "\r\n");
+ len = strlen(data);
+
+ packet = y_new0(char, header_len + len);
+ packet[pos++] = header_len;
+ packet[pos++] = 0;
+ packet[pos++] = 5; /* version byte?? */
+ packet[pos++] = 0;
+ pos += yahoo_put32(packet + pos, len);
+ packet[pos++] = 0; /* packet type */
+ pos += yahoo_put32(packet + pos, accept);
+ memcpy(packet + pos, data, len);
+ FREE(data);
+ yahoo_add_to_send_queue(yid, packet, header_len + len);
+ FREE(packet);
+}
+
+void yahoo_webcam_invite(int id, const char *who)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_packet *pkt;
+
+ if(!yid)
+ return;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id);
+
+ yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
+ yahoo_packet_hash(pkt, 14, " ");
+ yahoo_packet_hash(pkt, 13, "0");
+ yahoo_packet_hash(pkt, 1, yid->yd->user);
+ yahoo_packet_hash(pkt, 5, who);
+ yahoo_send_packet(yid, pkt, 0);
+
+ yahoo_packet_free(pkt);
+}
+
+static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct yahoo_input_data *yid;
+ char url[1024];
+ char buff[1024];
+ char *ctext, *p;
+
+ if(!yd)
+ return;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->yd = yd;
+ yid->type = YAHOO_CONNECTION_SEARCH;
+
+ /*
+ age range
+ .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
+ */
+
+ snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos);
+
+ ctext = strdup(text);
+ while((p = strchr(ctext, ' ')))
+ *p = '+';
+
+ snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
+ ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
+ startpos ? buff : "");
+
+ FREE(ctext);
+
+ snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+
+ inputs = y_list_prepend(inputs, yid);
+ yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
+}
+
+void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar,
+ int photo, int yahoo_only)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_search_state *yss;
+
+ if(!yid)
+ return;
+
+ if(!yid->ys)
+ yid->ys = y_new0(struct yahoo_search_state, 1);
+
+ yss = yid->ys;
+
+ FREE(yss->lsearch_text);
+ yss->lsearch_type = t;
+ yss->lsearch_text = strdup(text);
+ yss->lsearch_gender = g;
+ yss->lsearch_agerange = ar;
+ yss->lsearch_photo = photo;
+ yss->lsearch_yahoo_only = yahoo_only;
+
+ yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
+}
+
+void yahoo_search_again(int id, int start)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+ struct yahoo_search_state *yss;
+
+ if(!yid || !yid->ys)
+ return;
+
+ yss = yid->ys;
+
+ if(start == -1)
+ start = yss->lsearch_nstart + yss->lsearch_nfound;
+
+ yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text,
+ yss->lsearch_gender, yss->lsearch_agerange,
+ yss->lsearch_photo, yss->lsearch_yahoo_only,
+ start, yss->lsearch_ntotal);
+}
+
+struct send_file_data {
+ struct yahoo_packet *pkt;
+ yahoo_get_fd_callback callback;
+ void *user_data;
+};
+
+static void _yahoo_send_file_connected(int id, int fd, int error, void *data)
+{
+ struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT);
+ struct send_file_data *sfd = data;
+ struct yahoo_packet *pkt = sfd->pkt;
+ unsigned char buff[1024];
+
+ if(fd <= 0) {
+ sfd->callback(id, fd, error, sfd->user_data);
+ FREE(sfd);
+ yahoo_packet_free(pkt);
+ inputs = y_list_remove(inputs, yid);
+ FREE(yid);
+ return;
+ }
+
+ yid->fd = fd;
+ yahoo_send_packet(yid, pkt, 8);
+ yahoo_packet_free(pkt);
+
+ snprintf((char *)buff, sizeof(buff), "29");
+ buff[2] = 0xc0;
+ buff[3] = 0x80;
+
+ write(yid->fd, buff, 4);
+
+/* YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */
+
+ sfd->callback(id, fd, error, sfd->user_data);
+ FREE(sfd);
+ inputs = y_list_remove(inputs, yid);
+ /*
+ while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) {
+ if(!strcmp(buff, ""))
+ break;
+ }
+
+ */
+ yahoo_input_close(yid);
+}
+
+void yahoo_send_file(int id, const char *who, const char *msg,
+ const char *name, unsigned long size,
+ yahoo_get_fd_callback callback, void *data)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ struct yahoo_input_data *yid;
+ struct yahoo_server_settings *yss;
+ struct yahoo_packet *pkt = NULL;
+ char size_str[10];
+ long content_length=0;
+ unsigned char buff[1024];
+ char url[255];
+ struct send_file_data *sfd;
+
+ if(!yd)
+ return;
+
+ yss = yd->server_settings;
+
+ yid = y_new0(struct yahoo_input_data, 1);
+ yid->yd = yd;
+ yid->type = YAHOO_CONNECTION_FT;
+
+ pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+ snprintf(size_str, sizeof(size_str), "%ld", size);
+
+ yahoo_packet_hash(pkt, 0, yd->user);
+ yahoo_packet_hash(pkt, 5, who);
+ yahoo_packet_hash(pkt, 14, msg);
+ yahoo_packet_hash(pkt, 27, name);
+ yahoo_packet_hash(pkt, 28, size_str);
+
+ content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
+
+ snprintf(url, sizeof(url), "http://%s:%d/notifyft",
+ yss->filetransfer_host, yss->filetransfer_port);
+ snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s",
+ yd->cookie_y, yd->cookie_t);
+ inputs = y_list_prepend(inputs, yid);
+
+ sfd = y_new0(struct send_file_data, 1);
+ sfd->pkt = pkt;
+ sfd->callback = callback;
+ sfd->user_data = data;
+ yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size,
+ _yahoo_send_file_connected, sfd);
+}
+
+
+enum yahoo_status yahoo_current_status(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return YAHOO_STATUS_OFFLINE;
+ return yd->current_status;
+}
+
+const YList * yahoo_get_buddylist(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return NULL;
+ return yd->buddies;
+}
+
+const YList * yahoo_get_ignorelist(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return NULL;
+ return yd->ignore;
+}
+
+const YList * yahoo_get_identities(int id)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return NULL;
+ return yd->identities;
+}
+
+const char * yahoo_get_cookie(int id, const char *which)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return NULL;
+ if(!strncasecmp(which, "y", 1))
+ return yd->cookie_y;
+ if(!strncasecmp(which, "t", 1))
+ return yd->cookie_t;
+ if(!strncasecmp(which, "c", 1))
+ return yd->cookie_c;
+ if(!strncasecmp(which, "login", 5))
+ return yd->login_cookie;
+ return NULL;
+}
+
+void yahoo_get_url_handle(int id, const char *url,
+ yahoo_get_url_handle_callback callback, void *data)
+{
+ struct yahoo_data *yd = find_conn_by_id(id);
+ if(!yd)
+ return;
+
+ yahoo_get_url_fd(id, url, yd, callback, data);
+}
+
+const char * yahoo_get_profile_url( void )
+{
+ return profile_url;
+}
+
diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c
new file mode 100644
index 00000000..77fd86cc
--- /dev/null
+++ b/protocols/yahoo/yahoo.c
@@ -0,0 +1,958 @@
+/*
+ * libyahoo2 wrapper to BitlBee
+ *
+ * Mostly Copyright 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "nogaim.h"
+#include "yahoo2.h"
+#include "yahoo2_callbacks.h"
+
+#define BYAHOO_DEFAULT_GROUP "Buddies"
+
+/* A hack to handle removal of buddies not in the group "Buddies" correctly */
+struct byahoo_buddygroups
+{
+ char *buddy;
+ char *group;
+};
+
+struct byahoo_data
+{
+ int y2_id;
+ int current_status;
+ gboolean logged_in;
+ GSList *buddygroups;
+};
+
+struct byahoo_input_data
+{
+ int h;
+ void *d;
+};
+
+struct byahoo_conf_invitation
+{
+ char *name;
+ struct conversation *c;
+ int yid;
+ YList *members;
+ struct gaim_connection *gc;
+};
+
+static char *yahoo_name()
+{
+ return "Yahoo";
+}
+
+static struct prpl *my_protocol = NULL;
+static GSList *byahoo_inputs = NULL;
+static int byahoo_chat_id = 0;
+
+static char *byahoo_strip( char *in )
+{
+ int len;
+
+ /* This should get rid of HTML tags at the beginning of the string. */
+ while( *in )
+ {
+ if( g_strncasecmp( in, "<font", 5 ) == 0 ||
+ g_strncasecmp( in, "<fade", 5 ) == 0 ||
+ g_strncasecmp( in, "<alt", 4 ) == 0 )
+ {
+ char *s = strchr( in, '>' );
+ if( !s )
+ break;
+
+ in = s + 1;
+ }
+ else if( strncmp( in, "\e[", 2 ) == 0 )
+ {
+ char *s;
+
+ for( s = in + 2; *s && *s != 'm'; s ++ );
+
+ if( *s != 'm' )
+ break;
+
+ in = s + 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* This is supposed to get rid of the closing HTML tags at the end of the line. */
+ len = strlen( in );
+ while( len > 0 && in[len-1] == '>' )
+ {
+ int blen = len;
+
+ len --;
+ while( len > 0 && ( in[len] != '<' || in[len+1] != '/' ) )
+ len --;
+
+ if( len == 0 && ( in[len] != '<' || in[len+1] != '/' ) )
+ {
+ len = blen;
+ break;
+ }
+ }
+
+ return( g_strndup( in, len ) );
+}
+
+static void byahoo_login( struct aim_user *user )
+{
+ struct gaim_connection *gc = new_gaim_conn( user );
+ struct byahoo_data *yd = gc->proto_data = g_new0( struct byahoo_data, 1 );
+
+ yd->logged_in = FALSE;
+ yd->current_status = YAHOO_STATUS_AVAILABLE;
+
+ set_login_progress( gc, 1, "Connecting" );
+ yd->y2_id = yahoo_init( user->username, user->password );
+ yahoo_login( yd->y2_id, yd->current_status );
+}
+
+static void byahoo_close( struct gaim_connection *gc )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ GSList *l;
+
+ while( gc->conversations )
+ serv_got_chat_left( gc, gc->conversations->id );
+
+ for( l = yd->buddygroups; l; l = l->next )
+ {
+ struct byahoo_buddygroups *bg = l->data;
+
+ g_free( bg->buddy );
+ g_free( bg->group );
+ g_free( bg );
+ }
+ g_slist_free( yd->buddygroups );
+
+ if( yd->logged_in )
+ yahoo_logoff( yd->y2_id );
+ else
+ yahoo_close( yd->y2_id );
+
+ g_free( yd );
+}
+
+static void byahoo_get_info(struct gaim_connection *gc, char *who)
+{
+ /* Just make an URL and let the user fetch the info */
+ serv_got_crap(gc, "%s\n%s: %s%s", _("User Info"),
+ _("For now, fetch yourself"), yahoo_get_profile_url(),
+ who);
+}
+
+static int byahoo_send_im( struct gaim_connection *gc, char *who, char *what, int len, int flags )
+{
+ struct byahoo_data *yd = gc->proto_data;
+
+ yahoo_send_im( yd->y2_id, NULL, who, what, 1 );
+
+ return 1;
+}
+
+static int byahoo_send_typing( struct gaim_connection *gc, char *who, int typing )
+{
+ struct byahoo_data *yd = gc->proto_data;
+
+ yahoo_send_typing( yd->y2_id, NULL, who, typing );
+
+ return 1;
+}
+
+static void byahoo_set_away( struct gaim_connection *gc, char *state, char *msg )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+
+ gc->away = NULL;
+
+ if (msg)
+ {
+ yd->current_status = YAHOO_STATUS_CUSTOM;
+ gc->away = "";
+ }
+ else if (state)
+ {
+ gc->away = "";
+ if( g_strcasecmp(state, "Available" ) == 0 )
+ {
+ yd->current_status = YAHOO_STATUS_AVAILABLE;
+ gc->away = NULL;
+ }
+ else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
+ yd->current_status = YAHOO_STATUS_BRB;
+ else if( g_strcasecmp( state, "Busy" ) == 0 )
+ yd->current_status = YAHOO_STATUS_BUSY;
+ else if( g_strcasecmp( state, "Not At Home" ) == 0 )
+ yd->current_status = YAHOO_STATUS_NOTATHOME;
+ else if( g_strcasecmp( state, "Not At Desk" ) == 0 )
+ yd->current_status = YAHOO_STATUS_NOTATDESK;
+ else if( g_strcasecmp( state, "Not In Office" ) == 0 )
+ yd->current_status = YAHOO_STATUS_NOTINOFFICE;
+ else if( g_strcasecmp( state, "On Phone" ) == 0 )
+ yd->current_status = YAHOO_STATUS_ONPHONE;
+ else if( g_strcasecmp( state, "On Vacation" ) == 0 )
+ yd->current_status = YAHOO_STATUS_ONVACATION;
+ else if( g_strcasecmp( state, "Out To Lunch" ) == 0 )
+ yd->current_status = YAHOO_STATUS_OUTTOLUNCH;
+ else if( g_strcasecmp( state, "Stepped Out" ) == 0 )
+ yd->current_status = YAHOO_STATUS_STEPPEDOUT;
+ else if( g_strcasecmp( state, "Invisible" ) == 0 )
+ yd->current_status = YAHOO_STATUS_INVISIBLE;
+ else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 )
+ {
+ if (gc->is_idle)
+ yd->current_status = YAHOO_STATUS_IDLE;
+ else
+ yd->current_status = YAHOO_STATUS_AVAILABLE;
+
+ gc->away = NULL;
+ }
+ }
+ else if ( gc->is_idle )
+ yd->current_status = YAHOO_STATUS_IDLE;
+ else
+ yd->current_status = YAHOO_STATUS_AVAILABLE;
+
+ yahoo_set_away( yd->y2_id, yd->current_status, msg, gc->away != NULL );
+}
+
+static GList *byahoo_away_states( struct gaim_connection *gc )
+{
+ GList *m = NULL;
+
+ m = g_list_append( m, "Available" );
+ m = g_list_append( m, "Be Right Back" );
+ m = g_list_append( m, "Busy" );
+ m = g_list_append( m, "Not At Home" );
+ m = g_list_append( m, "Not At Desk" );
+ m = g_list_append( m, "Not In Office" );
+ m = g_list_append( m, "On Phone" );
+ m = g_list_append( m, "On Vacation" );
+ m = g_list_append( m, "Out To Lunch" );
+ m = g_list_append( m, "Stepped Out" );
+ m = g_list_append( m, "Invisible" );
+ m = g_list_append( m, GAIM_AWAY_CUSTOM );
+
+ return m;
+}
+
+static void byahoo_keepalive( struct gaim_connection *gc )
+{
+ struct byahoo_data *yd = gc->proto_data;
+
+ yahoo_keepalive( yd->y2_id );
+}
+
+static void byahoo_add_buddy( struct gaim_connection *gc, char *who )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+
+ yahoo_add_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP );
+}
+
+static void byahoo_remove_buddy( struct gaim_connection *gc, char *who, char *group )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ GSList *bgl;
+
+ yahoo_remove_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP );
+
+ for( bgl = yd->buddygroups; bgl; bgl = bgl->next )
+ {
+ struct byahoo_buddygroups *bg = bgl->data;
+
+ if( g_strcasecmp( bg->buddy, who ) == 0 )
+ yahoo_remove_buddy( yd->y2_id, who, bg->group );
+ }
+}
+
+static char *byahoo_get_status_string( struct gaim_connection *gc, int stat )
+{
+ enum yahoo_status a = stat >> 1;
+
+ switch (a)
+ {
+ case YAHOO_STATUS_BRB:
+ return "Be Right Back";
+ case YAHOO_STATUS_BUSY:
+ return "Busy";
+ case YAHOO_STATUS_NOTATHOME:
+ return "Not At Home";
+ case YAHOO_STATUS_NOTATDESK:
+ return "Not At Desk";
+ case YAHOO_STATUS_NOTINOFFICE:
+ return "Not In Office";
+ case YAHOO_STATUS_ONPHONE:
+ return "On Phone";
+ case YAHOO_STATUS_ONVACATION:
+ return "On Vacation";
+ case YAHOO_STATUS_OUTTOLUNCH:
+ return "Out To Lunch";
+ case YAHOO_STATUS_STEPPEDOUT:
+ return "Stepped Out";
+ case YAHOO_STATUS_INVISIBLE:
+ return "Invisible";
+ case YAHOO_STATUS_CUSTOM:
+ return "Away";
+ case YAHOO_STATUS_IDLE:
+ return "Idle";
+ case YAHOO_STATUS_OFFLINE:
+ return "Offline";
+ case YAHOO_STATUS_NOTIFY:
+ return "Notify";
+ default:
+ return "Away";
+ }
+}
+
+static int byahoo_chat_send( struct gaim_connection *gc, int id, char *message )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ struct conversation *c;
+
+ for( c = gc->conversations; c && c->id != id; c = c->next );
+
+ yahoo_conference_message( yd->y2_id, NULL, c->data, c->title, message, 1 );
+
+ return( 0 );
+}
+
+static void byahoo_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ struct conversation *c;
+
+ for( c = gc->conversations; c && c->id != id; c = c->next );
+
+ yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg );
+}
+
+static void byahoo_chat_leave( struct gaim_connection *gc, int id )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ struct conversation *c;
+
+ for( c = gc->conversations; c && c->id != id; c = c->next );
+
+ yahoo_conference_logoff( yd->y2_id, NULL, c->data, c->title );
+ serv_got_chat_left( gc, c->id );
+}
+
+static int byahoo_chat_open( struct gaim_connection *gc, char *who )
+{
+ struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+ struct conversation *c;
+ char *roomname;
+ YList *members;
+
+ roomname = g_new0( char, strlen( gc->username ) + 16 );
+ g_snprintf( roomname, strlen( gc->username ) + 16, "%s-Bee-%d", gc->username, byahoo_chat_id );
+
+ c = serv_got_joined_chat( gc, ++byahoo_chat_id, roomname );
+ add_chat_buddy( c, gc->username );
+
+ /* FIXME: Free this thing when the chat's destroyed. We can't *always*
+ do this because it's not always created here. */
+ c->data = members = g_new0( YList, 1 );
+ members->data = g_strdup( who );
+
+ yahoo_conference_invite( yd->y2_id, NULL, members, roomname, "Please join my groupchat..." );
+
+ g_free( roomname );
+
+ return( 1 );
+}
+
+void byahoo_init( struct prpl *ret )
+{
+ ret->protocol = PROTO_YAHOO;
+ ret->name = yahoo_name;
+
+ ret->login = byahoo_login;
+ ret->close = byahoo_close;
+ ret->send_im = byahoo_send_im;
+ ret->send_typing = byahoo_send_typing;
+ ret->get_info = byahoo_get_info;
+ ret->away_states = byahoo_away_states;
+ ret->set_away = byahoo_set_away;
+ ret->keepalive = byahoo_keepalive;
+ ret->add_buddy = byahoo_add_buddy;
+ ret->remove_buddy = byahoo_remove_buddy;
+ ret->get_status_string = byahoo_get_status_string;
+
+ ret->chat_send = byahoo_chat_send;
+ ret->chat_invite = byahoo_chat_invite;
+ ret->chat_leave = byahoo_chat_leave;
+ ret->chat_open = byahoo_chat_open;
+
+ my_protocol = ret;
+}
+
+static struct gaim_connection *byahoo_get_gc_by_id( int id )
+{
+ GSList *l;
+ struct gaim_connection *gc;
+ struct byahoo_data *yd;
+
+ for( l = get_connections(); l; l = l->next )
+ {
+ gc = l->data;
+ yd = gc->proto_data;
+
+ if( gc->protocol == PROTO_YAHOO && yd->y2_id == id )
+ return( gc );
+ }
+
+ return( NULL );
+}
+
+
+/* Now it's callback time! */
+
+struct byahoo_connect_callback_data
+{
+ int fd;
+ yahoo_connect_callback callback;
+ gpointer data;
+ int id;
+};
+
+void byahoo_connect_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct byahoo_connect_callback_data *d = data;
+
+ if( !byahoo_get_gc_by_id( d->id ) )
+ {
+ g_free( d );
+ return;
+ }
+
+ d->callback( d->fd, 0, d->data );
+ g_free( d );
+}
+
+struct byahoo_read_ready_data
+{
+ int id;
+ int fd;
+ int tag;
+ gpointer data;
+};
+
+void byahoo_read_ready_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct byahoo_read_ready_data *d = data;
+
+ if( !byahoo_get_gc_by_id( d->id ) )
+ {
+ /* WTF doesn't libyahoo clean this up? */
+ ext_yahoo_remove_handler( d->id, d->tag );
+ return;
+ }
+
+ yahoo_read_ready( d->id, d->fd, d->data );
+}
+
+struct byahoo_write_ready_data
+{
+ int id;
+ int fd;
+ int tag;
+ gpointer data;
+};
+
+void byahoo_write_ready_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+ struct byahoo_write_ready_data *d = data;
+
+ if( !byahoo_get_gc_by_id( d->id ) )
+ {
+ /* WTF doesn't libyahoo clean this up? */
+ ext_yahoo_remove_handler( d->id, d->tag );
+ return;
+ }
+
+ yahoo_write_ready( d->id, d->fd, d->data );
+}
+
+void ext_yahoo_login_response( int id, int succ, char *url )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ struct byahoo_data *yd = NULL;
+
+ if( gc == NULL )
+ {
+ /* libyahoo2 seems to call this one twice when something
+ went wrong sometimes. Don't know why. Because we clean
+ up the connection on the first failure, the second
+ should be ignored. */
+
+ return;
+ }
+
+ yd = (struct byahoo_data *) gc->proto_data;
+
+ if( succ == YAHOO_LOGIN_OK )
+ {
+ account_online( gc );
+
+ yd->logged_in = TRUE;
+ }
+ else
+ {
+ char *errstr;
+ char *s;
+
+ yd->logged_in = FALSE;
+
+ if( succ == YAHOO_LOGIN_UNAME )
+ errstr = "Incorrect Yahoo! username";
+ else if( succ == YAHOO_LOGIN_PASSWD )
+ errstr = "Incorrect Yahoo! password";
+ else if( succ == YAHOO_LOGIN_LOCK )
+ errstr = "Yahoo! account locked";
+ else if( succ == YAHOO_LOGIN_DUPL )
+ {
+ errstr = "Logged in on a different machine or device";
+ gc->wants_to_die = TRUE;
+ }
+ else if( succ == YAHOO_LOGIN_SOCK )
+ errstr = "Socket problem";
+ else
+ errstr = "Unknown error";
+
+ if( url && *url )
+ {
+ s = g_malloc( strlen( "Error %d (%s). See %s for more information." ) + strlen( url ) + strlen( errstr ) + 16 );
+ sprintf( s, "Error %d (%s). See %s for more information.", succ, errstr, url );
+ }
+ else
+ {
+ s = g_malloc( strlen( "Error %d (%s)" ) + strlen( errstr ) + 16 );
+ sprintf( s, "Error %d (%s)", succ, errstr );
+ }
+
+ if( yd->logged_in )
+ hide_login_progress_error( gc, s );
+ else
+ hide_login_progress( gc, s );
+
+ g_free( s );
+
+ signoff( gc );
+ }
+}
+
+void ext_yahoo_got_buddies( int id, YList *buds )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ struct byahoo_data *yd = gc->proto_data;
+ YList *bl = buds;
+
+ while( bl )
+ {
+ struct yahoo_buddy *b = bl->data;
+ struct byahoo_buddygroups *bg;
+
+ if( strcmp( b->group, BYAHOO_DEFAULT_GROUP ) != 0 )
+ {
+ bg = g_new0( struct byahoo_buddygroups, 1 );
+
+ bg->buddy = g_strdup( b->id );
+ bg->group = g_strdup( b->group );
+ yd->buddygroups = g_slist_append( yd->buddygroups, bg );
+ }
+
+ add_buddy( gc, b->group, b->id, b->real_name );
+ bl = bl->next;
+ }
+}
+
+void ext_yahoo_got_ignore( int id, YList *igns )
+{
+}
+
+void ext_yahoo_got_identities( int id, YList *ids )
+{
+}
+
+void ext_yahoo_got_cookies( int id )
+{
+}
+
+void ext_yahoo_status_changed( int id, char *who, int stat, char *msg, int away )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_update( gc, who, stat != YAHOO_STATUS_OFFLINE, 0, 0, 0,
+ ( stat != YAHOO_STATUS_AVAILABLE ) | ( stat << 1 ), 0 );
+}
+
+void ext_yahoo_got_im( int id, char *who, char *msg, long tm, int stat, int utf8 )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ char *m = byahoo_strip( msg );
+
+ serv_got_im( gc, who, m, 0, 0, strlen( m ) );
+ g_free( m );
+}
+
+void ext_yahoo_got_file( int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_crap( gc, "Got a file transfer (file = %s) from %s. Ignoring for now due to lack of support.", fname, who );
+}
+
+void ext_yahoo_typing_notify( int id, char *who, int stat )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_typing( gc, who, 1 );
+}
+
+void ext_yahoo_system_message( int id, char *msg )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_crap( gc, "Yahoo! system message: %s", msg );
+}
+
+void ext_yahoo_webcam_invite( int id, char *from )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_crap( gc, "Got a webcam invitation from %s. IRC+webcams is a no-no though...", from );
+}
+
+void ext_yahoo_error( int id, char *err, int fatal )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ if( fatal )
+ {
+ hide_login_progress_error( gc, err );
+ signoff( gc );
+ }
+ else
+ {
+ do_error_dialog( gc, err, "Yahoo! error" );
+ }
+}
+
+/* TODO: Clear up the mess of inp and d structures */
+int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *data )
+{
+ struct byahoo_input_data *inp = g_new0( struct byahoo_input_data, 1 );
+
+ if( cond == YAHOO_INPUT_READ )
+ {
+ struct byahoo_read_ready_data *d = g_new0( struct byahoo_read_ready_data, 1 );
+
+ d->id = id;
+ d->fd = fd;
+ d->data = data;
+
+ inp->d = d;
+ d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_READ, (GaimInputFunction) byahoo_read_ready_callback, (gpointer) d );
+ }
+ else if( cond == YAHOO_INPUT_WRITE )
+ {
+ struct byahoo_write_ready_data *d = g_new0( struct byahoo_write_ready_data, 1 );
+
+ d->id = id;
+ d->fd = fd;
+ d->data = data;
+
+ inp->d = d;
+ d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_WRITE, (GaimInputFunction) byahoo_write_ready_callback, (gpointer) d );
+ }
+ else
+ {
+ g_free( inp );
+ return( -1 );
+ /* Panic... */
+ }
+
+ byahoo_inputs = g_slist_append( byahoo_inputs, inp );
+ return( inp->h );
+}
+
+void ext_yahoo_remove_handler( int id, int tag )
+{
+ struct byahoo_input_data *inp;
+ GSList *l = byahoo_inputs;
+
+ while( l )
+ {
+ inp = l->data;
+ if( inp->h == tag )
+ {
+ g_free( inp->d );
+ g_free( inp );
+ byahoo_inputs = g_slist_remove( byahoo_inputs, inp );
+ break;
+ }
+ l = l->next;
+ }
+
+ gaim_input_remove( tag );
+}
+
+int ext_yahoo_connect_async( int id, char *host, int port, yahoo_connect_callback callback, void *data )
+{
+ struct byahoo_connect_callback_data *d;
+ int fd;
+
+ d = g_new0( struct byahoo_connect_callback_data, 1 );
+ if( ( fd = proxy_connect( host, port, (GaimInputFunction) byahoo_connect_callback, (gpointer) d ) ) < 0 )
+ {
+ g_free( d );
+ return( fd );
+ }
+ d->fd = fd;
+ d->callback = callback;
+ d->data = data;
+ d->id = id;
+
+ return( fd );
+}
+
+/* Because we don't want asynchronous connects in BitlBee, and because
+ libyahoo doesn't seem to use this one anyway, this one is now defunct. */
+int ext_yahoo_connect(char *host, int port)
+{
+#if 0
+ struct sockaddr_in serv_addr;
+ static struct hostent *server;
+ static char last_host[256];
+ int servfd;
+ char **p;
+
+ if(last_host[0] || g_strcasecmp(last_host, host)!=0) {
+ if(!(server = gethostbyname(host))) {
+ return -1;
+ }
+ strncpy(last_host, host, 255);
+ }
+
+ if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ for (p = server->h_addr_list; *p; p++)
+ {
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ memcpy(&serv_addr.sin_addr.s_addr, *p, server->h_length);
+ serv_addr.sin_port = htons(port);
+
+ if(connect(servfd, (struct sockaddr *) &serv_addr,
+ sizeof(serv_addr)) == -1) {
+ return -1;
+ } else {
+ return servfd;
+ }
+ }
+
+ closesocket(servfd);
+#endif
+ return -1;
+}
+
+static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
+{
+ yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
+ add_chat_buddy( inv->c, inv->gc->username );
+ g_free( inv->name );
+ g_free( inv );
+}
+
+static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
+{
+ yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
+ serv_got_chat_left( inv->gc, inv->c->id );
+ g_free( inv->name );
+ g_free( inv );
+}
+
+void ext_yahoo_got_conf_invite( int id, char *who, char *room, char *msg, YList *members )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ struct byahoo_conf_invitation *inv;
+ char txt[1024];
+ YList *m;
+
+ inv = g_malloc( sizeof( struct byahoo_conf_invitation ) );
+ memset( inv, 0, sizeof( struct byahoo_conf_invitation ) );
+ inv->name = g_strdup( room );
+ inv->c = serv_got_joined_chat( gc, ++byahoo_chat_id, room );
+ inv->c->data = members;
+ inv->yid = id;
+ inv->members = members;
+ inv->gc = gc;
+
+ for( m = members; m; m = m->next )
+ if( g_strcasecmp( m->data, gc->username ) != 0 )
+ add_chat_buddy( inv->c, m->data );
+
+ g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", room, who, msg );
+
+ do_ask_dialog( gc, txt, inv, byahoo_accept_conf, byahoo_reject_conf );
+}
+
+void ext_yahoo_conf_userdecline( int id, char *who, char *room, char *msg )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ serv_got_crap( gc, "Invite to chatroom %s rejected by %s: %s", room, who, msg );
+}
+
+void ext_yahoo_conf_userjoin( int id, char *who, char *room )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ struct conversation *c;
+
+ for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+
+ if( c )
+ add_chat_buddy( c, who );
+ else if( set_getint( gc->irc, "debug" ) )
+ serv_got_crap( gc, "Got ext_yahoo_conf_userjoin() from %s for unknown conference %s", who, room );
+}
+
+void ext_yahoo_conf_userleave( int id, char *who, char *room )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ struct conversation *c;
+
+ for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+
+ if( c )
+ remove_chat_buddy( c, who, "" );
+ else if( set_getint( gc->irc, "debug" ) )
+ serv_got_crap( gc, "Got ext_yahoo_conf_userleave() from %s for unknown conference %s", who, room );
+}
+
+void ext_yahoo_conf_message( int id, char *who, char *room, char *msg, int utf8 )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+ char *m = byahoo_strip( msg );
+ struct conversation *c;
+
+ for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+
+ serv_got_chat_in( gc, c ? c->id : 0, who, 0, m, 0 );
+ g_free( m );
+}
+
+void ext_yahoo_chat_cat_xml( int id, char *xml )
+{
+}
+
+void ext_yahoo_chat_join( int id, char *room, char *topic, YList *members, int fd )
+{
+}
+
+void ext_yahoo_chat_userjoin( int id, char *room, struct yahoo_chat_member *who )
+{
+}
+
+void ext_yahoo_chat_userleave( int id, char *room, char *who )
+{
+}
+
+void ext_yahoo_chat_message( int id, char *who, char *room, char *msg, int msgtype, int utf8 )
+{
+}
+
+void ext_yahoo_chat_yahoologout( int id )
+{
+}
+
+void ext_yahoo_chat_yahooerror( int id )
+{
+}
+
+void ext_yahoo_contact_added( int id, char *myid, char *who, char *msg )
+{
+}
+
+void ext_yahoo_rejected( int id, char *who, char *msg )
+{
+}
+
+void ext_yahoo_game_notify( int id, char *who, int stat )
+{
+}
+
+void ext_yahoo_mail_notify( int id, char *from, char *subj, int cnt )
+{
+ struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+
+ if( from && subj )
+ serv_got_crap( gc, "Received e-mail message from %s with subject `%s'", from, subj );
+ else if( cnt > 0 )
+ serv_got_crap( gc, "Received %d new e-mails", cnt );
+}
+
+void ext_yahoo_webcam_invite_reply( int id, char *from, int accept )
+{
+}
+
+void ext_yahoo_webcam_closed( int id, char *who, int reason )
+{
+}
+
+void ext_yahoo_got_search_result( int id, int found, int start, int total, YList *contacts )
+{
+}
+
+void ext_yahoo_webcam_viewer( int id, char *who, int connect )
+{
+}
+
+void ext_yahoo_webcam_data_request( int id, int send )
+{
+}
+
+int ext_yahoo_log( char *fmt, ... )
+{
+ return( 0 );
+}
+
+void ext_yahoo_got_webcam_image( int id, const char * who, const unsigned char *image, unsigned int image_size, unsigned int real_size, unsigned int timestamp )
+{
+}
diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h
new file mode 100644
index 00000000..5ac5e4f9
--- /dev/null
+++ b/protocols/yahoo/yahoo2.h
@@ -0,0 +1,222 @@
+/*
+ * libyahoo2: yahoo2.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef YAHOO2_H
+#define YAHOO2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* *** BitlBee: *** */
+#include "bitlbee.h"
+#undef free
+#define free( x ) g_free( x )
+#undef malloc
+#define malloc( x ) g_malloc( x )
+#undef calloc
+#define calloc( x, y ) g_calloc( x, y )
+#undef realloc
+#define realloc( x, y ) g_realloc( x, y )
+#undef strdup
+#define strdup( x ) g_strdup( x )
+#undef strndup
+#define strndup( x,y ) g_strndup( x,y )
+#undef snprintf
+// #define snprintf( x... ) g_snprintf( x )
+#undef strcasecmp
+#define strcasecmp( x,y ) g_strcasecmp( x,y )
+#undef strncasecmp
+#define strncasecmp( x,y,z ) g_strncasecmp( x,y,z )
+
+
+#include "yahoo2_types.h"
+
+/* returns the socket descriptor for a given pager connection. shouldn't be needed */
+int yahoo_get_fd(int id);
+
+/* says how much logging to do */
+/* see yahoo2_types.h for the different values */
+int yahoo_set_log_level(enum yahoo_log_level level);
+enum yahoo_log_level yahoo_get_log_level( void );
+
+/* these functions should be self explanatory */
+/* who always means the buddy you're acting on */
+/* id is the successful value returned by yahoo_init */
+
+
+/* init returns a connection id used to identify the connection hereon */
+/* or 0 on failure */
+/* you must call init before calling any other function */
+/*
+ * The optional parameters to init are key/value pairs that specify
+ * server settings to use. This list must be NULL terminated - even
+ * if the list is empty. If a parameter isn't set, a default value
+ * will be used. Parameter keys are strings, parameter values are
+ * either strings or ints, depending on the key. Values passed in
+ * are copied, so you can use const/auto/static/pointers/whatever
+ * you want. Parameters are:
+ * NAME TYPE DEFAULT
+ * pager_host char * scs.msg.yahoo.com
+ * pager_port int 5050
+ * filetransfer_host char * filetransfer.msg.yahoo.com
+ * filetransfer_port int 80
+ * webcam_host char * webcam.yahoo.com
+ * webcam_port int 5100
+ * webcam_description char * ""
+ * local_host char * ""
+ * conn_type int Y_WCM_DSL
+ *
+ * You should set at least local_host if you intend to use webcams
+ */
+int yahoo_init_with_attributes(const char *username, const char *password, ...);
+
+/* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults
+ * for all attributes */
+int yahoo_init(const char *username, const char *password);
+
+
+
+/* release all resources held by this session */
+/* you need to call yahoo_close for a session only if
+ * yahoo_logoff is never called for it (ie, it was never logged in) */
+void yahoo_close(int id);
+/* login logs in to the server */
+/* initial is of type enum yahoo_status. see yahoo2_types.h */
+void yahoo_login(int id, int initial);
+void yahoo_logoff(int id);
+/* reloads status of all buddies */
+void yahoo_refresh(int id);
+/* activates/deactivates an identity */
+void yahoo_set_identity_status(int id, const char * identity, int active);
+/* regets the entire buddy list from the server */
+void yahoo_get_list(int id);
+/* download buddy contact information from your yahoo addressbook */
+void yahoo_get_yab(int id);
+/* add/modify an address book entry. if yab->dbid is set, it will */
+/* modify that entry else it creates a new entry */
+void yahoo_set_yab(int id, struct yab * yab);
+void yahoo_keepalive(int id);
+void yahoo_chat_keepalive(int id);
+
+/* from is the identity you're sending from. if NULL, the default is used */
+/* utf8 is whether msg is a utf8 string or not. */
+void yahoo_send_im(int id, const char *from, const char *who, const char *msg, int utf8);
+/* if type is true, send typing notice, else send stopped typing notice */
+void yahoo_send_typing(int id, const char *from, const char *who, int typ);
+
+/* used to set away/back status. */
+/* away says whether the custom message is an away message or a sig */
+void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away);
+
+void yahoo_add_buddy(int id, const char *who, const char *group);
+void yahoo_remove_buddy(int id, const char *who, const char *group);
+void yahoo_reject_buddy(int id, const char *who, const char *msg);
+/* if unignore is true, unignore, else ignore */
+void yahoo_ignore_buddy(int id, const char *who, int unignore);
+void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group);
+void yahoo_group_rename(int id, const char *old_group, const char *new_group);
+
+void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg);
+void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg);
+void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg);
+void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8);
+void yahoo_conference_logon(int id, const char * from, YList *who, const char *room);
+void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room);
+
+/* Get a list of chatrooms */
+void yahoo_get_chatrooms(int id,int chatroomid);
+/* join room with specified roomname and roomid */
+void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid);
+/* Send message "msg" to room with specified roomname, msgtype is 1-normal message or 2-/me mesage */
+void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8);
+/* Log off chat */
+void yahoo_chat_logoff(int id, const char *from);
+
+/* requests a webcam feed */
+/* who is the person who's webcam you would like to view */
+/* if who is null, then you're the broadcaster */
+void yahoo_webcam_get_feed(int id, const char *who);
+void yahoo_webcam_close_feed(int id, const char *who);
+
+/* sends an image when uploading */
+/* image points to a JPEG-2000 image, length is the length of the image */
+/* in bytes. The timestamp is the time in milliseconds since we started the */
+/* webcam. */
+void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp);
+
+/* this function should be called if we want to allow a user to watch the */
+/* webcam. Who is the user we want to accept. */
+/* Accept user (accept = 1), decline user (accept = 0) */
+void yahoo_webcam_accept_viewer(int id, const char* who, int accept);
+
+/* send an invitation to a user to view your webcam */
+void yahoo_webcam_invite(int id, const char *who);
+
+/* will set up a connection and initiate file transfer.
+ * callback will be called with the fd that you should write
+ * the file data to
+ */
+void yahoo_send_file(int id, const char *who, const char *msg, const char *name, unsigned long size,
+ yahoo_get_fd_callback callback, void *data);
+
+/* send a search request
+ */
+void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar,
+ int photo, int yahoo_only);
+
+/* continue last search
+ * should be called if only (start+found >= total)
+ *
+ * where the above three are passed to ext_yahoo_got_search_result
+ */
+void yahoo_search_again(int id, int start);
+
+/* returns a socket fd to a url for downloading a file. */
+void yahoo_get_url_handle(int id, const char *url,
+ yahoo_get_url_handle_callback callback, void *data);
+
+/* these should be called when input is available on a fd */
+/* registered by ext_yahoo_add_handler */
+/* if these return negative values, errno may be set */
+int yahoo_read_ready(int id, int fd, void *data);
+int yahoo_write_ready(int id, int fd, void *data);
+
+/* utility functions. these do not hit the server */
+enum yahoo_status yahoo_current_status(int id);
+const YList * yahoo_get_buddylist(int id);
+const YList * yahoo_get_ignorelist(int id);
+const YList * yahoo_get_identities(int id);
+/* 'which' could be y, t, c or login. This may change in later versions. */
+const char * yahoo_get_cookie(int id, const char *which);
+
+/* returns the url used to get user profiles - you must append the user id */
+/* as of now this is http://profiles.yahoo.com/ */
+/* You'll have to do urlencoding yourself, but see yahoo_httplib.h first */
+const char * yahoo_get_profile_url( void );
+
+#include "yahoo_httplib.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h
new file mode 100644
index 00000000..1ab8a9d7
--- /dev/null
+++ b/protocols/yahoo/yahoo2_callbacks.h
@@ -0,0 +1,697 @@
+/*
+ * libyahoo2: yahoo2_callbacks.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * The functions in this file *must* be defined in your client program
+ * If you want to use a callback structure instead of direct functions,
+ * then you must define USE_STRUCT_CALLBACKS in all files that #include
+ * this one.
+ *
+ * Register the callback structure by calling yahoo_register_callbacks -
+ * declared in this file and defined in libyahoo2.c
+ */
+
+
+
+#ifndef YAHOO2_CALLBACKS_H
+#define YAHOO2_CALLBACKS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yahoo2_types.h"
+
+/*
+ * yahoo2_callbacks.h
+ *
+ * Callback interface for libyahoo2
+ */
+
+typedef enum {
+ YAHOO_INPUT_READ = 1 << 0,
+ YAHOO_INPUT_WRITE = 1 << 1,
+ YAHOO_INPUT_EXCEPTION = 1 << 2
+} yahoo_input_condition;
+
+/*
+ * A callback function called when an asynchronous connect completes.
+ *
+ * Params:
+ * fd - The file descriptor that has been connected, or -1 on error
+ * error - The value of errno set by the call to connect or 0 if no error
+ * Set both fd and error to 0 if the connect was cancelled by the
+ * user
+ * callback_data - the callback_data passed to the ext_yahoo_connect_async
+ * function
+ */
+typedef void (*yahoo_connect_callback)(int fd, int error, void *callback_data);
+
+
+
+/*
+ * The following functions need to be implemented in the client
+ * interface. They will be called by the library when each
+ * event occurs.
+ */
+
+/*
+ * should we use a callback structure or directly call functions
+ * if you want the structure, you *must* define USE_STRUCT_CALLBACKS
+ * both when you compile the library, and when you compile your code
+ * that uses the library
+ */
+
+#ifdef USE_STRUCT_CALLBACKS
+#define YAHOO_CALLBACK_TYPE(x) (*x)
+struct yahoo_callbacks {
+#else
+#define YAHOO_CALLBACK_TYPE(x) x
+#endif
+
+/*
+ * Name: ext_yahoo_login_response
+ * Called when the login process is complete
+ * Params:
+ * id - the id that identifies the server connection
+ * succ - enum yahoo_login_status
+ * url - url to reactivate account if locked
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, char *url);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_buddies
+ * Called when the contact list is got from the server
+ * Params:
+ * id - the id that identifies the server connection
+ * buds - the buddy list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_ignore
+ * Called when the ignore list is got from the server
+ * Params:
+ * id - the id that identifies the server connection
+ * igns - the ignore list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_identities
+ * Called when the contact list is got from the server
+ * Params:
+ * id - the id that identifies the server connection
+ * ids - the identity list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_cookies
+ * Called when the cookie list is got from the server
+ * Params:
+ * id - the id that identifies the server connection
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id);
+
+
+
+
+/*
+ * Name: ext_yahoo_status_changed
+ * Called when remote user's status changes.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the handle of the remote user
+ * stat - status code (enum yahoo_status)
+ * msg - the message if stat == YAHOO_STATUS_CUSTOM
+ * away - whether the contact is away or not (YAHOO_STATUS_CUSTOM)
+ * for YAHOO_STATUS_IDLE, this is the number of seconds he is idle
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, char *who, int stat, char *msg, int away);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_im
+ * Called when remote user sends you a message.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the handle of the remote user
+ * msg - the message - NULL if stat == 2
+ * tm - timestamp of message if offline
+ * stat - message status - 0
+ * 1
+ * 2 == error sending message
+ * 5
+ * utf8 - whether the message is encoded as utf8 or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, char *who, char *msg, long tm, int stat, int utf8);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_conf_invite
+ * Called when remote user sends you a conference invitation.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user inviting you
+ * room - the room to join
+ * msg - the message
+ * members - the initial members of the conference (null terminated list)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, char *who, char *room, char *msg, YList *members);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userdecline
+ * Called when someone declines to join the conference.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who has declined
+ * room - the room
+ * msg - the declining message
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, char *who, char *room, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userjoin
+ * Called when someone joins the conference.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who has joined
+ * room - the room joined
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, char *who, char *room);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userleave
+ * Called when someone leaves the conference.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who has left
+ * room - the room left
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, char *who, char *room);
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_cat_xml
+ * Called when joining the chatroom.
+ * Params:
+ * id - the id that identifies the server connection
+ * room - the room joined, used in all other chat calls, freed by
+ * library after call
+ * topic - the topic of the room, freed by library after call
+ * members - the initial members of the chatroom (null terminated YList of
+ * yahoo_chat_member's) Must be freed by the client
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, char *xml);
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_join
+ * Called when joining the chatroom.
+ * Params:
+ * id - the id that identifies the server connection
+ * room - the room joined, used in all other chat calls, freed by
+ * library after call
+ * topic - the topic of the room, freed by library after call
+ * members - the initial members of the chatroom (null terminated YList
+ * of yahoo_chat_member's) Must be freed by the client
+ * fd - the socket where the connection is coming from (for tracking)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, char *room, char *topic, YList *members, int fd);
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_userjoin
+ * Called when someone joins the chatroom.
+ * Params:
+ * id - the id that identifies the server connection
+ * room - the room joined
+ * who - the user who has joined, Must be freed by the client
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, char *room, struct yahoo_chat_member *who);
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_userleave
+ * Called when someone leaves the chatroom.
+ * Params:
+ * id - the id that identifies the server connection
+ * room - the room left
+ * who - the user who has left (Just the User ID)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, char *room, char *who);
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_message
+ * Called when someone messages in the chatroom.
+ * Params:
+ * id - the id that identifies the server connection
+ * room - the room
+ * who - the user who messaged (Just the user id)
+ * msg - the message
+ * msgtype - 1 = Normal message
+ * 2 = /me type message
+ * utf8 - whether the message is utf8 encoded or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, char *who, char *room, char *msg, int msgtype, int utf8);
+
+/*
+ *
+ * Name: ext_yahoo_chat_yahoologout
+ * called when yahoo disconnects your chat session
+ * Note this is called whenver a disconnect happens, client or server
+ * requested. Care should be taken to make sure you know the origin
+ * of the disconnect request before doing anything here (auto-join's etc)
+ * Params:
+ * id - the id that identifies this connection
+ * Returns:
+ * nothing.
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id);
+
+/*
+ *
+ * Name: ext_yahoo_chat_yahooerror
+ * called when yahoo sends back an error to you
+ * Note this is called whenver chat message is sent into a room
+ * in error (fd not connected, room doesn't exists etc)
+ * Care should be taken to make sure you know the origin
+ * of the error before doing anything about it.
+ * Params:
+ * id - the id that identifies this connection
+ * Returns:
+ * nothing.
+ */
+
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id);
+
+/*
+ * Name: ext_yahoo_conf_message
+ * Called when someone messages in the conference.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who messaged
+ * room - the room
+ * msg - the message
+ * utf8 - whether the message is utf8 encoded or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, char *who, char *room, char *msg, int utf8);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_file
+ * Called when someone sends you a file
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who sent the file
+ * url - the file url
+ * expires - the expiry date of the file on the server (timestamp)
+ * msg - the message
+ * fname- the file name if direct transfer
+ * fsize- the file size if direct transfer
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize);
+
+
+
+
+/*
+ * Name: ext_yahoo_contact_added
+ * Called when a contact is added to your list
+ * Params:
+ * id - the id that identifies the server connection
+ * myid - the identity he was added to
+ * who - who was added
+ * msg - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, char *myid, char *who, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_rejected
+ * Called when a contact rejects your add
+ * Params:
+ * id - the id that identifies the server connection
+ * who - who rejected you
+ * msg - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, char *who, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_typing_notify
+ * Called when remote user starts or stops typing.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the handle of the remote user
+ * stat - 1 if typing, 0 if stopped typing
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, char *who, int stat);
+
+
+
+
+/*
+ * Name: ext_yahoo_game_notify
+ * Called when remote user starts or stops a game.
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the handle of the remote user
+ * stat - 1 if game, 0 if stopped gaming
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, char *who, int stat);
+
+
+
+
+/*
+ * Name: ext_yahoo_mail_notify
+ * Called when you receive mail, or with number of messages
+ * Params:
+ * id - the id that identifies the server connection
+ * from - who the mail is from - NULL if only mail count
+ * subj - the subject of the mail - NULL if only mail count
+ * cnt - mail count - 0 if new mail notification
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify)(int id, char *from, char *subj, int cnt);
+
+
+
+
+/*
+ * Name: ext_yahoo_system_message
+ * System message
+ * Params:
+ * id - the id that identifies the server connection
+ * msg - the message
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, char *msg);
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_webcam_image
+ * Called when you get a webcam update
+ * An update can either be receiving an image, a part of an image or
+ * just an update with a timestamp
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who's webcam we're viewing
+ * image - image data
+ * image_size - length of the image in bytes
+ * real_size - actual length of image data
+ * timestamp - milliseconds since the webcam started
+ *
+ * If the real_size is smaller then the image_size then only part of
+ * the image has been read. This function will keep being called till
+ * the total amount of bytes in image_size has been read. The image
+ * received is in JPEG-2000 Code Stream Syntax (ISO/IEC 15444-1).
+ * The size of the image will be either 160x120 or 320x240.
+ * Each webcam image contains a timestamp. This timestamp should be
+ * used to keep the image in sync since some images can take longer
+ * to transport then others. When image_size is 0 we can still receive
+ * a timestamp to stay in sync
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who,
+ const unsigned char *image, unsigned int image_size, unsigned int real_size,
+ unsigned int timestamp);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_invite
+ * Called when you get a webcam invitation
+ * Params:
+ * id - the id that identifies the server connection
+ * from - who the invitation is from
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, char *from);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_invite_reply
+ * Called when you get a response to a webcam invitation
+ * Params:
+ * id - the id that identifies the server connection
+ * from - who the invitation response is from
+ * accept - 0 (decline), 1 (accept)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, char *from, int accept);
+
+
+
+/*
+ * Name: ext_yahoo_webcam_closed
+ * Called when the webcam connection closed
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the user who we where connected to
+ * reason - reason why the connection closed
+ * 1 = user stopped broadcasting
+ * 2 = user cancelled viewing permission
+ * 3 = user declines permission
+ * 4 = user does not have webcam online
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, char *who, int reason);
+
+
+/*
+ * Name: ext_yahoo_got_search_result
+ * Called when the search result received from server
+ * Params:
+ * id - the id that identifies the server connection
+ * found - total number of results returned in the current result set
+ * start - offset from where the current result set starts
+ * total - total number of results available (start + found <= total)
+ * contacts - the list of results as a YList of yahoo_found_contact
+ * these will be freed after this function returns, so
+ * if you need to use the information, make a copy
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int start, int total, YList *contacts);
+
+
+
+/*
+ * Name: ext_yahoo_error
+ * Called on error.
+ * Params:
+ * id - the id that identifies the server connection
+ * err - the error message
+ * fatal- whether this error is fatal to the connection or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, char *err, int fatal);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_viewer
+ * Called when a viewer disconnects/connects/requests to connect
+ * Params:
+ * id - the id that identifies the server connection
+ * who - the viewer
+ * connect - 0=disconnect 1=connect 2=request
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, char *who, int connect);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_data_request
+ * Called when you get a request for webcam images
+ * Params:
+ * id - the id that identifies the server connection
+ * send - whether to send images or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send);
+
+
+
+
+/*
+ * Name: ext_yahoo_log
+ * Called to log a message.
+ * Params:
+ * fmt - the printf formatted message
+ * Returns:
+ * 0
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(char *fmt, ...);
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_add_handler
+ * Add a listener for the fd. Must call yahoo_read_ready
+ * when a YAHOO_INPUT_READ fd is ready and yahoo_write_ready
+ * when a YAHOO_INPUT_WRITE fd is ready.
+ * Params:
+ * id - the id that identifies the server connection
+ * fd - the fd on which to listen
+ * cond - the condition on which to call the callback
+ * data - callback data to pass to yahoo_*_ready
+ *
+ * Returns: a tag to be used when removing the handler
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condition cond, void *data);
+
+
+
+
+/*
+ * Name: ext_yahoo_remove_handler
+ * Remove the listener for the fd.
+ * Params:
+ * id - the id that identifies the connection
+ * tag - the handler tag to remove
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_connect
+ * Connect to a host:port
+ * Params:
+ * host - the host to connect to
+ * port - the port to connect on
+ * Returns:
+ * a unix file descriptor to the socket
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(char *host, int port);
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_connect_async
+ * Connect to a host:port asynchronously. This function should return
+ * immediately returing a tag used to identify the connection handler,
+ * or a pre-connect error (eg: host name lookup failure).
+ * Once the connect completes (successfully or unsuccessfully), callback
+ * should be called (see the signature for yahoo_connect_callback).
+ * The callback may safely be called before this function returns, but
+ * it should not be called twice.
+ * Params:
+ * id - the id that identifies this connection
+ * host - the host to connect to
+ * port - the port to connect on
+ * callback - function to call when connect completes
+ * callback_data - data to pass to the callback function
+ * Returns:
+ * a unix file descriptor to the socket
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, char *host, int port,
+ yahoo_connect_callback callback, void *callback_data);
+
+#ifdef USE_STRUCT_CALLBACKS
+};
+
+/*
+ * if using a callback structure, call yahoo_register_callbacks
+ * before doing anything else
+ */
+void yahoo_register_callbacks(struct yahoo_callbacks * tyc);
+
+#undef YAHOO_CALLBACK_TYPE
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h
new file mode 100644
index 00000000..1a92b267
--- /dev/null
+++ b/protocols/yahoo/yahoo2_types.h
@@ -0,0 +1,241 @@
+/*
+ * libyahoo2: yahoo2_types.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef YAHOO2_TYPES_H
+#define YAHOO2_TYPES_H
+
+#include "yahoo_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum yahoo_status {
+ YAHOO_STATUS_AVAILABLE = 0,
+ YAHOO_STATUS_BRB,
+ YAHOO_STATUS_BUSY,
+ YAHOO_STATUS_NOTATHOME,
+ YAHOO_STATUS_NOTATDESK,
+ YAHOO_STATUS_NOTINOFFICE,
+ YAHOO_STATUS_ONPHONE,
+ YAHOO_STATUS_ONVACATION,
+ YAHOO_STATUS_OUTTOLUNCH,
+ YAHOO_STATUS_STEPPEDOUT,
+ YAHOO_STATUS_INVISIBLE = 12,
+ YAHOO_STATUS_CUSTOM = 99,
+ YAHOO_STATUS_IDLE = 999,
+ YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
+ YAHOO_STATUS_NOTIFY = 0x16
+};
+#define YAHOO_STATUS_GAME 0x2 /* Games don't fit into the regular status model */
+
+enum yahoo_login_status {
+ YAHOO_LOGIN_OK = 0,
+ YAHOO_LOGIN_UNAME = 3,
+ YAHOO_LOGIN_PASSWD = 13,
+ YAHOO_LOGIN_LOCK = 14,
+ YAHOO_LOGIN_DUPL = 99,
+ YAHOO_LOGIN_SOCK = -1
+};
+
+enum yahoo_error {
+ E_CUSTOM = 0,
+
+ /* responses from ignore buddy */
+ E_IGNOREDUP = 2,
+ E_IGNORENONE = 3,
+ E_IGNORECONF = 12,
+
+ /* conference */
+ E_CONFNOTAVAIL = 20
+};
+
+enum yahoo_log_level {
+ YAHOO_LOG_NONE = 0,
+ YAHOO_LOG_FATAL,
+ YAHOO_LOG_ERR,
+ YAHOO_LOG_WARNING,
+ YAHOO_LOG_NOTICE,
+ YAHOO_LOG_INFO,
+ YAHOO_LOG_DEBUG
+};
+
+
+/* Yahoo style/color directives */
+#define YAHOO_COLOR_BLACK "\033[30m"
+#define YAHOO_COLOR_BLUE "\033[31m"
+#define YAHOO_COLOR_LIGHTBLUE "\033[32m"
+#define YAHOO_COLOR_GRAY "\033[33m"
+#define YAHOO_COLOR_GREEN "\033[34m"
+#define YAHOO_COLOR_PINK "\033[35m"
+#define YAHOO_COLOR_PURPLE "\033[36m"
+#define YAHOO_COLOR_ORANGE "\033[37m"
+#define YAHOO_COLOR_RED "\033[38m"
+#define YAHOO_COLOR_OLIVE "\033[39m"
+#define YAHOO_COLOR_ANY "\033[#"
+#define YAHOO_STYLE_ITALICON "\033[2m"
+#define YAHOO_STYLE_ITALICOFF "\033[x2m"
+#define YAHOO_STYLE_BOLDON "\033[1m"
+#define YAHOO_STYLE_BOLDOFF "\033[x1m"
+#define YAHOO_STYLE_UNDERLINEON "\033[4m"
+#define YAHOO_STYLE_UNDERLINEOFF "\033[x4m"
+#define YAHOO_STYLE_URLON "\033[lm"
+#define YAHOO_STYLE_URLOFF "\033[xlm"
+
+enum yahoo_connection_type {
+ YAHOO_CONNECTION_PAGER=0,
+ YAHOO_CONNECTION_FT,
+ YAHOO_CONNECTION_YAB,
+ YAHOO_CONNECTION_WEBCAM_MASTER,
+ YAHOO_CONNECTION_WEBCAM,
+ YAHOO_CONNECTION_CHATCAT,
+ YAHOO_CONNECTION_SEARCH
+};
+
+enum yahoo_webcam_direction_type {
+ YAHOO_WEBCAM_DOWNLOAD=0,
+ YAHOO_WEBCAM_UPLOAD
+};
+
+/* chat member attribs */
+#define YAHOO_CHAT_MALE 0x8000
+#define YAHOO_CHAT_FEMALE 0x10000
+#define YAHOO_CHAT_FEMALE 0x10000
+#define YAHOO_CHAT_DUNNO 0x400
+#define YAHOO_CHAT_WEBCAM 0x10
+
+enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 };
+
+struct yahoo_webcam {
+ int direction; /* Uploading or downloading */
+ int conn_type; /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */
+
+ char *user; /* user we are viewing */
+ char *server; /* webcam server to connect to */
+ int port; /* webcam port to connect on */
+ char *key; /* key to connect to the server with */
+ char *description; /* webcam description */
+ char *my_ip; /* own ip number */
+};
+
+struct yahoo_webcam_data {
+ unsigned int data_size;
+ unsigned int to_read;
+ unsigned int timestamp;
+ unsigned char packet_type;
+};
+
+struct yahoo_data {
+ char *user;
+ char *password;
+
+ char *cookie_y;
+ char *cookie_t;
+ char *cookie_c;
+ char *login_cookie;
+
+ YList *buddies;
+ YList *ignore;
+ YList *identities;
+ char *login_id;
+
+ int current_status;
+ int initial_status;
+ int logged_in;
+
+ int session_id;
+
+ int client_id;
+
+ char *rawbuddylist;
+ char *ignorelist;
+
+ void *server_settings;
+};
+
+struct yab {
+ char *id;
+ char *fname;
+ char *lname;
+ char *nname;
+ char *email;
+ char *hphone;
+ char *wphone;
+ char *mphone;
+ int dbid;
+};
+
+struct yahoo_buddy {
+ char *group;
+ char *id;
+ char *real_name;
+ struct yab *yab_entry;
+};
+
+enum yahoo_search_type {
+ YAHOO_SEARCH_KEYWORD = 0,
+ YAHOO_SEARCH_YID,
+ YAHOO_SEARCH_NAME
+};
+
+enum yahoo_search_gender {
+ YAHOO_GENDER_NONE = 0,
+ YAHOO_GENDER_MALE,
+ YAHOO_GENDER_FEMALE
+};
+
+enum yahoo_search_agerange {
+ YAHOO_AGERANGE_NONE = 0
+};
+
+struct yahoo_found_contact {
+ char *id;
+ char *gender;
+ char *location;
+ int age;
+ int online;
+};
+
+/*
+ * Function pointer to be passed to http get/post and send file
+ */
+typedef void (*yahoo_get_fd_callback)(int id, int fd, int error, void *data);
+
+/*
+ * Function pointer to be passed to yahoo_get_url_handle
+ */
+typedef void (*yahoo_get_url_handle_callback)(int id, int fd, int error,
+ const char *filename, unsigned long size, void *data);
+
+
+struct yahoo_chat_member {
+ char *id;
+ int age;
+ int attribs;
+ char *alias;
+ char *location;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/protocols/yahoo/yahoo_debug.h b/protocols/yahoo/yahoo_debug.h
new file mode 100644
index 00000000..59d92901
--- /dev/null
+++ b/protocols/yahoo/yahoo_debug.h
@@ -0,0 +1,38 @@
+/*
+ * libyahoo2: yahoo_debug.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+extern int yahoo_log_message(char *fmt, ...);
+
+#define NOTICE(x) if(yahoo_get_log_level() >= YAHOO_LOG_NOTICE) { yahoo_log_message x; yahoo_log_message("\n"); }
+
+#define LOG(x) if(yahoo_get_log_level() >= YAHOO_LOG_INFO) { yahoo_log_message("%s:%d: ", __FILE__, __LINE__); \
+ yahoo_log_message x; \
+ yahoo_log_message("\n"); }
+
+#define WARNING(x) if(yahoo_get_log_level() >= YAHOO_LOG_WARNING) { yahoo_log_message("%s:%d: warning: ", __FILE__, __LINE__); \
+ yahoo_log_message x; \
+ yahoo_log_message("\n"); }
+
+#define DEBUG_MSG(x) if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { yahoo_log_message("%s:%d: debug: ", __FILE__, __LINE__); \
+ yahoo_log_message x; \
+ yahoo_log_message("\n"); }
+
+
diff --git a/protocols/yahoo/yahoo_fn.c b/protocols/yahoo/yahoo_fn.c
new file mode 100644
index 00000000..6f14c263
--- /dev/null
+++ b/protocols/yahoo/yahoo_fn.c
@@ -0,0 +1,4622 @@
+/*
+ * libyahoo2 - originally from gaim patches by Amatus
+ *
+ * Copyright (C) 2003-2004
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "yahoo_fn.h"
+
+unsigned char table_0[256] = {
+ 0x5A, 0x41, 0x11, 0x77, 0x29, 0x9C, 0x31, 0xAD,
+ 0x4A, 0x32, 0x1A, 0x6D, 0x56, 0x9F, 0x39, 0xA6,
+ 0x0C, 0xE8, 0x49, 0x40, 0xA4, 0x21, 0xE9, 0x01,
+ 0x91, 0x86, 0x2F, 0xB9, 0xED, 0x80, 0x51, 0xAB,
+ 0x7F, 0x92, 0xF2, 0x73, 0xCD, 0xD9, 0x75, 0x2A,
+ 0x70, 0x34, 0x35, 0x8D, 0xA8, 0x72, 0x7D, 0x9B,
+ 0x2E, 0xC5, 0x2D, 0x76, 0x1E, 0xBB, 0xE7, 0x37,
+ 0xBA, 0xB7, 0xB2, 0x03, 0x20, 0x17, 0x8A, 0x07,
+ 0xD6, 0x96, 0x13, 0x95, 0xE5, 0xF1, 0x18, 0x3B,
+ 0xA5, 0x62, 0x33, 0xC1, 0x44, 0x3D, 0x6C, 0xA7,
+ 0xBF, 0x1C, 0x60, 0xFF, 0x5B, 0xF5, 0x8E, 0xE6,
+ 0x5C, 0xCC, 0xF7, 0x69, 0x15, 0x0F, 0x0B, 0xBD,
+ 0x12, 0x9D, 0xB3, 0x65, 0x53, 0xB1, 0x14, 0xF4,
+ 0x19, 0x3E, 0xB6, 0x45, 0xCB, 0xA2, 0x7A, 0xD3,
+ 0xF8, 0xD1, 0x61, 0xEE, 0xBC, 0xC6, 0xB0, 0x5D,
+ 0x4B, 0x09, 0x26, 0xE1, 0x1D, 0x6E, 0xC3, 0xFB,
+ 0x68, 0x4C, 0x42, 0x52, 0x5F, 0xDE, 0xFD, 0xEF,
+ 0x81, 0x04, 0x6F, 0xE0, 0xF0, 0x1F, 0x0D, 0x7C,
+ 0x58, 0x4F, 0x1B, 0x30, 0xCF, 0x9A, 0x2B, 0x05,
+ 0xF6, 0x3F, 0x78, 0xAC, 0xD8, 0xEC, 0xE2, 0x25,
+ 0x93, 0xDA, 0x84, 0x8C, 0x4E, 0xD5, 0x38, 0x0A,
+ 0x06, 0x7E, 0xD4, 0x59, 0x98, 0xE3, 0x36, 0xC2,
+ 0xD2, 0xA3, 0x10, 0x79, 0xFA, 0xC9, 0x16, 0x27,
+ 0x66, 0x89, 0xFE, 0x57, 0xF3, 0x83, 0xB8, 0x28,
+ 0x3C, 0xC7, 0xCE, 0x71, 0xC8, 0xDB, 0x22, 0xE4,
+ 0xDD, 0xDF, 0x02, 0x8F, 0x5E, 0xEB, 0x48, 0x2C,
+ 0x08, 0xC4, 0x43, 0xEA, 0x50, 0x55, 0x90, 0x54,
+ 0x87, 0xCA, 0x00, 0x24, 0x6B, 0x85, 0x97, 0xD7,
+ 0xDC, 0x6A, 0x67, 0xD0, 0x88, 0xA1, 0x9E, 0xC0,
+ 0x46, 0xAE, 0x64, 0x74, 0x4D, 0xA0, 0x99, 0xB5,
+ 0x0E, 0x8B, 0xAA, 0x3A, 0xB4, 0xFC, 0xA9, 0x94,
+ 0x7B, 0xBE, 0xF9, 0xAF, 0x82, 0x63, 0x47, 0x23 };
+
+unsigned char table_1[256] = {
+ 0x08, 0xCB, 0x54, 0xCF, 0x97, 0x53, 0x59, 0xF1,
+ 0x66, 0xEC, 0xDB, 0x1B, 0xB1, 0xE2, 0x36, 0xEB,
+ 0xB3, 0x8F, 0x71, 0xA8, 0x90, 0x7D, 0xDA, 0xDC,
+ 0x2C, 0x2F, 0xE8, 0x6A, 0x73, 0x37, 0xAE, 0xCC,
+ 0xA1, 0x16, 0xE6, 0xFC, 0x9C, 0xA9, 0x2A, 0x3F,
+ 0x58, 0xFD, 0x56, 0x4C, 0xA5, 0xF2, 0x33, 0x99,
+ 0x1A, 0xB7, 0xFE, 0xA6, 0x1E, 0x32, 0x9E, 0x48,
+ 0x03, 0x4A, 0x78, 0xEE, 0xCA, 0xC3, 0x88, 0x7A,
+ 0xAC, 0x23, 0xAA, 0xBD, 0xDE, 0xD3, 0x67, 0x43,
+ 0xFF, 0x64, 0x8A, 0xF9, 0x04, 0xD0, 0x7B, 0xC2,
+ 0xBC, 0xF3, 0x89, 0x0E, 0xDD, 0xAB, 0x9D, 0x84,
+ 0x5A, 0x62, 0x7F, 0x6D, 0x82, 0x68, 0xA3, 0xED,
+ 0x2E, 0x07, 0x41, 0xEF, 0x2D, 0x70, 0x4F, 0x69,
+ 0x8E, 0xE7, 0x0F, 0x11, 0x19, 0xAF, 0x31, 0xFB,
+ 0x8D, 0x4B, 0x5F, 0x96, 0x75, 0x42, 0x6C, 0x46,
+ 0xE4, 0x55, 0xD6, 0x3B, 0xE1, 0xD1, 0xB0, 0xB5,
+ 0x45, 0x29, 0xC0, 0x94, 0x9F, 0xD4, 0x15, 0x17,
+ 0x3C, 0x47, 0xC8, 0xD9, 0xC6, 0x76, 0xB9, 0x02,
+ 0xE0, 0xC9, 0xB2, 0x01, 0xC1, 0x5D, 0x4E, 0x14,
+ 0xF4, 0xAD, 0xB6, 0x00, 0x72, 0xF0, 0x49, 0x0D,
+ 0xD8, 0x5E, 0x6F, 0x2B, 0x8C, 0x51, 0x83, 0xC5,
+ 0x0A, 0x85, 0xE5, 0x38, 0x7E, 0x26, 0xEA, 0x22,
+ 0x6B, 0x06, 0xD5, 0x8B, 0xBF, 0xC7, 0x35, 0x1D,
+ 0xF6, 0x24, 0x28, 0xCE, 0x9B, 0x77, 0x20, 0x60,
+ 0xF5, 0x87, 0x3D, 0x65, 0x86, 0x0C, 0xDF, 0xBA,
+ 0x12, 0xA4, 0x3A, 0x34, 0xD7, 0xA0, 0xF8, 0x63,
+ 0x52, 0x27, 0xB8, 0x18, 0xA7, 0x13, 0x91, 0x09,
+ 0x93, 0x5C, 0x10, 0x9A, 0xB4, 0xE9, 0x44, 0xC4,
+ 0x21, 0x57, 0x1C, 0x0B, 0xA2, 0x74, 0x4D, 0xBE,
+ 0xD2, 0x1F, 0xCD, 0xE3, 0x6E, 0x7C, 0x40, 0x50,
+ 0x39, 0x80, 0x98, 0xFA, 0x25, 0x92, 0x30, 0x5B,
+ 0x05, 0x95, 0xBB, 0x79, 0x61, 0x3E, 0x81, 0xF7 };
+
+unsigned char table_2[32] = {
+ 0x19, 0x05, 0x09, 0x1C, 0x0B, 0x1A, 0x12, 0x03,
+ 0x06, 0x04, 0x0D, 0x1D, 0x15, 0x0E, 0x1B, 0x18,
+ 0x00, 0x07, 0x08, 0x02, 0x13, 0x1F, 0x0C, 0x1E,
+ 0x16, 0x0A, 0x10, 0x0F, 0x01, 0x14, 0x11, 0x17 };
+
+unsigned char table_3[256] = {
+ 0xBC, 0x1B, 0xCC, 0x1E, 0x5B, 0x59, 0x4F, 0xA8,
+ 0x62, 0xC6, 0xC1, 0xBB, 0x83, 0x2D, 0xA3, 0xA6,
+ 0x5A, 0xDC, 0xE5, 0x93, 0xFB, 0x5C, 0xD6, 0x2A,
+ 0x97, 0xC7, 0x1C, 0x73, 0x08, 0x45, 0xD2, 0x89,
+ 0x4A, 0xD4, 0xCF, 0x0C, 0x1D, 0xD8, 0xCD, 0x26,
+ 0x8F, 0x11, 0x55, 0x8B, 0xD3, 0x53, 0xCE, 0x00,
+ 0xB5, 0x3B, 0x2E, 0x39, 0x88, 0x7B, 0x85, 0x46,
+ 0x54, 0xA5, 0x31, 0x40, 0x3E, 0x0A, 0x4C, 0x68,
+ 0x70, 0x0F, 0xBA, 0x0E, 0x75, 0x8A, 0xEB, 0x44,
+ 0x60, 0x6C, 0x05, 0xC9, 0xF0, 0xDD, 0x0D, 0x66,
+ 0xAB, 0xA1, 0xAD, 0xF2, 0x12, 0x6A, 0xE6, 0x27,
+ 0xF6, 0x9F, 0xDB, 0xB8, 0xF4, 0x56, 0x5E, 0x2C,
+ 0xDA, 0xFE, 0x34, 0x86, 0xF5, 0xC2, 0xB0, 0xF1,
+ 0xCB, 0xF3, 0x78, 0x9B, 0x7F, 0xB4, 0xD7, 0x58,
+ 0x74, 0x07, 0x72, 0x96, 0x02, 0xCA, 0xAC, 0xE8,
+ 0x5D, 0xA7, 0x32, 0xBD, 0x81, 0x43, 0x18, 0xF8,
+ 0x15, 0x0B, 0xE9, 0x76, 0x30, 0xBF, 0x3A, 0x22,
+ 0x9E, 0xD1, 0x79, 0x37, 0xBE, 0x8C, 0x7A, 0x98,
+ 0x21, 0x95, 0x10, 0x8D, 0xDF, 0xC0, 0x69, 0xC8,
+ 0x03, 0x6E, 0x4B, 0x36, 0xFC, 0x6F, 0xA9, 0x48,
+ 0x63, 0xE1, 0xB9, 0x24, 0x87, 0x13, 0xB2, 0xA4,
+ 0x84, 0x06, 0x14, 0x61, 0x3D, 0x92, 0xB1, 0x41,
+ 0xE2, 0x71, 0xAF, 0x16, 0xDE, 0x25, 0x82, 0xD9,
+ 0x2B, 0x33, 0x51, 0xA2, 0x4E, 0x7D, 0x94, 0xFF,
+ 0xFD, 0x5F, 0x80, 0xED, 0x64, 0xE7, 0x50, 0x6D,
+ 0xD0, 0x3C, 0x6B, 0x65, 0x77, 0x17, 0x1A, 0xEC,
+ 0xD5, 0xAA, 0xF9, 0xC4, 0x9C, 0x35, 0xE3, 0x42,
+ 0xE4, 0x19, 0x52, 0x67, 0xB7, 0x9D, 0x28, 0xC5,
+ 0x47, 0x38, 0x91, 0x57, 0xAE, 0x3F, 0x29, 0x9A,
+ 0x2F, 0xF7, 0x90, 0x04, 0xEE, 0xFA, 0x20, 0xB6,
+ 0xEA, 0x49, 0x23, 0x4D, 0xB3, 0x8E, 0xC3, 0x1F,
+ 0x7C, 0xEF, 0xE0, 0x99, 0x09, 0xA0, 0x01, 0x7E };
+
+unsigned char table_4[32] = {
+ 0x1F, 0x0B, 0x00, 0x1E, 0x03, 0x0E, 0x15, 0x01,
+ 0x1A, 0x17, 0x1D, 0x1B, 0x11, 0x0F, 0x0A, 0x12,
+ 0x13, 0x18, 0x02, 0x04, 0x09, 0x06, 0x0D, 0x07,
+ 0x08, 0x05, 0x10, 0x19, 0x0C, 0x14, 0x16, 0x1C };
+
+unsigned char table_5[256] = {
+ 0x9A, 0xAB, 0x61, 0x28, 0x0A, 0x23, 0xFC, 0xBA,
+ 0x90, 0x22, 0xB7, 0x62, 0xD9, 0x09, 0x91, 0xF4,
+ 0x7B, 0x5D, 0x6B, 0x80, 0xAC, 0x9E, 0x21, 0x72,
+ 0x64, 0x2D, 0xFF, 0x66, 0xEB, 0x5B, 0x05, 0xC8,
+ 0x1B, 0xD1, 0x55, 0xF5, 0x97, 0x08, 0xAE, 0xC7,
+ 0x00, 0xDE, 0xE1, 0x78, 0xD8, 0xB6, 0xF0, 0x17,
+ 0xE4, 0x32, 0xCD, 0x76, 0x07, 0x14, 0x7F, 0x7A,
+ 0xBF, 0xB4, 0x1D, 0x94, 0x48, 0x75, 0xFA, 0xA7,
+ 0x99, 0x7E, 0x65, 0x38, 0x29, 0x51, 0xC3, 0x83,
+ 0x7C, 0x0D, 0xA0, 0xCC, 0xF1, 0xDD, 0xE2, 0x49,
+ 0xF8, 0xD2, 0x25, 0x54, 0x9B, 0x0E, 0xB9, 0xFE,
+ 0x67, 0xC4, 0xCE, 0x13, 0xD4, 0xE7, 0xB8, 0x41,
+ 0x77, 0xDB, 0xA6, 0xB0, 0x11, 0x6A, 0x5E, 0x68,
+ 0x8D, 0xF9, 0x36, 0xD3, 0xC2, 0x3A, 0xAA, 0x59,
+ 0x03, 0xE0, 0xE3, 0xF3, 0x42, 0x2C, 0x04, 0x47,
+ 0xE6, 0x93, 0xCB, 0x6E, 0x20, 0xCA, 0x01, 0xA1,
+ 0x40, 0x2B, 0x2F, 0x5F, 0x87, 0xD0, 0xEC, 0x88,
+ 0x27, 0x58, 0xC6, 0x3E, 0xDF, 0x26, 0x5C, 0xE9,
+ 0x1F, 0x0F, 0x95, 0x1C, 0xFB, 0xA5, 0x12, 0x39,
+ 0x1E, 0x3C, 0x33, 0x43, 0x56, 0xE8, 0x82, 0xF7,
+ 0x7D, 0x89, 0xF2, 0xD7, 0x50, 0x92, 0x60, 0x4C,
+ 0x2A, 0x86, 0x16, 0x6C, 0x37, 0xC0, 0xAD, 0xB3,
+ 0x24, 0x45, 0xB1, 0xA2, 0x71, 0xA4, 0xA3, 0xED,
+ 0xC9, 0x5A, 0x4D, 0x84, 0x0C, 0x3F, 0xC5, 0x9D,
+ 0x63, 0x19, 0x79, 0x57, 0x96, 0x30, 0x74, 0xBB,
+ 0xDA, 0x1A, 0x9F, 0x44, 0xC1, 0x98, 0xE5, 0x81,
+ 0xD6, 0x18, 0x8F, 0xFD, 0x8E, 0x06, 0x6F, 0xF6,
+ 0x2E, 0x3B, 0xB5, 0x85, 0x8A, 0x9C, 0x53, 0x4A,
+ 0xA9, 0x52, 0x3D, 0x4E, 0xBE, 0xAF, 0xBC, 0xA8,
+ 0x4F, 0x6D, 0x15, 0x35, 0x8C, 0xBD, 0x34, 0x8B,
+ 0xDC, 0x0B, 0xCF, 0x31, 0xEA, 0xB2, 0x70, 0x4B,
+ 0x46, 0x73, 0x69, 0xD5, 0x10, 0xEE, 0x02, 0xEF };
+
+unsigned char table_6[32] = {
+ 0x1A, 0x1C, 0x0F, 0x0C, 0x00, 0x02, 0x13, 0x09,
+ 0x11, 0x05, 0x0D, 0x12, 0x18, 0x0B, 0x04, 0x10,
+ 0x14, 0x1B, 0x1E, 0x16, 0x07, 0x08, 0x03, 0x17,
+ 0x19, 0x1F, 0x01, 0x0E, 0x15, 0x06, 0x0A, 0x1D };
+
+unsigned char table_7[256] = {
+ 0x52, 0x11, 0x72, 0xD0, 0x76, 0xD7, 0xAE, 0x03,
+ 0x7F, 0x19, 0xF4, 0xB8, 0xB3, 0x5D, 0xCA, 0x2D,
+ 0x5C, 0x30, 0x53, 0x1A, 0x57, 0xF6, 0xAD, 0x83,
+ 0x29, 0x79, 0xD5, 0xF0, 0x0F, 0xC3, 0x8B, 0xD3,
+ 0x8E, 0x37, 0x01, 0xA6, 0xF1, 0x10, 0x04, 0x71,
+ 0xCC, 0xC6, 0xE7, 0xC2, 0x85, 0x94, 0xBD, 0x6F,
+ 0xCB, 0xEA, 0xFC, 0xA1, 0x38, 0x5E, 0x08, 0x2E,
+ 0x35, 0x42, 0x67, 0xD4, 0x56, 0x6D, 0x7C, 0xE5,
+ 0x0E, 0x7D, 0x12, 0x65, 0xF5, 0x33, 0x82, 0xC4,
+ 0x1D, 0xD2, 0x16, 0x58, 0xEC, 0xCD, 0xA8, 0xBF,
+ 0xAB, 0x07, 0x45, 0x55, 0xB7, 0x6A, 0x70, 0xF2,
+ 0xBE, 0x05, 0x6B, 0x9D, 0xEB, 0x13, 0x0D, 0x9F,
+ 0xE8, 0xA7, 0xC8, 0x31, 0x3C, 0xB6, 0x21, 0xC0,
+ 0x20, 0x60, 0x6C, 0xE2, 0xCE, 0x8C, 0xFD, 0x95,
+ 0xE3, 0x4A, 0xB5, 0xB2, 0x40, 0xB1, 0xF3, 0x17,
+ 0xF9, 0x24, 0x06, 0x22, 0x2F, 0x25, 0x93, 0x8A,
+ 0x2A, 0x7E, 0x28, 0x3D, 0x47, 0xF8, 0x89, 0xA5,
+ 0x7B, 0x9B, 0xC5, 0x84, 0x59, 0x46, 0x90, 0x74,
+ 0x69, 0xC7, 0xAA, 0xEE, 0x6E, 0xD6, 0xB0, 0x18,
+ 0x66, 0xA0, 0x7A, 0x1E, 0xFB, 0xDB, 0x4E, 0x51,
+ 0x92, 0xE4, 0xE0, 0x3E, 0xB4, 0xD8, 0x23, 0x3B,
+ 0xC1, 0x5F, 0xFE, 0x98, 0x99, 0x73, 0x09, 0xA9,
+ 0xA3, 0xDF, 0x14, 0x5A, 0x26, 0x8F, 0x0B, 0xAF,
+ 0x4C, 0x97, 0x54, 0xE1, 0x63, 0x48, 0xED, 0xBA,
+ 0xCF, 0xBB, 0x1F, 0xDC, 0xA4, 0xFA, 0x64, 0x75,
+ 0xDE, 0x81, 0x9A, 0xFF, 0x49, 0x41, 0x27, 0x62,
+ 0x02, 0x15, 0xD9, 0x86, 0xAC, 0x3F, 0x0C, 0x61,
+ 0xD1, 0x77, 0x2B, 0x1B, 0x96, 0xDA, 0x68, 0x1C,
+ 0x44, 0x32, 0xBC, 0xA2, 0x87, 0xF7, 0x91, 0x8D,
+ 0x80, 0xDD, 0x0A, 0x50, 0x34, 0x4B, 0x00, 0xB9,
+ 0x36, 0xE6, 0x78, 0x4F, 0xC9, 0xE9, 0x2C, 0x43,
+ 0x88, 0x9E, 0x9C, 0x5B, 0x4D, 0x3A, 0x39, 0xEF };
+
+unsigned char table_8[32] = {
+ 0x13, 0x08, 0x1E, 0x1D, 0x17, 0x16, 0x07, 0x1F,
+ 0x0E, 0x03, 0x1A, 0x19, 0x01, 0x12, 0x11, 0x10,
+ 0x09, 0x0C, 0x0F, 0x14, 0x0B, 0x05, 0x00, 0x04,
+ 0x1C, 0x18, 0x0A, 0x15, 0x02, 0x1B, 0x06, 0x0D };
+
+unsigned char table_9[256] = {
+ 0x20, 0x2A, 0xDA, 0xFE, 0x76, 0x0D, 0xED, 0x39,
+ 0x51, 0x4C, 0x46, 0x9A, 0xF1, 0xB0, 0x10, 0xC7,
+ 0xD1, 0x6F, 0x18, 0x24, 0xB9, 0x7A, 0x4F, 0x47,
+ 0xE0, 0x4E, 0x88, 0x09, 0x8A, 0xBA, 0x60, 0xBD,
+ 0xC2, 0x27, 0x93, 0x7D, 0x94, 0x40, 0xCB, 0x80,
+ 0xB8, 0x41, 0x84, 0x5D, 0xC1, 0x0F, 0x5E, 0x78,
+ 0x2B, 0x48, 0x28, 0x29, 0xEE, 0x81, 0x90, 0x86,
+ 0x50, 0x9C, 0xF3, 0xB2, 0x35, 0x52, 0x0C, 0x9D,
+ 0xFC, 0x69, 0xD6, 0xA6, 0x06, 0xD7, 0xC6, 0xFF,
+ 0x1C, 0x14, 0x57, 0x33, 0xE2, 0x1F, 0x83, 0xA8,
+ 0xF7, 0x99, 0xC5, 0xDC, 0x70, 0x9E, 0xF4, 0x6B,
+ 0x0A, 0x77, 0x95, 0x4A, 0x2E, 0x53, 0xF2, 0x62,
+ 0x98, 0xF8, 0x96, 0xDB, 0xE6, 0x32, 0x3C, 0x58,
+ 0xD5, 0x6D, 0xE7, 0x4B, 0xCE, 0x91, 0x43, 0xD8,
+ 0xFA, 0xE3, 0x4D, 0xD9, 0x68, 0xDE, 0xEC, 0x01,
+ 0x08, 0xD3, 0x8F, 0x19, 0xC4, 0xA7, 0x6E, 0x3E,
+ 0x63, 0x12, 0x72, 0x42, 0x9F, 0xB4, 0x04, 0x1B,
+ 0x7E, 0x11, 0x17, 0x73, 0xB5, 0x22, 0x56, 0xA1,
+ 0x89, 0xDD, 0xF5, 0x3F, 0x49, 0x26, 0x8D, 0x15,
+ 0x85, 0x75, 0x5F, 0x65, 0x82, 0xB6, 0xF6, 0xD2,
+ 0xA4, 0x55, 0x37, 0xC8, 0xA0, 0xCC, 0x66, 0x5C,
+ 0xC9, 0x25, 0x36, 0x67, 0x7C, 0xE1, 0xA3, 0xCF,
+ 0xA9, 0x59, 0x2F, 0xFB, 0xBB, 0x07, 0x87, 0xA2,
+ 0x44, 0x92, 0x13, 0x00, 0x16, 0x61, 0x38, 0xEB,
+ 0xAE, 0xD4, 0x1E, 0x64, 0x6A, 0xE4, 0xCA, 0x1D,
+ 0x6C, 0xDF, 0xAB, 0x5B, 0x03, 0x7B, 0x9B, 0x8C,
+ 0x5A, 0xFD, 0xC3, 0xB3, 0x0B, 0xAA, 0xAC, 0x8B,
+ 0xBE, 0xBC, 0x3D, 0x97, 0xCD, 0x05, 0x21, 0x8E,
+ 0xAD, 0xEA, 0x54, 0x30, 0xAF, 0x02, 0xB1, 0x34,
+ 0x0E, 0xA5, 0x3B, 0x45, 0x1A, 0x23, 0xE8, 0x7F,
+ 0xEF, 0xB7, 0x31, 0xD0, 0xBF, 0x3A, 0x79, 0xE5,
+ 0xF9, 0xF0, 0x2C, 0x74, 0xE9, 0x71, 0xC0, 0x2D };
+
+unsigned char table_10[32] = {
+ 0x1D, 0x12, 0x11, 0x0D, 0x1E, 0x19, 0x16, 0x1B,
+ 0x18, 0x13, 0x07, 0x17, 0x0C, 0x02, 0x00, 0x15,
+ 0x0E, 0x08, 0x05, 0x01, 0x10, 0x06, 0x04, 0x0F,
+ 0x1F, 0x1A, 0x0B, 0x09, 0x0A, 0x14, 0x1C, 0x03 };
+
+unsigned char table_11[256] = {
+ 0x6B, 0x1D, 0xC6, 0x0A, 0xB7, 0xAC, 0xB2, 0x11,
+ 0x29, 0xD3, 0xA2, 0x4D, 0xCB, 0x03, 0xEF, 0xA6,
+ 0xC1, 0x5D, 0x75, 0x48, 0x35, 0x6C, 0xE2, 0x84,
+ 0xAB, 0xAA, 0xD8, 0x2C, 0x0E, 0x95, 0x25, 0x27,
+ 0x7D, 0x0B, 0xD0, 0xFB, 0x14, 0xE5, 0xF2, 0x4E,
+ 0x7F, 0x2A, 0x63, 0x3C, 0xC9, 0xF6, 0xDC, 0x07,
+ 0x26, 0x55, 0xCF, 0x2B, 0xCD, 0xA7, 0x17, 0xD2,
+ 0x9A, 0x7B, 0x93, 0x78, 0x9E, 0xE6, 0x2F, 0x49,
+ 0x1E, 0xFD, 0xF0, 0xFE, 0x7C, 0x33, 0x92, 0xA3,
+ 0xC8, 0xA0, 0xA9, 0xC4, 0xA1, 0x94, 0x6D, 0x44,
+ 0x0C, 0x90, 0x3A, 0x8C, 0x8E, 0x85, 0xAF, 0x40,
+ 0x36, 0xA4, 0xD1, 0xB9, 0x19, 0x6F, 0xF4, 0xBA,
+ 0x1A, 0x73, 0xD9, 0xB5, 0xB4, 0x7A, 0xF9, 0x83,
+ 0x58, 0xAD, 0xCE, 0x60, 0x98, 0xDB, 0x1C, 0x1B,
+ 0x52, 0xB8, 0xF3, 0x96, 0xED, 0xDE, 0xB3, 0xEE,
+ 0x4F, 0xBD, 0x10, 0xD4, 0x43, 0xEA, 0xE7, 0x37,
+ 0x12, 0x3D, 0xA8, 0x22, 0x65, 0xEC, 0x5B, 0x08,
+ 0x9D, 0x0D, 0x5C, 0xB6, 0x8A, 0x79, 0x3F, 0x04,
+ 0xD6, 0x01, 0xE1, 0xBE, 0xDD, 0x50, 0xFA, 0x41,
+ 0x13, 0x91, 0xF7, 0xDA, 0x18, 0xB0, 0x45, 0x81,
+ 0x4C, 0xF5, 0x32, 0x23, 0x56, 0x5A, 0xEB, 0x97,
+ 0x34, 0x00, 0x77, 0x71, 0x4B, 0x70, 0xD5, 0x31,
+ 0x72, 0x05, 0xDF, 0xE8, 0x15, 0x3B, 0x54, 0x16,
+ 0x89, 0xE4, 0xF1, 0xD7, 0x80, 0x82, 0x4A, 0xE3,
+ 0x39, 0x06, 0x47, 0x28, 0xC2, 0x86, 0x87, 0xB1,
+ 0x62, 0x74, 0x53, 0x21, 0x67, 0x38, 0x42, 0xCA,
+ 0x9B, 0xC3, 0x51, 0x99, 0x8B, 0x1F, 0x24, 0x8D,
+ 0xF8, 0x68, 0x3E, 0x59, 0xBB, 0x61, 0x5F, 0xBC,
+ 0x09, 0x6E, 0x8F, 0x0F, 0x2D, 0xC0, 0xE0, 0x46,
+ 0x66, 0x69, 0xA5, 0xE9, 0x30, 0x9C, 0x5E, 0xAE,
+ 0xBF, 0xC7, 0x20, 0x7E, 0x6A, 0xC5, 0x88, 0xFC,
+ 0x64, 0x76, 0xFF, 0x9F, 0x2E, 0x02, 0xCC, 0x57 };
+
+unsigned char table_12[32] = {
+ 0x14, 0x1B, 0x18, 0x00, 0x1F, 0x15, 0x17, 0x07,
+ 0x11, 0x1A, 0x0E, 0x13, 0x12, 0x06, 0x01, 0x03,
+ 0x1C, 0x0C, 0x0B, 0x1D, 0x10, 0x0F, 0x09, 0x19,
+ 0x0D, 0x1E, 0x04, 0x05, 0x08, 0x16, 0x0A, 0x02 };
+
+unsigned char table_13[256] = {
+ 0x37, 0x8A, 0x1B, 0x91, 0xA5, 0x2B, 0x2D, 0x88,
+ 0x8E, 0xFE, 0x0E, 0xD3, 0xF3, 0xE9, 0x7D, 0xD1,
+ 0x24, 0xEA, 0xB1, 0x8B, 0x5C, 0xA4, 0x44, 0x7E,
+ 0x8C, 0x2C, 0x73, 0xD5, 0x50, 0x3E, 0xD7, 0x18,
+ 0xB9, 0xD6, 0xBA, 0x94, 0x0C, 0xFC, 0xCB, 0xB4,
+ 0x0D, 0x63, 0x4C, 0xDE, 0x77, 0x16, 0xFD, 0x81,
+ 0x3C, 0x11, 0x45, 0x36, 0xF6, 0x67, 0x95, 0x6D,
+ 0x6A, 0x1A, 0xA3, 0xC5, 0x92, 0x10, 0x28, 0x84,
+ 0x48, 0xA6, 0x23, 0xE3, 0x4B, 0xE1, 0xF5, 0x19,
+ 0xE0, 0x2E, 0x00, 0x61, 0x74, 0xCC, 0xF7, 0xB0,
+ 0x68, 0xC8, 0x40, 0x6F, 0x59, 0x52, 0x26, 0x99,
+ 0xC9, 0xF9, 0xC4, 0x53, 0x9B, 0xEC, 0x03, 0x17,
+ 0xE2, 0x06, 0x30, 0x7B, 0xBE, 0xCD, 0x1D, 0x3B,
+ 0xD2, 0x5B, 0x65, 0x21, 0x49, 0xB7, 0x79, 0xCF,
+ 0x82, 0x86, 0xC7, 0x62, 0xEE, 0x8D, 0xFF, 0xD4,
+ 0xC3, 0x85, 0xA7, 0xFA, 0xA9, 0x6B, 0xF2, 0x69,
+ 0x9C, 0x38, 0x78, 0xBD, 0x7F, 0xDD, 0xCE, 0xA1,
+ 0x33, 0xC2, 0x43, 0xEB, 0xD8, 0xE6, 0x2A, 0xE4,
+ 0x76, 0x6C, 0xAA, 0x46, 0x05, 0xE7, 0xA0, 0x0A,
+ 0x71, 0x98, 0x41, 0x5F, 0x0F, 0xEF, 0x51, 0xAD,
+ 0xF0, 0xED, 0x96, 0x5A, 0x42, 0x3F, 0xBF, 0x6E,
+ 0xBC, 0x5D, 0xC1, 0x15, 0x70, 0x54, 0x4D, 0x14,
+ 0xB5, 0xCA, 0x27, 0x80, 0x87, 0x39, 0x60, 0x47,
+ 0x9D, 0x2F, 0x56, 0x1F, 0xBB, 0x31, 0xF1, 0xE8,
+ 0xB3, 0x9E, 0x5E, 0x7C, 0xD0, 0xC6, 0xB2, 0x57,
+ 0x83, 0xAC, 0x09, 0x8F, 0xA2, 0x90, 0x13, 0x25,
+ 0x01, 0x08, 0x64, 0xB6, 0x02, 0xDB, 0x55, 0x32,
+ 0xAF, 0x9A, 0xC0, 0x1C, 0x12, 0x29, 0x0B, 0x72,
+ 0x4F, 0xDA, 0xAB, 0x35, 0xF8, 0x22, 0xD9, 0x4E,
+ 0x3D, 0x1E, 0xDC, 0x58, 0x20, 0x34, 0xAE, 0x66,
+ 0x75, 0x93, 0x9F, 0x3A, 0x07, 0xE5, 0x89, 0xDF,
+ 0x97, 0x4A, 0xB8, 0x7A, 0xF4, 0xFB, 0x04, 0xA8 };
+
+unsigned char table_14[32] = {
+ 0x04, 0x14, 0x13, 0x15, 0x1A, 0x1B, 0x0F, 0x16,
+ 0x02, 0x0D, 0x0C, 0x06, 0x10, 0x17, 0x01, 0x0B,
+ 0x1E, 0x08, 0x1C, 0x18, 0x19, 0x0A, 0x1F, 0x05,
+ 0x11, 0x09, 0x1D, 0x07, 0x0E, 0x12, 0x03, 0x00 };
+
+unsigned char table_15[256] = {
+ 0x61, 0x48, 0x58, 0x41, 0x7F, 0x88, 0x43, 0x42,
+ 0xD9, 0x80, 0x81, 0xFE, 0xC6, 0x49, 0xD7, 0x2C,
+ 0xE6, 0x5B, 0xEE, 0xFF, 0x2A, 0x6F, 0xBF, 0x98,
+ 0xD6, 0x20, 0xB9, 0xB1, 0x5D, 0x95, 0x72, 0x1E,
+ 0x82, 0x96, 0xDE, 0xC1, 0x40, 0xD8, 0x70, 0xA3,
+ 0xD1, 0x1F, 0xF0, 0x9F, 0x2D, 0xDC, 0x3F, 0xF9,
+ 0x5E, 0x0D, 0x15, 0x2F, 0x67, 0x31, 0x9D, 0x84,
+ 0x97, 0x0C, 0xF6, 0x79, 0xC2, 0xA7, 0xC0, 0x32,
+ 0xB3, 0xEB, 0xED, 0x71, 0x30, 0xCC, 0x4B, 0xA0,
+ 0xF5, 0xC4, 0xCD, 0x27, 0xFA, 0x11, 0x25, 0xDB,
+ 0x4F, 0xE2, 0x7E, 0xA6, 0xAF, 0x34, 0x69, 0x63,
+ 0x8F, 0x08, 0x1C, 0x85, 0xF1, 0x57, 0x78, 0xC8,
+ 0xA2, 0x83, 0xB5, 0x68, 0xF7, 0x64, 0x45, 0x26,
+ 0x3B, 0x03, 0xAD, 0x3C, 0x50, 0xD5, 0x77, 0xFC,
+ 0xFB, 0x18, 0xC9, 0xD2, 0x9C, 0xBB, 0xBA, 0x76,
+ 0x23, 0x55, 0xD3, 0x5A, 0x01, 0xE9, 0x87, 0x07,
+ 0x19, 0x09, 0x39, 0x8A, 0x91, 0x93, 0x12, 0xDF,
+ 0x22, 0xA8, 0xCF, 0x4E, 0x4D, 0x65, 0xB0, 0x0F,
+ 0x13, 0x53, 0x21, 0x8C, 0xE5, 0xB7, 0x0B, 0x0E,
+ 0x6C, 0x44, 0xCA, 0x7B, 0xC5, 0x6E, 0xCE, 0xE3,
+ 0x14, 0x29, 0xAC, 0x2E, 0xE7, 0x59, 0xE8, 0x0A,
+ 0xEA, 0x66, 0x7C, 0x94, 0x6D, 0x05, 0x9E, 0x9A,
+ 0x2B, 0x38, 0x6A, 0xCB, 0x51, 0xEF, 0x06, 0xDA,
+ 0xFD, 0x47, 0x92, 0x1D, 0xA5, 0x37, 0x33, 0xEC,
+ 0xB4, 0x52, 0x56, 0xC3, 0xF4, 0xF8, 0x8B, 0xD0,
+ 0xA4, 0x5F, 0x28, 0x89, 0x75, 0xC7, 0x04, 0x00,
+ 0xE4, 0x86, 0x36, 0x3A, 0x99, 0x16, 0x7D, 0xE0,
+ 0x7A, 0x4C, 0x54, 0x46, 0x73, 0xB2, 0xF3, 0xE1,
+ 0x62, 0xBE, 0x90, 0x4A, 0x24, 0x6B, 0x3E, 0xAA,
+ 0x1B, 0xF2, 0x60, 0xD4, 0xA9, 0x9B, 0x1A, 0xB8,
+ 0xA1, 0x35, 0xAE, 0xB6, 0x10, 0x5C, 0x17, 0xBC,
+ 0xAB, 0x8D, 0x02, 0x74, 0xBD, 0x3D, 0x8E, 0xDD };
+
+unsigned char table_16[256] = {
+ 0x3F, 0x9C, 0x17, 0xC1, 0x59, 0xC6, 0x23, 0x93,
+ 0x4B, 0xDF, 0xCB, 0x55, 0x2B, 0xDE, 0xCD, 0xAD,
+ 0xB3, 0xE7, 0x42, 0x2F, 0x02, 0x5A, 0x7B, 0x5C,
+ 0x8F, 0xD1, 0x11, 0xCE, 0xEC, 0xF6, 0xA4, 0xE6,
+ 0x58, 0x98, 0x6A, 0x99, 0xFB, 0x9B, 0x53, 0x21,
+ 0x8A, 0x09, 0x2E, 0x3C, 0x22, 0x38, 0xAC, 0x07,
+ 0x91, 0x46, 0xA9, 0x95, 0xC3, 0x14, 0x84, 0xDB,
+ 0x36, 0x68, 0x1D, 0xDD, 0xF9, 0x12, 0xE0, 0x3D,
+ 0x8D, 0x4D, 0x05, 0x86, 0x69, 0xC0, 0xD3, 0xD5,
+ 0xA5, 0xC9, 0xE5, 0x67, 0x6D, 0xE2, 0x7F, 0xFE,
+ 0xB2, 0x0F, 0x62, 0xCF, 0x37, 0x35, 0xF3, 0x28,
+ 0x16, 0xA6, 0x50, 0x76, 0x80, 0x00, 0x31, 0x97,
+ 0x39, 0x7C, 0x25, 0x0C, 0x64, 0xF2, 0x52, 0x1A,
+ 0x92, 0x4F, 0x2A, 0x56, 0x03, 0x4C, 0xBD, 0x10,
+ 0xB7, 0x2C, 0x8C, 0xAE, 0x73, 0xB9, 0xE9, 0xF7,
+ 0xA7, 0xE1, 0x75, 0xBC, 0xC5, 0x1C, 0x3A, 0x63,
+ 0x7A, 0x4A, 0x29, 0xD2, 0x71, 0xE8, 0x08, 0xA1,
+ 0xD4, 0xFD, 0x13, 0xFA, 0xA0, 0x27, 0x41, 0x72,
+ 0x82, 0x18, 0x51, 0x60, 0x5E, 0x66, 0x0D, 0xAA,
+ 0xD8, 0x1F, 0xAF, 0x45, 0xD0, 0xF1, 0x9F, 0x6B,
+ 0xE4, 0x44, 0x89, 0xEE, 0xC4, 0x0B, 0x6C, 0xCC,
+ 0x83, 0x77, 0xA2, 0x87, 0x0A, 0xA8, 0xED, 0x90,
+ 0x74, 0x6E, 0xF5, 0xAB, 0xA3, 0xB6, 0x5F, 0x0E,
+ 0x04, 0x9A, 0xB4, 0x8E, 0xF0, 0xFF, 0x88, 0xB5,
+ 0xF8, 0xBF, 0x8B, 0x6F, 0x4E, 0x79, 0x40, 0xCA,
+ 0x24, 0x26, 0xDC, 0x33, 0xEB, 0x2D, 0x5B, 0x1B,
+ 0x9D, 0xC7, 0x49, 0x48, 0x54, 0x85, 0xEF, 0xD7,
+ 0xC2, 0xB8, 0xC8, 0x5D, 0xD9, 0x3B, 0x15, 0xBB,
+ 0x65, 0xE3, 0xD6, 0x30, 0x3E, 0x1E, 0x32, 0x9E,
+ 0x57, 0x81, 0x34, 0x06, 0xFC, 0xBA, 0x7D, 0x20,
+ 0x70, 0xDA, 0x7E, 0x47, 0x94, 0x61, 0xB0, 0x78,
+ 0xF4, 0xBE, 0xEA, 0x19, 0x43, 0x01, 0xB1, 0x96 };
+
+unsigned char table_17[256] = {
+ 0x7E, 0xF1, 0xD3, 0x75, 0x87, 0xA6, 0xED, 0x9E,
+ 0xA9, 0xD5, 0xC6, 0xBF, 0xE6, 0x6A, 0xEE, 0x4B,
+ 0x34, 0xDF, 0x4C, 0x7D, 0xDD, 0xFE, 0x3F, 0xAF,
+ 0x66, 0x2D, 0x74, 0x6F, 0xFC, 0x4F, 0x5F, 0x88,
+ 0x29, 0x7B, 0xC7, 0x2A, 0x70, 0xE8, 0x1D, 0xDE,
+ 0xD0, 0x55, 0x71, 0x81, 0xC4, 0x0D, 0x50, 0x4E,
+ 0x58, 0x00, 0x96, 0x97, 0xBB, 0xD7, 0x53, 0x15,
+ 0x6C, 0x40, 0x17, 0xC9, 0xFF, 0x8F, 0x94, 0xFB,
+ 0x19, 0x9A, 0x3E, 0xB5, 0x5A, 0x5E, 0x86, 0x24,
+ 0xB8, 0x77, 0xBA, 0x85, 0x51, 0x18, 0xBE, 0x59,
+ 0x79, 0xF3, 0xD4, 0xC3, 0xAB, 0x28, 0xFD, 0x25,
+ 0x41, 0x91, 0x07, 0x8D, 0xAE, 0x49, 0xF5, 0x80,
+ 0x35, 0xA1, 0x9C, 0x3C, 0xE2, 0x65, 0xB3, 0xE0,
+ 0x16, 0xCB, 0x12, 0x6B, 0xF7, 0xB1, 0x93, 0x8A,
+ 0xCE, 0x54, 0x4D, 0xF8, 0x13, 0xA2, 0x95, 0x46,
+ 0xEA, 0x61, 0x57, 0x9D, 0x27, 0x8B, 0x3D, 0x60,
+ 0x36, 0x68, 0x06, 0x56, 0xB6, 0x1B, 0xD2, 0x89,
+ 0x10, 0xA7, 0xC5, 0x1A, 0x0B, 0x2C, 0xBD, 0x14,
+ 0x0A, 0xDC, 0x23, 0xA8, 0xE1, 0x04, 0x02, 0xC0,
+ 0xB2, 0x9B, 0xE3, 0x2E, 0x33, 0x7C, 0x32, 0xAC,
+ 0x7A, 0x39, 0xB0, 0xF9, 0x98, 0x5B, 0x3A, 0x48,
+ 0x21, 0x90, 0xB9, 0x20, 0xF0, 0xA0, 0x09, 0x1F,
+ 0x2F, 0xEF, 0xEB, 0x22, 0x78, 0x82, 0x37, 0xD6,
+ 0xD1, 0x84, 0x76, 0x01, 0xDB, 0x43, 0xC2, 0xB7,
+ 0x7F, 0xA4, 0xE5, 0xC1, 0x1C, 0x69, 0x05, 0xEC,
+ 0xD8, 0x38, 0x67, 0x42, 0x72, 0xBC, 0x73, 0xAD,
+ 0xA3, 0xE9, 0x4A, 0x8E, 0x47, 0x1E, 0xC8, 0x6E,
+ 0xDA, 0x5D, 0x2B, 0xF6, 0x30, 0x63, 0xCC, 0xF4,
+ 0xCD, 0x8C, 0x0F, 0x3B, 0xE7, 0xD9, 0xCF, 0xB4,
+ 0x03, 0x92, 0x0E, 0x31, 0xE4, 0x08, 0xF2, 0x45,
+ 0xCA, 0x83, 0x26, 0x5C, 0xA5, 0x44, 0x64, 0x6D,
+ 0x9F, 0x99, 0x62, 0xAA, 0xFA, 0x11, 0x0C, 0x52 };
+
+unsigned char table_18[256] = {
+ 0x0F, 0x42, 0x3D, 0x86, 0x3E, 0x66, 0xFE, 0x5C,
+ 0x52, 0xE2, 0xA3, 0xB3, 0xCE, 0x16, 0xCC, 0x95,
+ 0xB0, 0x8B, 0x82, 0x3B, 0x93, 0x7D, 0x62, 0x08,
+ 0x1C, 0x6E, 0xBB, 0xCB, 0x1D, 0x88, 0x69, 0xD4,
+ 0xC9, 0x40, 0x1F, 0xBE, 0x27, 0xBC, 0xDB, 0x38,
+ 0xE5, 0xA1, 0x71, 0xBA, 0x8A, 0x5E, 0xFD, 0x36,
+ 0x8F, 0x26, 0x6B, 0xE4, 0x20, 0x6D, 0xC5, 0xDE,
+ 0xE0, 0x83, 0x7C, 0xD5, 0xD9, 0x4D, 0xDC, 0xE3,
+ 0x0D, 0x32, 0xED, 0x0E, 0x2F, 0x21, 0xA7, 0x79,
+ 0xA0, 0xD3, 0x8C, 0x14, 0x6F, 0xB7, 0xF8, 0x85,
+ 0x5D, 0x37, 0x24, 0xD6, 0x25, 0xD2, 0x8E, 0xA5,
+ 0xB8, 0xCD, 0x5A, 0x9F, 0x05, 0xAD, 0x65, 0x9E,
+ 0x4F, 0x5B, 0x56, 0xF0, 0xAA, 0xC2, 0x28, 0xA8,
+ 0x6A, 0x01, 0x99, 0x2E, 0xA6, 0x77, 0x74, 0x64,
+ 0x76, 0x15, 0x90, 0x75, 0xAF, 0xE8, 0x39, 0x48,
+ 0x09, 0x11, 0xE1, 0x2D, 0xEC, 0xB5, 0x7A, 0xB1,
+ 0x94, 0x13, 0x41, 0x4C, 0x02, 0xA9, 0x97, 0xDF,
+ 0xC3, 0x8D, 0xEA, 0x3A, 0x9C, 0xD1, 0xA2, 0x9A,
+ 0xD7, 0x59, 0xD8, 0x18, 0xDA, 0x47, 0x89, 0x81,
+ 0xC7, 0xF5, 0xFC, 0x98, 0xCA, 0x91, 0x06, 0x68,
+ 0xC8, 0x07, 0x4A, 0x84, 0x0A, 0xE7, 0x33, 0x2C,
+ 0xEB, 0xDD, 0x5F, 0xAC, 0x23, 0x1A, 0x35, 0x70,
+ 0x43, 0x80, 0x61, 0xAE, 0xC1, 0xD0, 0x7B, 0x92,
+ 0x49, 0x51, 0x53, 0xC4, 0x34, 0x30, 0x0C, 0x4B,
+ 0x00, 0x04, 0x10, 0xFF, 0x63, 0x44, 0xB4, 0x0B,
+ 0x57, 0x72, 0xF1, 0x9D, 0x19, 0xF6, 0xB2, 0x87,
+ 0x1B, 0xEE, 0x46, 0x2A, 0xF3, 0xBF, 0x12, 0x96,
+ 0x58, 0x2B, 0xF9, 0xB6, 0xCF, 0x22, 0x3C, 0xAB,
+ 0x1E, 0x6C, 0x31, 0xC6, 0xF7, 0x78, 0x45, 0x17,
+ 0xE9, 0x7E, 0x73, 0xF2, 0x55, 0xFB, 0x3F, 0x9B,
+ 0xF4, 0xBD, 0xA4, 0x29, 0x60, 0x03, 0xB9, 0x50,
+ 0xFA, 0x4E, 0xEF, 0x54, 0xE6, 0x7F, 0xC0, 0x67 };
+
+unsigned char table_19[256] = {
+ 0xEA, 0xE7, 0x13, 0x14, 0xB9, 0xC0, 0xC4, 0x42,
+ 0x49, 0x6E, 0x2A, 0xA6, 0x65, 0x3C, 0x6A, 0x40,
+ 0x07, 0xCD, 0x4F, 0xFE, 0xF2, 0x2D, 0xC8, 0x30,
+ 0x9D, 0xBE, 0x1B, 0x9B, 0x4A, 0x7E, 0x9F, 0xA7,
+ 0x78, 0xAB, 0x4D, 0x1D, 0xF1, 0x96, 0x32, 0x84,
+ 0xFB, 0x80, 0x88, 0xE8, 0x41, 0x97, 0xDC, 0xD0,
+ 0x4E, 0x33, 0xA4, 0x3B, 0xE0, 0xDD, 0x36, 0xC9,
+ 0x72, 0x48, 0x8A, 0x2F, 0x35, 0xF0, 0xDF, 0x21,
+ 0xE1, 0xE5, 0x6C, 0x9A, 0x60, 0x8F, 0xB7, 0x24,
+ 0xE4, 0x9E, 0x8C, 0x0F, 0x3D, 0x28, 0xBB, 0xD6,
+ 0x69, 0xA0, 0x66, 0xC7, 0xE3, 0xD8, 0x11, 0x27,
+ 0xD9, 0x37, 0xF4, 0xF5, 0x8E, 0xD4, 0x76, 0xE2,
+ 0xDB, 0x15, 0xA2, 0x5C, 0x9C, 0xEE, 0x44, 0xED,
+ 0x2B, 0xB3, 0x75, 0x74, 0x71, 0x8B, 0x3A, 0x91,
+ 0x06, 0x19, 0xC1, 0x57, 0x89, 0xCC, 0x82, 0x10,
+ 0x17, 0xB2, 0x08, 0x70, 0x39, 0xCA, 0xBA, 0xB5,
+ 0xAA, 0xBF, 0x02, 0xBD, 0x26, 0x58, 0x04, 0x54,
+ 0x23, 0x4B, 0x90, 0x51, 0x6D, 0x98, 0xD5, 0xB0,
+ 0xAF, 0x22, 0xDA, 0xB4, 0x87, 0xFC, 0x7D, 0x18,
+ 0x6F, 0x64, 0x59, 0x09, 0x0C, 0xA5, 0x5D, 0x03,
+ 0x0A, 0xD3, 0xCE, 0x99, 0x8D, 0xC2, 0xC3, 0x62,
+ 0xD2, 0x83, 0x1A, 0xAC, 0x7C, 0x93, 0xD7, 0xA9,
+ 0x16, 0xF7, 0x77, 0xE6, 0x3E, 0x05, 0x73, 0x55,
+ 0x43, 0x95, 0x7A, 0x6B, 0x38, 0x67, 0x3F, 0xC6,
+ 0xAD, 0x0E, 0x29, 0x46, 0x45, 0xFA, 0xBC, 0xEC,
+ 0x5B, 0x7F, 0x0B, 0x1C, 0x01, 0x12, 0x85, 0x50,
+ 0xF9, 0xEF, 0x25, 0x34, 0x79, 0x2E, 0xEB, 0x00,
+ 0x5F, 0x86, 0xF8, 0x4C, 0xA8, 0x56, 0xB6, 0x5A,
+ 0xF3, 0x31, 0x94, 0x92, 0xB1, 0xB8, 0x52, 0xD1,
+ 0xCF, 0xCB, 0xA1, 0x81, 0x68, 0x47, 0xFF, 0xC5,
+ 0xFD, 0x1F, 0xDE, 0x53, 0xA3, 0x2C, 0x20, 0xF6,
+ 0x1E, 0x0D, 0xAE, 0x7B, 0x5E, 0x61, 0xE9, 0x63 };
+
+unsigned char table_20[32] = {
+ 0x0D, 0x0B, 0x11, 0x02, 0x05, 0x1B, 0x08, 0x1D,
+ 0x04, 0x14, 0x01, 0x09, 0x00, 0x19, 0x1E, 0x15,
+ 0x1F, 0x0A, 0x0F, 0x1C, 0x10, 0x16, 0x0C, 0x07,
+ 0x13, 0x1A, 0x06, 0x17, 0x0E, 0x12, 0x18, 0x03 };
+
+unsigned char table_21[256] = {
+ 0x4C, 0x94, 0xAD, 0x66, 0x9E, 0x69, 0x04, 0xA8,
+ 0x61, 0xE0, 0xE1, 0x3D, 0xFD, 0x9C, 0xFB, 0x19,
+ 0x1E, 0x80, 0x8C, 0xA0, 0xFC, 0x27, 0x26, 0x3B,
+ 0x48, 0x6D, 0x07, 0xE4, 0xEA, 0x17, 0x64, 0x9B,
+ 0xD0, 0xE2, 0xD1, 0x13, 0x39, 0xF5, 0x73, 0xD3,
+ 0x0C, 0x3A, 0x6E, 0x77, 0xFA, 0xE3, 0x2F, 0x44,
+ 0x7E, 0x72, 0x30, 0x43, 0xD4, 0x7F, 0x36, 0xD9,
+ 0xBD, 0x3E, 0x3F, 0x91, 0xBE, 0x54, 0x79, 0xA6,
+ 0x7C, 0x0E, 0xC5, 0x7A, 0x70, 0xC4, 0xD7, 0xCE,
+ 0xDA, 0xAA, 0x68, 0x8F, 0xBC, 0x96, 0x1B, 0x16,
+ 0xA2, 0xC6, 0x67, 0x09, 0x45, 0x9F, 0xCF, 0x41,
+ 0xC8, 0x60, 0x74, 0x99, 0x5D, 0x85, 0x5F, 0x50,
+ 0x33, 0x52, 0x22, 0xA9, 0xB5, 0x2D, 0x98, 0x87,
+ 0x15, 0x9A, 0xAC, 0x2C, 0xDE, 0xC0, 0xB8, 0x37,
+ 0x88, 0x1F, 0xC1, 0x4F, 0x65, 0x0F, 0x3C, 0x84,
+ 0x4B, 0x1A, 0xAB, 0xA4, 0x23, 0xCB, 0xB1, 0xC7,
+ 0xDB, 0xEF, 0x40, 0x0D, 0x46, 0xE8, 0xF4, 0x71,
+ 0x38, 0x01, 0x5C, 0x0B, 0x5E, 0xC9, 0xAF, 0xC3,
+ 0xF6, 0xB6, 0x10, 0x1D, 0xE5, 0x8A, 0x90, 0xA7,
+ 0xA3, 0x05, 0x4E, 0x14, 0x63, 0x25, 0x34, 0xEC,
+ 0x6B, 0x95, 0x21, 0x55, 0xF2, 0xF0, 0x47, 0x9D,
+ 0xF8, 0x8E, 0x02, 0x0A, 0xED, 0x97, 0xAE, 0x00,
+ 0x2A, 0xEB, 0xB2, 0xA5, 0x32, 0x06, 0x2E, 0xFE,
+ 0x8D, 0x7B, 0x7D, 0x35, 0x5A, 0xD2, 0xF1, 0xE9,
+ 0xF9, 0x62, 0xB7, 0xB9, 0x53, 0x75, 0x5B, 0x8B,
+ 0xCC, 0x6C, 0x18, 0x49, 0x89, 0x31, 0xB0, 0x92,
+ 0x6F, 0xDF, 0x03, 0x57, 0xF3, 0x58, 0xCA, 0x2B,
+ 0x93, 0xA1, 0xD6, 0x24, 0x29, 0xCD, 0x59, 0x1C,
+ 0x83, 0xB3, 0x42, 0xBF, 0x82, 0xB4, 0x11, 0x4A,
+ 0x08, 0xEE, 0x76, 0x4D, 0x12, 0xDC, 0xE6, 0xC2,
+ 0x56, 0xBA, 0x86, 0x28, 0x6A, 0x20, 0x51, 0xF7,
+ 0xFF, 0xD8, 0xE7, 0xDD, 0xBB, 0x78, 0xD5, 0x81 };
+
+unsigned char table_22[32] = {
+ 0x0B, 0x15, 0x1C, 0x0C, 0x06, 0x0A, 0x1D, 0x16,
+ 0x12, 0x0E, 0x04, 0x11, 0x1F, 0x0F, 0x07, 0x02,
+ 0x17, 0x13, 0x19, 0x18, 0x0D, 0x10, 0x1A, 0x05,
+ 0x03, 0x00, 0x01, 0x08, 0x09, 0x14, 0x1B, 0x1E };
+
+unsigned char table_23[256] = {
+ 0x36, 0x53, 0x2D, 0xD0, 0x7A, 0xF0, 0xD5, 0x1C,
+ 0x50, 0x61, 0x9A, 0x90, 0x0B, 0x29, 0x20, 0x77,
+ 0xF1, 0x82, 0xFE, 0xC1, 0xA7, 0xB6, 0x78, 0x87,
+ 0x02, 0x05, 0xCB, 0x28, 0xAE, 0xD6, 0x17, 0x1A,
+ 0x91, 0x5D, 0xB9, 0xE2, 0xDE, 0x6A, 0x4E, 0x07,
+ 0xAC, 0x38, 0x13, 0x3B, 0x46, 0xFD, 0xB7, 0xD1,
+ 0x79, 0xFB, 0x58, 0x76, 0x08, 0x47, 0x95, 0xA6,
+ 0x99, 0x9E, 0x12, 0x67, 0xC2, 0xED, 0x9C, 0x1B,
+ 0x89, 0x71, 0xB5, 0x4A, 0xAA, 0x5F, 0x34, 0x85,
+ 0x40, 0x2B, 0x9F, 0x37, 0x7C, 0x0F, 0xD4, 0x75,
+ 0x48, 0x27, 0x2E, 0xC9, 0xEB, 0x06, 0xDF, 0x8C,
+ 0x14, 0xAF, 0xEE, 0xA2, 0x74, 0x45, 0x8D, 0x70,
+ 0x6B, 0xD7, 0x56, 0xCF, 0xBC, 0x7B, 0x01, 0xC8,
+ 0x54, 0xB0, 0x3C, 0x39, 0xFA, 0x81, 0xDC, 0xBB,
+ 0x0D, 0xB2, 0xAD, 0x93, 0xC7, 0x8A, 0x73, 0x6C,
+ 0xC3, 0x04, 0x2F, 0xEF, 0x52, 0x33, 0x9D, 0x1E,
+ 0xC5, 0x65, 0x23, 0xD8, 0xB1, 0xD2, 0xE5, 0x25,
+ 0x2C, 0xE6, 0x92, 0xB4, 0xF7, 0xF4, 0x8F, 0x6E,
+ 0xE8, 0x5A, 0x8E, 0x7D, 0x4C, 0xB3, 0xFF, 0x41,
+ 0x26, 0xE3, 0x30, 0x69, 0xF8, 0x80, 0x57, 0x4F,
+ 0xA0, 0x7F, 0x66, 0x68, 0xE1, 0x7E, 0x0E, 0x31,
+ 0xE7, 0xEA, 0x3E, 0x8B, 0x4B, 0x94, 0xE9, 0xCD,
+ 0x19, 0x35, 0xA3, 0x98, 0xD9, 0x5B, 0x44, 0x2A,
+ 0xE0, 0x6D, 0xF3, 0xE4, 0x72, 0x18, 0x03, 0x59,
+ 0x84, 0x09, 0xA1, 0x9B, 0xBD, 0xDA, 0x4D, 0x63,
+ 0xCC, 0x3A, 0x10, 0xFC, 0x3F, 0x0A, 0x88, 0x24,
+ 0xF5, 0x21, 0xC4, 0x6F, 0x1F, 0x42, 0x62, 0x64,
+ 0x51, 0xDD, 0xCA, 0xF9, 0x22, 0xCE, 0xA8, 0x86,
+ 0xBA, 0xB8, 0x5C, 0xAB, 0x32, 0x00, 0x0C, 0xF2,
+ 0x83, 0xDB, 0xF6, 0x60, 0x3D, 0x16, 0xEC, 0x11,
+ 0xA4, 0xBE, 0x96, 0x5E, 0x97, 0xD3, 0xA5, 0x55,
+ 0x1D, 0x15, 0xC6, 0xBF, 0xA9, 0x43, 0xC0, 0x49 };
+
+unsigned char table_24[256] = {
+ 0xDC, 0x5A, 0xE6, 0x59, 0x64, 0xDA, 0x58, 0x40,
+ 0x95, 0xF8, 0x2A, 0xE0, 0x39, 0x7E, 0x32, 0x89,
+ 0x09, 0x93, 0xED, 0x55, 0xC3, 0x5B, 0x1A, 0xD1,
+ 0xA5, 0x8B, 0x0F, 0x13, 0xC9, 0xE1, 0x34, 0xD0,
+ 0xB6, 0xA2, 0xD9, 0x52, 0x57, 0x83, 0xFD, 0xE9,
+ 0xAC, 0x73, 0x6E, 0x21, 0xF1, 0x0E, 0x25, 0xCC,
+ 0x36, 0xFB, 0xF7, 0x92, 0x15, 0x30, 0x54, 0x91,
+ 0xD6, 0x9E, 0xAA, 0x35, 0x70, 0xB2, 0xC0, 0x27,
+ 0xFE, 0x04, 0xBC, 0xC7, 0x02, 0xFA, 0x7D, 0xE3,
+ 0xBE, 0x62, 0x79, 0x2B, 0x31, 0x6A, 0x8F, 0x7F,
+ 0x56, 0xF0, 0xB4, 0x0C, 0x1F, 0x68, 0xB7, 0xB9,
+ 0x0B, 0x14, 0x3E, 0xA9, 0x4B, 0x03, 0x10, 0xEE,
+ 0x2C, 0xAB, 0x8A, 0x77, 0xB1, 0xE7, 0xCA, 0xD4,
+ 0x98, 0x01, 0xAD, 0x1E, 0x50, 0x26, 0x82, 0x44,
+ 0xF3, 0xBF, 0xD3, 0x6B, 0x33, 0x0A, 0x3C, 0x5D,
+ 0xCE, 0x81, 0xC5, 0x78, 0x9F, 0xB8, 0x23, 0xDB,
+ 0x4E, 0xA1, 0x41, 0x76, 0xAE, 0x51, 0x86, 0x06,
+ 0x7A, 0x66, 0xA0, 0x5E, 0x29, 0x17, 0x84, 0x4A,
+ 0xB0, 0x3B, 0x3D, 0x71, 0x07, 0x7B, 0x0D, 0x9A,
+ 0x6F, 0x9B, 0x5C, 0x88, 0xB3, 0xD7, 0x24, 0xD5,
+ 0x48, 0xF5, 0xE8, 0xE4, 0xCF, 0x16, 0xA4, 0xC8,
+ 0xEF, 0x42, 0x22, 0xEC, 0x47, 0x69, 0x90, 0x63,
+ 0xE2, 0x1B, 0x87, 0x85, 0x3F, 0xDE, 0x8C, 0x60,
+ 0x99, 0xE5, 0x8E, 0x4F, 0xF4, 0xBA, 0xB5, 0x9C,
+ 0x37, 0x67, 0xBD, 0xA6, 0x97, 0xDD, 0xCB, 0x43,
+ 0x45, 0x19, 0x49, 0x1C, 0x75, 0xC1, 0xBB, 0xF2,
+ 0x46, 0xFC, 0x53, 0x9D, 0xD8, 0xA3, 0xDF, 0x2F,
+ 0xEB, 0x72, 0x94, 0xA8, 0x6D, 0xC6, 0x28, 0x4C,
+ 0x00, 0x38, 0xC2, 0x65, 0x05, 0x2E, 0xD2, 0x12,
+ 0xFF, 0x18, 0x61, 0x6C, 0x7C, 0x11, 0xAF, 0x96,
+ 0xCD, 0x20, 0x74, 0x08, 0x1D, 0xC4, 0xF9, 0x4D,
+ 0xEA, 0x8D, 0x2D, 0x5F, 0xF6, 0xA7, 0x80, 0x3A };
+
+unsigned char table_25[32] = {
+ 0x0A, 0x11, 0x17, 0x03, 0x05, 0x0B, 0x18, 0x13,
+ 0x09, 0x02, 0x00, 0x1C, 0x0C, 0x08, 0x1B, 0x14,
+ 0x06, 0x0E, 0x01, 0x0D, 0x16, 0x1E, 0x1D, 0x19,
+ 0x0F, 0x1A, 0x10, 0x04, 0x12, 0x15, 0x07, 0x1F };
+
+unsigned char table_26[32] = {
+ 0x19, 0x13, 0x1B, 0x01, 0x1C, 0x0D, 0x0C, 0x15,
+ 0x0B, 0x00, 0x1A, 0x0F, 0x12, 0x16, 0x08, 0x0A,
+ 0x03, 0x06, 0x14, 0x10, 0x18, 0x04, 0x11, 0x1D,
+ 0x1F, 0x07, 0x17, 0x05, 0x02, 0x0E, 0x1E, 0x09 };
+
+unsigned char table_27[256] = {
+ 0x72, 0xF0, 0x14, 0xCB, 0x61, 0xA5, 0xB2, 0x02,
+ 0x75, 0x22, 0xC3, 0x9D, 0x5A, 0x63, 0xFA, 0x5F,
+ 0xD9, 0x55, 0x58, 0x43, 0x24, 0x7D, 0x77, 0x93,
+ 0xBA, 0x50, 0x1D, 0xF7, 0x49, 0x18, 0xB0, 0x42,
+ 0xBB, 0xEC, 0x52, 0x38, 0xDC, 0xC8, 0x16, 0x54,
+ 0x17, 0x19, 0x89, 0x67, 0x33, 0x3C, 0x0A, 0xAD,
+ 0xC9, 0xDE, 0x81, 0xED, 0xBD, 0x0E, 0x0B, 0x6D,
+ 0x46, 0x30, 0x35, 0x2B, 0x8C, 0xA0, 0x1C, 0x0D,
+ 0xFD, 0xA1, 0x70, 0xC6, 0xD8, 0x41, 0xB3, 0xC0,
+ 0x44, 0xEB, 0x92, 0xBE, 0x6B, 0x98, 0x1A, 0x76,
+ 0x71, 0xC5, 0x51, 0x56, 0x80, 0xFC, 0x01, 0x53,
+ 0x4B, 0xD0, 0x8B, 0xD2, 0x7B, 0xE7, 0x15, 0x5D,
+ 0xE5, 0xA6, 0x8A, 0xD3, 0x9B, 0xF4, 0x69, 0x23,
+ 0xE8, 0xB6, 0xC7, 0xE2, 0x73, 0x9F, 0x88, 0xDF,
+ 0xB4, 0x28, 0xEE, 0xC2, 0x94, 0xB8, 0xF9, 0x7F,
+ 0x4A, 0x57, 0x06, 0xF6, 0xBF, 0xC1, 0xAB, 0xFB,
+ 0xA4, 0x8E, 0xD1, 0xD7, 0xF5, 0x7C, 0xA3, 0x1E,
+ 0x3B, 0x32, 0x03, 0xAA, 0x90, 0x5C, 0x48, 0xE0,
+ 0xE3, 0xCF, 0xD4, 0xEF, 0x59, 0xD5, 0x1B, 0x34,
+ 0x1F, 0x95, 0xCE, 0x7A, 0x20, 0x26, 0x87, 0xB7,
+ 0x78, 0x9C, 0x4F, 0xA2, 0x12, 0x97, 0x27, 0x3F,
+ 0xFF, 0x07, 0x84, 0x96, 0x04, 0xAF, 0xA8, 0xEA,
+ 0x2C, 0x6C, 0xAE, 0x37, 0x91, 0xA9, 0x10, 0xDB,
+ 0xCD, 0xDA, 0x08, 0x99, 0xF1, 0x4D, 0xCC, 0x68,
+ 0x79, 0x2E, 0xB1, 0x39, 0x9E, 0xE9, 0x2F, 0x6A,
+ 0x3D, 0x0F, 0x85, 0x8D, 0xCA, 0x29, 0x86, 0xD6,
+ 0xDD, 0x05, 0x25, 0x3A, 0x40, 0x21, 0x45, 0xAC,
+ 0x11, 0xF3, 0xA7, 0x09, 0x2A, 0x31, 0xE4, 0x0C,
+ 0xF8, 0x6E, 0x3E, 0xB5, 0x82, 0xFE, 0x74, 0x13,
+ 0x65, 0xE1, 0x2D, 0x8F, 0xE6, 0xC4, 0x00, 0x5B,
+ 0x4E, 0xB9, 0x66, 0xF2, 0x62, 0x36, 0x4C, 0x83,
+ 0x5E, 0x6F, 0x47, 0x64, 0xBC, 0x9A, 0x60, 0x7E };
+
+unsigned char table_28[32] = {
+ 0x15, 0x05, 0x08, 0x19, 0x02, 0x18, 0x1E, 0x07,
+ 0x0D, 0x0C, 0x1A, 0x06, 0x17, 0x03, 0x10, 0x09,
+ 0x01, 0x11, 0x1C, 0x04, 0x0F, 0x1F, 0x12, 0x0B,
+ 0x1B, 0x13, 0x0A, 0x16, 0x0E, 0x00, 0x1D, 0x14 };
+
+unsigned char table_29[256] = {
+ 0x34, 0x59, 0x05, 0x13, 0x09, 0x1D, 0xDF, 0x77,
+ 0x11, 0xA5, 0x92, 0x27, 0xCD, 0x7B, 0x5E, 0x80,
+ 0xF9, 0x50, 0x18, 0x24, 0xD4, 0x70, 0x4A, 0x39,
+ 0x66, 0xA4, 0xDB, 0xE9, 0xED, 0x48, 0xD9, 0xE7,
+ 0x32, 0xDA, 0x53, 0x8F, 0x72, 0xE1, 0xF6, 0xFE,
+ 0xD3, 0xAD, 0xA6, 0x1F, 0xB9, 0xD1, 0x0F, 0x4C,
+ 0x23, 0x90, 0x68, 0xBC, 0x4B, 0x9B, 0x3D, 0xAB,
+ 0xF0, 0x94, 0x4F, 0x1C, 0x07, 0x65, 0x7F, 0x01,
+ 0x5C, 0xD7, 0x21, 0x8C, 0xBF, 0x8E, 0xB8, 0x86,
+ 0x6C, 0x33, 0x36, 0xC1, 0x06, 0x74, 0x37, 0x84,
+ 0x41, 0xAE, 0x67, 0x29, 0xB4, 0x85, 0xCE, 0x2A,
+ 0xCB, 0x1E, 0x61, 0x9E, 0x7A, 0x44, 0x3E, 0x89,
+ 0x14, 0x20, 0x19, 0xBB, 0xE0, 0xAA, 0xCF, 0x83,
+ 0xA8, 0x93, 0x43, 0xF2, 0xAC, 0x0E, 0xD2, 0xCC,
+ 0xDD, 0x47, 0x58, 0xC9, 0xCA, 0x1B, 0x54, 0x6E,
+ 0x8A, 0x79, 0xF8, 0xC4, 0xFB, 0xD5, 0x91, 0xDE,
+ 0x12, 0x31, 0x99, 0xFA, 0x6D, 0xC8, 0x57, 0xEC,
+ 0xB7, 0x28, 0x0C, 0x52, 0xF1, 0x0D, 0xB1, 0x9A,
+ 0x26, 0x98, 0x16, 0x7D, 0xD0, 0x2E, 0x8B, 0xD8,
+ 0xE6, 0xE8, 0x30, 0xFD, 0x7C, 0x64, 0x5A, 0xBD,
+ 0x87, 0xE2, 0xA1, 0x3F, 0xC3, 0x38, 0x96, 0xA3,
+ 0x2D, 0xF3, 0x3A, 0xEE, 0xC0, 0x10, 0xEA, 0x6F,
+ 0x8D, 0x03, 0xF4, 0x51, 0x97, 0x7E, 0x56, 0x42,
+ 0x3C, 0x5D, 0x5F, 0xF5, 0x6A, 0xAF, 0xE4, 0xBE,
+ 0xBA, 0x78, 0xA0, 0x5B, 0x49, 0xA7, 0xC7, 0x9C,
+ 0x63, 0x6B, 0x00, 0x17, 0x69, 0x75, 0x3B, 0x40,
+ 0xEF, 0x45, 0xB5, 0x2B, 0x2F, 0x02, 0xC6, 0x22,
+ 0x9F, 0xFC, 0x73, 0x08, 0x81, 0xB2, 0x2C, 0x71,
+ 0x35, 0xA2, 0xE3, 0xB3, 0x9D, 0xC5, 0x0A, 0xC2,
+ 0x25, 0x82, 0xDC, 0x88, 0xA9, 0xE5, 0xF7, 0xEB,
+ 0xD6, 0x60, 0x76, 0x55, 0x0B, 0x4E, 0xFF, 0x1A,
+ 0x46, 0x62, 0xB6, 0xB0, 0x15, 0x04, 0x95, 0x4D };
+
+unsigned char table_30[32] = {
+ 0x00, 0x1C, 0x0E, 0x0C, 0x06, 0x16, 0x09, 0x12,
+ 0x01, 0x13, 0x0B, 0x14, 0x11, 0x08, 0x04, 0x18,
+ 0x10, 0x1B, 0x15, 0x03, 0x02, 0x19, 0x1A, 0x17,
+ 0x1E, 0x1F, 0x0F, 0x07, 0x0D, 0x05, 0x1D, 0x0A };
+
+unsigned char table_31[256] = {
+ 0xDF, 0xD8, 0x3F, 0xBC, 0x5F, 0xC9, 0x8E, 0x4C,
+ 0x0B, 0x3C, 0xE5, 0xBF, 0x39, 0xD5, 0x30, 0xDD,
+ 0x23, 0xC7, 0x72, 0x63, 0x1F, 0xF8, 0x96, 0x31,
+ 0x70, 0xD6, 0x9E, 0xE8, 0x9D, 0xF5, 0xEF, 0x65,
+ 0xC2, 0x50, 0x62, 0x77, 0xD3, 0x6C, 0x1A, 0x91,
+ 0xBB, 0xFF, 0xCD, 0x9B, 0xB6, 0xBA, 0xB8, 0x7A,
+ 0x14, 0xA7, 0x74, 0x89, 0xD4, 0x6E, 0x19, 0x69,
+ 0xAB, 0x01, 0x15, 0x0E, 0x87, 0x55, 0x79, 0x1C,
+ 0x18, 0xBE, 0xA8, 0xDB, 0x52, 0xD2, 0x8F, 0x7E,
+ 0x81, 0xAF, 0xFD, 0x5C, 0x3E, 0x1B, 0xB9, 0xB2,
+ 0xB7, 0x51, 0x57, 0x8C, 0xCF, 0x5B, 0xA4, 0x75,
+ 0xDE, 0x22, 0x8B, 0x10, 0x12, 0xC8, 0x35, 0x2D,
+ 0x45, 0xB5, 0xF0, 0x47, 0x88, 0x16, 0xEB, 0x67,
+ 0xD9, 0x0C, 0xF1, 0xC1, 0x34, 0x33, 0xC6, 0x78,
+ 0xB3, 0x26, 0xE3, 0xBD, 0x5D, 0x4E, 0x66, 0xE4,
+ 0xD7, 0xC4, 0xE6, 0xA1, 0xB0, 0x95, 0x2B, 0x9A,
+ 0x4A, 0x3A, 0xCB, 0x40, 0xE1, 0x60, 0x49, 0xCC,
+ 0x03, 0xAC, 0xF4, 0x97, 0x32, 0x0F, 0x38, 0x17,
+ 0xF9, 0xE0, 0xD1, 0xFB, 0x04, 0x5E, 0x68, 0x06,
+ 0xAE, 0xFA, 0xAA, 0xED, 0x24, 0x0D, 0x00, 0x61,
+ 0x20, 0xA3, 0x7B, 0x6B, 0x76, 0x27, 0xEA, 0xCE,
+ 0x6A, 0x82, 0x9F, 0x6D, 0x9C, 0x64, 0xA2, 0x11,
+ 0x37, 0x2A, 0xCA, 0x84, 0x25, 0x7C, 0x2F, 0x8D,
+ 0x90, 0xE7, 0x09, 0x93, 0xF3, 0x43, 0x71, 0xEC,
+ 0xA9, 0x7D, 0x94, 0xA6, 0x3D, 0x7F, 0x54, 0x44,
+ 0x99, 0x80, 0x41, 0xC0, 0xA0, 0x8A, 0x1E, 0xDC,
+ 0x08, 0xD0, 0x2E, 0x42, 0x05, 0x85, 0x86, 0xFE,
+ 0x3B, 0x59, 0xC3, 0x58, 0x13, 0xB4, 0x36, 0xA5,
+ 0x73, 0x28, 0x29, 0xDA, 0x4F, 0x1D, 0xB1, 0x53,
+ 0x46, 0x2C, 0xF2, 0x4D, 0xAD, 0xFC, 0x83, 0x02,
+ 0x6F, 0x07, 0xE9, 0xEE, 0x21, 0x98, 0x5A, 0xC5,
+ 0x92, 0x48, 0xF7, 0x0A, 0xF6, 0xE2, 0x4B, 0x56 };
+
+unsigned char table_32[256] = {
+ 0x7B, 0x0F, 0x56, 0x2F, 0x1E, 0x2A, 0x7A, 0xD1,
+ 0x02, 0x91, 0x4E, 0x37, 0x6C, 0x10, 0xA7, 0xF2,
+ 0x38, 0xAC, 0x9E, 0x2B, 0x5E, 0x23, 0xE3, 0x19,
+ 0x9B, 0xF6, 0xB0, 0x59, 0x14, 0xB9, 0xA9, 0x46,
+ 0x84, 0x1D, 0xC0, 0x98, 0xF3, 0xE1, 0xE8, 0x94,
+ 0x52, 0x35, 0xBA, 0xD8, 0x07, 0xEF, 0x31, 0xF8,
+ 0x03, 0x76, 0x9C, 0xD7, 0xE4, 0x8B, 0xAF, 0x60,
+ 0xDD, 0x51, 0x00, 0xDF, 0x11, 0x7F, 0x1C, 0xED,
+ 0x49, 0xC9, 0xF4, 0x87, 0x64, 0xFC, 0x5D, 0xAD,
+ 0x88, 0x85, 0xF7, 0x5A, 0x92, 0xDB, 0x72, 0x1A,
+ 0x83, 0x15, 0x30, 0x24, 0x9F, 0xFF, 0x5B, 0xF1,
+ 0xD2, 0xFD, 0xC2, 0xB5, 0x25, 0x22, 0x18, 0x3D,
+ 0xCD, 0x97, 0x8C, 0xCC, 0x78, 0x90, 0xAA, 0x5F,
+ 0x0A, 0x57, 0x05, 0x61, 0xD4, 0xA0, 0x3A, 0xDE,
+ 0x3B, 0xF9, 0x65, 0x68, 0x4F, 0x28, 0xFA, 0xEB,
+ 0x63, 0x2D, 0x8D, 0xD0, 0xA1, 0xFE, 0x12, 0x96,
+ 0x3C, 0x42, 0x29, 0xD6, 0xA4, 0x34, 0xBD, 0x70,
+ 0x89, 0xBE, 0xF5, 0x79, 0xAB, 0x8F, 0x32, 0xB4,
+ 0xEE, 0xE7, 0x2C, 0x04, 0x4B, 0xD5, 0xB1, 0x54,
+ 0xF0, 0xDA, 0x16, 0x77, 0xA6, 0x53, 0xB2, 0xE2,
+ 0x73, 0xBF, 0x17, 0xA8, 0x75, 0x26, 0xE0, 0xBC,
+ 0x0C, 0x71, 0xFB, 0x6D, 0x7E, 0xC5, 0xEA, 0x21,
+ 0x9D, 0x95, 0x8E, 0xA5, 0x48, 0xB8, 0x7D, 0xCB,
+ 0x01, 0x99, 0xE5, 0xBB, 0x82, 0xC4, 0xCA, 0xC1,
+ 0x58, 0x6E, 0x5C, 0x7C, 0xDC, 0x33, 0xB6, 0xC3,
+ 0x09, 0xC7, 0x1F, 0x0D, 0x43, 0x6F, 0xE9, 0x86,
+ 0x27, 0xC8, 0x44, 0xB3, 0xD3, 0xCF, 0x08, 0x66,
+ 0x1B, 0x20, 0x4D, 0xD9, 0xC6, 0x36, 0x40, 0x74,
+ 0x62, 0x6A, 0x55, 0xEC, 0x06, 0x2E, 0xE6, 0x80,
+ 0x13, 0x93, 0x50, 0xCE, 0x69, 0x3E, 0x67, 0x4A,
+ 0x81, 0x4C, 0x0B, 0x3F, 0xB7, 0x0E, 0x39, 0xAE,
+ 0x47, 0x6B, 0x8A, 0xA2, 0x9A, 0xA3, 0x45, 0x41 };
+
+unsigned char table_33[256] = {
+ 0xDE, 0xD3, 0x79, 0x67, 0x13, 0x5C, 0x04, 0xF2,
+ 0xD9, 0x9F, 0x65, 0x56, 0xCC, 0x3B, 0xA4, 0x9A,
+ 0x08, 0xBF, 0x26, 0xB2, 0xA7, 0x5E, 0xAA, 0xCA,
+ 0xBB, 0x2B, 0x38, 0x3F, 0xD8, 0x87, 0xFA, 0x5D,
+ 0x73, 0x8E, 0x1E, 0x93, 0x05, 0xAF, 0x3E, 0x4E,
+ 0x90, 0xDB, 0x0B, 0x33, 0x0D, 0x2F, 0x86, 0x4F,
+ 0xFD, 0xD0, 0x39, 0xB1, 0x8A, 0x1A, 0x20, 0xE6,
+ 0xCF, 0xA2, 0x82, 0xDF, 0x42, 0x9C, 0x30, 0x40,
+ 0xE3, 0xB0, 0x88, 0x5A, 0xEC, 0x25, 0xE2, 0xC4,
+ 0x12, 0x54, 0x50, 0x97, 0x96, 0x21, 0x23, 0x7B,
+ 0x1D, 0x61, 0x52, 0x34, 0x7D, 0x69, 0x16, 0xC3,
+ 0x31, 0xF8, 0x48, 0x19, 0x95, 0x01, 0x29, 0x8C,
+ 0x15, 0xAC, 0x84, 0x74, 0xAB, 0x70, 0xDA, 0x36,
+ 0xD6, 0x8F, 0xFE, 0x35, 0xD7, 0x2E, 0x89, 0x07,
+ 0x62, 0x17, 0xDC, 0x92, 0x45, 0x83, 0xB5, 0xE5,
+ 0x8B, 0xC0, 0x27, 0x85, 0x7C, 0x9D, 0x55, 0x81,
+ 0x71, 0xCD, 0xC9, 0x00, 0x02, 0xC1, 0x0A, 0x37,
+ 0xED, 0xEA, 0xC2, 0x98, 0x49, 0x06, 0x1C, 0x78,
+ 0x64, 0xCE, 0x9E, 0x4C, 0x7A, 0xB4, 0x43, 0x0F,
+ 0xE0, 0x7E, 0xBC, 0x5B, 0x51, 0xE7, 0x18, 0xF9,
+ 0x11, 0xA1, 0xF5, 0xC7, 0xCB, 0x4D, 0x6A, 0x0E,
+ 0x57, 0xF1, 0xFB, 0xB3, 0x99, 0xF0, 0x32, 0xD5,
+ 0xA9, 0x4B, 0x6F, 0x6D, 0xA8, 0xC5, 0xDD, 0x7F,
+ 0xEB, 0xBE, 0xFC, 0x2C, 0x22, 0x58, 0x03, 0x9B,
+ 0x77, 0xF7, 0xBD, 0xBA, 0xD2, 0x6B, 0xAD, 0x5F,
+ 0x10, 0x6E, 0x09, 0xD1, 0x1B, 0x24, 0xEF, 0x72,
+ 0x3D, 0x59, 0x28, 0xE1, 0xB7, 0x44, 0x8D, 0xB8,
+ 0xAE, 0x2D, 0x60, 0xA6, 0xC8, 0x0C, 0xF4, 0x41,
+ 0xA3, 0x68, 0x46, 0x6C, 0x76, 0xA0, 0xB6, 0x66,
+ 0xE4, 0x1F, 0x75, 0x4A, 0xFF, 0x2A, 0x94, 0xD4,
+ 0xF3, 0xE9, 0x91, 0x63, 0xA5, 0xB9, 0xE8, 0x14,
+ 0x80, 0x3C, 0xEE, 0x47, 0xC6, 0x3A, 0x53, 0xF6 };
+
+unsigned char table_34[256] = {
+ 0xF0, 0xE9, 0x3E, 0xD6, 0x89, 0xC8, 0xC7, 0x23,
+ 0x75, 0x26, 0x5F, 0x9C, 0x57, 0xB8, 0x2A, 0x29,
+ 0xE5, 0xB5, 0x68, 0xA4, 0x92, 0x46, 0x40, 0x7F,
+ 0xF2, 0xBC, 0x6A, 0xE0, 0x8F, 0x0F, 0xE4, 0x3A,
+ 0xE1, 0x30, 0x84, 0x6E, 0x82, 0x8E, 0x56, 0xC5,
+ 0x32, 0x85, 0xFB, 0x59, 0x43, 0x41, 0xC2, 0xF6,
+ 0x67, 0x5A, 0x7C, 0x34, 0xA1, 0xD0, 0x4B, 0xAC,
+ 0x61, 0x72, 0x6B, 0xAF, 0xC4, 0x20, 0x9A, 0xD4,
+ 0x74, 0x8D, 0x87, 0x83, 0xE2, 0x62, 0x6D, 0xE6,
+ 0xE7, 0xF9, 0x76, 0xCB, 0x18, 0x90, 0x4F, 0xFF,
+ 0xD3, 0x3C, 0x08, 0x79, 0x93, 0x2D, 0x95, 0xA3,
+ 0xDD, 0x5B, 0xDA, 0x7A, 0x39, 0x4D, 0xC1, 0x2E,
+ 0xCC, 0x53, 0xE8, 0xA2, 0xCF, 0x15, 0x78, 0x1C,
+ 0xEB, 0x9B, 0x7B, 0xAD, 0x31, 0x2F, 0xE3, 0xC9,
+ 0x3B, 0xEC, 0x2C, 0x49, 0x02, 0x52, 0x28, 0xBA,
+ 0x0C, 0x19, 0x24, 0xF7, 0x97, 0x09, 0xA6, 0xA0,
+ 0xDF, 0xD1, 0xD2, 0xDC, 0x51, 0xA5, 0x94, 0xFD,
+ 0x71, 0xF5, 0x50, 0x0A, 0x69, 0x25, 0x88, 0x5C,
+ 0x91, 0xD5, 0x47, 0x0B, 0x27, 0x13, 0x96, 0xD9,
+ 0xF1, 0xA9, 0x70, 0xC3, 0xBE, 0x42, 0x4E, 0x4A,
+ 0xB1, 0x07, 0xA7, 0x54, 0xFE, 0x48, 0x9F, 0x63,
+ 0x17, 0xAE, 0xB9, 0x58, 0x21, 0x35, 0xED, 0x5D,
+ 0x9D, 0x3D, 0xB4, 0xFC, 0xEA, 0x8C, 0x80, 0xA8,
+ 0x1E, 0xB0, 0xDE, 0x0D, 0x11, 0x6F, 0x04, 0x12,
+ 0xF4, 0x10, 0x64, 0x0E, 0xD7, 0x2B, 0xB3, 0x8B,
+ 0xB7, 0x01, 0x86, 0xCA, 0xFA, 0x9E, 0xEE, 0x66,
+ 0x37, 0x65, 0x81, 0x38, 0x1F, 0xAA, 0x73, 0xAB,
+ 0xBD, 0xDB, 0x14, 0xCD, 0x00, 0xBB, 0x98, 0x44,
+ 0x45, 0xB6, 0x99, 0x5E, 0xD8, 0x1D, 0x36, 0xF8,
+ 0x55, 0x6C, 0x16, 0x7E, 0x77, 0x3F, 0x22, 0xEF,
+ 0xF3, 0x7D, 0xC6, 0xCE, 0x8A, 0xB2, 0x33, 0x4C,
+ 0x03, 0x05, 0xBF, 0x06, 0x1B, 0xC0, 0x1A, 0x60 };
+
+unsigned char table_35[256] = {
+ 0xCC, 0x40, 0xEF, 0x1F, 0xDB, 0xE5, 0x71, 0x51,
+ 0x3B, 0x0F, 0x7D, 0x9C, 0x83, 0x17, 0x6F, 0x8F,
+ 0x13, 0xDC, 0x7F, 0xA9, 0xA5, 0xA2, 0x9D, 0xDF,
+ 0xE7, 0x97, 0x2A, 0x30, 0xF2, 0x73, 0xCF, 0x87,
+ 0x29, 0xB3, 0x86, 0x43, 0x09, 0xB0, 0x2E, 0x10,
+ 0x8E, 0xBC, 0x57, 0xBA, 0x68, 0xF5, 0xCB, 0x89,
+ 0x32, 0xC1, 0x6B, 0x1E, 0xAC, 0xB2, 0x2D, 0x6A,
+ 0x50, 0xEB, 0x18, 0x06, 0xD8, 0xC7, 0x36, 0x31,
+ 0xC5, 0xAF, 0x12, 0x15, 0xB7, 0x37, 0x4E, 0x01,
+ 0x14, 0x21, 0x44, 0x5E, 0xF4, 0xB4, 0xE4, 0x65,
+ 0xFE, 0x8A, 0xEA, 0x0D, 0xBB, 0x45, 0x8B, 0x25,
+ 0x80, 0x35, 0x61, 0xA8, 0x4A, 0x47, 0xAB, 0x91,
+ 0x1B, 0x1C, 0x05, 0x4D, 0x5A, 0xD4, 0xF1, 0x9B,
+ 0x0E, 0x98, 0xCA, 0x96, 0x42, 0x7E, 0x03, 0x5F,
+ 0xE2, 0x90, 0xBF, 0x82, 0xC9, 0x3D, 0xE0, 0x5C,
+ 0xFA, 0x3E, 0x41, 0x11, 0x79, 0x58, 0x24, 0x2C,
+ 0xC0, 0x28, 0x5D, 0xA3, 0xDE, 0x67, 0xFF, 0xA4,
+ 0x63, 0xB1, 0x22, 0x04, 0xFD, 0x70, 0x39, 0x46,
+ 0xAA, 0x0A, 0x34, 0x6C, 0xD7, 0x92, 0xA1, 0x3C,
+ 0x19, 0xD5, 0xFC, 0xAD, 0x85, 0x07, 0x00, 0x23,
+ 0xF8, 0x69, 0x56, 0x53, 0x55, 0x7A, 0xB8, 0xC8,
+ 0xDA, 0xCE, 0xF3, 0x5B, 0x49, 0xE1, 0xBE, 0xEC,
+ 0x1A, 0x88, 0x02, 0xBD, 0xF7, 0x1D, 0x64, 0xA0,
+ 0x4F, 0xD9, 0xE3, 0x95, 0xC6, 0x48, 0x2B, 0xED,
+ 0x9A, 0x9E, 0x26, 0x6E, 0xD1, 0x94, 0xB9, 0x93,
+ 0xDD, 0xF6, 0xA6, 0xFB, 0xC2, 0xB6, 0x0C, 0xE9,
+ 0x77, 0xF9, 0xCD, 0x08, 0xEE, 0x3F, 0xE6, 0x75,
+ 0xD6, 0x84, 0x76, 0x8C, 0xF0, 0xAE, 0xD2, 0x78,
+ 0x2F, 0x4B, 0x16, 0x4C, 0x27, 0x81, 0x6D, 0x99,
+ 0x38, 0xD3, 0x54, 0x62, 0x74, 0x20, 0x60, 0xC3,
+ 0x7C, 0x8D, 0x72, 0x0B, 0x52, 0xE8, 0xA7, 0x3A,
+ 0x59, 0xC4, 0x9F, 0xD0, 0x66, 0x7B, 0x33, 0xB5 };
+
+unsigned char table_36[256] = {
+ 0xDB, 0x6F, 0xFE, 0xB3, 0x5C, 0x1F, 0xB8, 0xBF,
+ 0xA3, 0x71, 0x11, 0x56, 0x90, 0xE2, 0x63, 0x18,
+ 0x83, 0x51, 0x21, 0xEB, 0x66, 0x08, 0xA6, 0xA5,
+ 0x1C, 0xF5, 0x14, 0x24, 0x41, 0x33, 0xA7, 0xB5,
+ 0xC7, 0x79, 0x57, 0x50, 0x85, 0xE1, 0x6D, 0xF7,
+ 0x0E, 0xDE, 0x67, 0xAB, 0xA1, 0x0B, 0xD9, 0x4A,
+ 0xCA, 0x36, 0xEA, 0xDA, 0x16, 0xEF, 0x9F, 0x0A,
+ 0x09, 0x9A, 0x1D, 0xC5, 0xD7, 0x5F, 0x19, 0xDC,
+ 0x15, 0x06, 0xE8, 0x94, 0x0C, 0x0D, 0xC9, 0x7C,
+ 0xD6, 0x62, 0xBB, 0x49, 0xF9, 0x61, 0x07, 0x9B,
+ 0x28, 0xC3, 0x9E, 0xF4, 0x38, 0x78, 0x20, 0x03,
+ 0xA2, 0x7F, 0xC2, 0x9D, 0x5E, 0x65, 0x52, 0x17,
+ 0x2E, 0x1B, 0xB0, 0x42, 0xBC, 0xFD, 0xF1, 0xD2,
+ 0xF6, 0x60, 0xD3, 0x29, 0x97, 0x3D, 0x0F, 0xB1,
+ 0x2F, 0x22, 0xDD, 0x80, 0x32, 0xF8, 0xAD, 0x70,
+ 0xB9, 0x8F, 0x37, 0xCE, 0x46, 0x58, 0xB7, 0x30,
+ 0xED, 0x7A, 0xE9, 0xC0, 0x7D, 0x13, 0x64, 0x23,
+ 0x4E, 0xC8, 0xF0, 0xCC, 0x3B, 0x45, 0x68, 0x8D,
+ 0xBE, 0x8B, 0xD8, 0x43, 0x02, 0x27, 0xE4, 0xAA,
+ 0x10, 0xF2, 0x59, 0x72, 0x40, 0x26, 0x69, 0xE5,
+ 0x05, 0x84, 0x4F, 0xE0, 0x6B, 0xC1, 0xAC, 0x4C,
+ 0xFB, 0x31, 0x77, 0x8E, 0xD4, 0x12, 0xA9, 0xB4,
+ 0xEC, 0x00, 0x76, 0x1E, 0x25, 0xAE, 0xE7, 0x3C,
+ 0x35, 0x93, 0x9C, 0xC4, 0xFC, 0x2D, 0x91, 0x04,
+ 0xAF, 0x53, 0x3F, 0xE6, 0xA4, 0xD0, 0x1A, 0xDF,
+ 0x3A, 0x55, 0x99, 0x01, 0xCB, 0x6C, 0x82, 0x3E,
+ 0x5D, 0xA8, 0x88, 0x54, 0x5B, 0x95, 0xCD, 0x8C,
+ 0x81, 0x34, 0xD1, 0x39, 0xFF, 0xEE, 0xFA, 0x8A,
+ 0x6E, 0x86, 0x92, 0x89, 0xF3, 0x6A, 0xBA, 0x2C,
+ 0xD5, 0x44, 0xC6, 0x96, 0xBD, 0xB2, 0x2B, 0x87,
+ 0x74, 0xA0, 0x73, 0x5A, 0x2A, 0x98, 0x75, 0x47,
+ 0x4B, 0xB6, 0x7B, 0x4D, 0xCF, 0x7E, 0x48, 0xE3 };
+
+unsigned char table_37[256] = {
+ 0x1F, 0xD6, 0xB1, 0xB3, 0x40, 0xAD, 0xDE, 0xB7,
+ 0x19, 0xB4, 0xE7, 0x0B, 0x9C, 0x2D, 0xE0, 0xF5,
+ 0xCF, 0x2C, 0x30, 0x65, 0x2F, 0xCD, 0x02, 0x91,
+ 0xCE, 0x2B, 0xBF, 0x78, 0xE6, 0xFA, 0x51, 0x48,
+ 0xFB, 0x4D, 0xBE, 0x71, 0x1A, 0x56, 0xFD, 0x81,
+ 0x33, 0x75, 0x89, 0x96, 0x37, 0x82, 0x9E, 0x93,
+ 0x41, 0x18, 0x5B, 0x2E, 0x22, 0x0F, 0xAF, 0x4B,
+ 0xB9, 0xD5, 0xEE, 0x6C, 0xE4, 0x05, 0xCC, 0x99,
+ 0xE5, 0x3B, 0x62, 0xBD, 0x7B, 0xAA, 0x4A, 0xE2,
+ 0x34, 0x43, 0xF7, 0x39, 0xFE, 0x14, 0x1D, 0xE3,
+ 0xF0, 0xA7, 0x77, 0xDF, 0xA0, 0xD3, 0xAC, 0xD9,
+ 0xEA, 0x76, 0xDD, 0xA4, 0xC5, 0xC9, 0x61, 0xF3,
+ 0xA8, 0xB0, 0x35, 0xE8, 0x68, 0xD4, 0x15, 0xF9,
+ 0x97, 0xED, 0x25, 0x0A, 0x88, 0x8F, 0x06, 0xA3,
+ 0x16, 0x36, 0x32, 0xA2, 0xC6, 0x64, 0xD7, 0x94,
+ 0xD2, 0x6D, 0x74, 0xFC, 0x44, 0x27, 0x5C, 0xFF,
+ 0x60, 0x1E, 0x58, 0x8B, 0x5E, 0xC7, 0x90, 0x17,
+ 0x63, 0xAE, 0xC3, 0x12, 0x13, 0x84, 0xEC, 0x49,
+ 0xA5, 0x9B, 0x31, 0x8D, 0xE1, 0x79, 0xF1, 0x00,
+ 0x28, 0x3D, 0xC2, 0x55, 0x20, 0x52, 0x95, 0x7E,
+ 0x42, 0x1C, 0x66, 0x92, 0x7D, 0xB6, 0xC4, 0xF4,
+ 0x80, 0xB2, 0x72, 0x6E, 0x11, 0xF6, 0x0D, 0x5A,
+ 0xEF, 0x9D, 0x69, 0x9A, 0x45, 0x67, 0x3F, 0xDA,
+ 0x8E, 0x57, 0x09, 0x7C, 0x38, 0xA6, 0x83, 0x87,
+ 0x7A, 0x08, 0x4C, 0x5F, 0x85, 0x7F, 0xD0, 0x04,
+ 0x50, 0xCB, 0xB8, 0x07, 0x24, 0x26, 0x29, 0x46,
+ 0x01, 0x03, 0xC1, 0xD8, 0xDC, 0x0E, 0x3C, 0x4F,
+ 0x53, 0x4E, 0xB5, 0xF8, 0xC0, 0x8A, 0xF2, 0xBB,
+ 0xE9, 0x5D, 0x2A, 0xBA, 0x0C, 0x1B, 0x3A, 0xA9,
+ 0x21, 0x6A, 0x70, 0xBC, 0xEB, 0xA1, 0x54, 0x10,
+ 0x98, 0x9F, 0x23, 0xD1, 0x6B, 0x59, 0x3E, 0xCA,
+ 0x73, 0xC8, 0x86, 0x47, 0xDB, 0xAB, 0x6F, 0x8C };
+
+unsigned char table_38[256] = {
+ 0xAA, 0x8D, 0x37, 0x94, 0x99, 0xDD, 0x70, 0x77,
+ 0x78, 0xC9, 0x0F, 0xFA, 0xE2, 0x05, 0xC2, 0x16,
+ 0x02, 0x4D, 0x44, 0x65, 0xAC, 0xB0, 0x39, 0xF8,
+ 0x06, 0x60, 0xD8, 0xE1, 0x19, 0xB4, 0x36, 0x20,
+ 0x59, 0x1D, 0xAD, 0xE4, 0xE8, 0xFF, 0x9D, 0x0D,
+ 0x51, 0x28, 0xE7, 0x8C, 0x0E, 0x97, 0xE3, 0xAE,
+ 0x6A, 0x27, 0x98, 0xDB, 0x26, 0xF6, 0xEC, 0xC6,
+ 0xC0, 0xBD, 0x68, 0x61, 0x83, 0x86, 0xE0, 0x2C,
+ 0xEE, 0x47, 0xF9, 0x5F, 0x6D, 0xBA, 0xE9, 0x72,
+ 0x8A, 0xBB, 0x08, 0x29, 0xAF, 0x1C, 0xD3, 0x5D,
+ 0xF7, 0x87, 0x6F, 0x9A, 0x2F, 0x11, 0xD9, 0x90,
+ 0x66, 0x8E, 0xEB, 0xB1, 0x2E, 0xEA, 0xA3, 0x55,
+ 0x2B, 0xCC, 0x4C, 0x4B, 0x48, 0x71, 0x3B, 0xFC,
+ 0xA4, 0x45, 0x0A, 0x8F, 0x7A, 0x13, 0x01, 0x22,
+ 0xC1, 0xF1, 0xA2, 0xB8, 0x7C, 0xF4, 0xB3, 0xB7,
+ 0x5B, 0xE5, 0x07, 0x50, 0x7E, 0x18, 0xEF, 0x91,
+ 0x5C, 0x15, 0x69, 0xBE, 0x0C, 0x93, 0x56, 0x35,
+ 0x7B, 0xCF, 0x34, 0x74, 0x3E, 0x5E, 0x31, 0x21,
+ 0x12, 0x63, 0x7F, 0x2A, 0x9B, 0xD4, 0x6B, 0xBC,
+ 0x33, 0x62, 0x30, 0x75, 0x17, 0x23, 0xB2, 0xF0,
+ 0x57, 0x67, 0x95, 0x3D, 0xCD, 0x10, 0xE6, 0xC8,
+ 0x8B, 0xA9, 0x73, 0xC4, 0x43, 0xBF, 0xA7, 0xCA,
+ 0xB5, 0xD5, 0xD6, 0x3F, 0x1A, 0x7D, 0x82, 0xA8,
+ 0x40, 0x64, 0xAB, 0x04, 0xC3, 0x1F, 0xA0, 0x5A,
+ 0x85, 0xF3, 0xDE, 0xFE, 0xDA, 0x1E, 0x81, 0x92,
+ 0x9C, 0x2D, 0x9F, 0x32, 0xB9, 0xA1, 0x96, 0xD0,
+ 0x4F, 0x38, 0x80, 0xCB, 0x6C, 0x14, 0x84, 0x1B,
+ 0xD7, 0xC5, 0xED, 0xD2, 0x3A, 0x0B, 0x88, 0xFD,
+ 0xDC, 0x49, 0x9E, 0xF5, 0xF2, 0x52, 0xA6, 0x24,
+ 0xC7, 0xB6, 0x03, 0x3C, 0xD1, 0x54, 0x41, 0xDF,
+ 0x89, 0x58, 0x79, 0xFB, 0x6E, 0xA5, 0x42, 0x25,
+ 0x09, 0x76, 0x00, 0x46, 0x4E, 0x53, 0xCE, 0x4A };
+
+unsigned char table_39[32] = {
+ 0x12, 0x18, 0x0E, 0x08, 0x16, 0x05, 0x06, 0x00,
+ 0x11, 0x17, 0x15, 0x1B, 0x14, 0x01, 0x1F, 0x19,
+ 0x04, 0x0D, 0x0A, 0x0F, 0x10, 0x07, 0x1D, 0x03,
+ 0x0B, 0x13, 0x0C, 0x09, 0x1E, 0x02, 0x1A, 0x1C };
+
+unsigned char table_40[32] = {
+ 0x16, 0x02, 0x06, 0x0E, 0x0D, 0x1C, 0x08, 0x0A,
+ 0x0F, 0x13, 0x0B, 0x18, 0x07, 0x04, 0x14, 0x01,
+ 0x1B, 0x05, 0x17, 0x1E, 0x11, 0x1A, 0x10, 0x1F,
+ 0x12, 0x19, 0x1D, 0x03, 0x0C, 0x00, 0x09, 0x15 };
+
+unsigned char table_41[32] = {
+ 0x13, 0x18, 0x04, 0x1F, 0x1D, 0x11, 0x03, 0x00,
+ 0x10, 0x12, 0x06, 0x0A, 0x1C, 0x07, 0x15, 0x0E,
+ 0x08, 0x05, 0x0C, 0x09, 0x01, 0x02, 0x16, 0x0B,
+ 0x1A, 0x17, 0x14, 0x1E, 0x0D, 0x0F, 0x19, 0x1B };
+
+unsigned char table_42[32] = {
+ 0x00, 0x08, 0x15, 0x1D, 0x05, 0x18, 0x06, 0x07,
+ 0x1F, 0x01, 0x0B, 0x03, 0x19, 0x13, 0x02, 0x1C,
+ 0x17, 0x11, 0x0E, 0x1E, 0x0C, 0x0F, 0x09, 0x1A,
+ 0x1B, 0x16, 0x10, 0x0D, 0x0A, 0x14, 0x12, 0x04 };
+
+unsigned char table_43[256] = {
+ 0x34, 0xB7, 0x36, 0x85, 0x5F, 0x93, 0x98, 0x70,
+ 0x1E, 0x59, 0x83, 0x60, 0x6F, 0xBF, 0xF9, 0xD0,
+ 0xB3, 0x22, 0x12, 0x38, 0xF5, 0x01, 0xC9, 0x5B,
+ 0xEF, 0x1D, 0x81, 0x64, 0xFA, 0x8F, 0x7F, 0xBC,
+ 0x05, 0x08, 0xE0, 0x8B, 0xE8, 0x86, 0x95, 0xCB,
+ 0xCA, 0x5A, 0xEB, 0x10, 0x92, 0xE2, 0x7E, 0x28,
+ 0xD9, 0xC7, 0x0D, 0x24, 0xA7, 0x02, 0x0B, 0xF1,
+ 0x7B, 0xD3, 0xFE, 0x2B, 0x89, 0x0E, 0xAE, 0xAD,
+ 0xC8, 0x82, 0x79, 0x43, 0x96, 0xDE, 0x0C, 0x9A,
+ 0x57, 0x84, 0xB4, 0x19, 0xF8, 0xF0, 0xAF, 0xBE,
+ 0x99, 0x9F, 0x46, 0xE4, 0x31, 0xDF, 0x30, 0x51,
+ 0xD4, 0xE5, 0xFC, 0x32, 0x04, 0x56, 0x7D, 0x33,
+ 0xF7, 0x18, 0x23, 0x4E, 0xC2, 0x7C, 0x6C, 0xD2,
+ 0xB1, 0x9B, 0x40, 0xA2, 0x88, 0x00, 0xA1, 0xAB,
+ 0xC6, 0x5C, 0x87, 0x3B, 0xD7, 0x27, 0x2E, 0x45,
+ 0xDA, 0x8E, 0x61, 0x5E, 0xFB, 0x09, 0x5D, 0x6B,
+ 0xA3, 0x29, 0x4F, 0xAC, 0xD1, 0x77, 0x4A, 0xA9,
+ 0xC4, 0x7A, 0x15, 0xD8, 0xAA, 0x17, 0xB9, 0x2D,
+ 0xE7, 0xBD, 0x2C, 0x62, 0x2F, 0xB2, 0xED, 0x3F,
+ 0x48, 0x26, 0x1B, 0x35, 0x20, 0x72, 0x4D, 0xFF,
+ 0xBB, 0x78, 0x1F, 0xCC, 0xEC, 0xA8, 0x9D, 0x90,
+ 0x4B, 0x13, 0xE1, 0xBA, 0xF3, 0x3C, 0x42, 0x65,
+ 0x14, 0xDD, 0x75, 0xE3, 0x4C, 0x74, 0x94, 0xCD,
+ 0xF2, 0x66, 0x06, 0xE9, 0x49, 0xB8, 0x71, 0x41,
+ 0xA0, 0x25, 0x55, 0x47, 0x97, 0x9E, 0x11, 0x54,
+ 0x1A, 0xB0, 0x3E, 0x37, 0x39, 0x1C, 0x8D, 0x03,
+ 0x6E, 0xF6, 0x80, 0x6D, 0x8C, 0x9C, 0xB6, 0xCF,
+ 0xC3, 0x91, 0x63, 0xC0, 0x07, 0x67, 0xE6, 0xF4,
+ 0xCE, 0x3D, 0xDB, 0x16, 0xFD, 0xEA, 0xD6, 0x68,
+ 0xD5, 0xA6, 0x0F, 0x58, 0x44, 0x52, 0xB5, 0xDC,
+ 0x0A, 0x69, 0xC5, 0xA5, 0xC1, 0x8A, 0x2A, 0xEE,
+ 0x73, 0x76, 0x3A, 0x21, 0x53, 0xA4, 0x50, 0x6A };
+
+unsigned char table_44[32] = {
+ 0x1A, 0x0E, 0x0A, 0x17, 0x1F, 0x08, 0x10, 0x14,
+ 0x0C, 0x0F, 0x09, 0x1C, 0x06, 0x18, 0x1E, 0x12,
+ 0x15, 0x00, 0x11, 0x13, 0x0D, 0x01, 0x0B, 0x03,
+ 0x16, 0x19, 0x05, 0x1D, 0x02, 0x07, 0x04, 0x1B };
+
+unsigned char table_45[256] = {
+ 0x5E, 0xD6, 0xE2, 0x54, 0x35, 0xC2, 0xAC, 0x9D,
+ 0x92, 0x64, 0x57, 0x65, 0xC8, 0xAE, 0x21, 0xA9,
+ 0x89, 0x48, 0x12, 0x59, 0xEC, 0xEF, 0x9F, 0xF7,
+ 0x19, 0x03, 0x83, 0xC0, 0x79, 0x5D, 0x4A, 0x10,
+ 0x8C, 0xEB, 0xFF, 0xB5, 0x3B, 0x51, 0x2D, 0xD1,
+ 0x6B, 0xC5, 0x24, 0x5C, 0xE6, 0x11, 0x94, 0x3F,
+ 0xD0, 0x2F, 0x0E, 0x95, 0x3C, 0xFE, 0x5B, 0x20,
+ 0x23, 0xE0, 0x91, 0x6F, 0xCA, 0x56, 0x0C, 0x73,
+ 0xDA, 0x67, 0x37, 0xA3, 0xA5, 0x70, 0x93, 0x1C,
+ 0x18, 0xD9, 0x42, 0x5F, 0x44, 0xF0, 0xF2, 0x14,
+ 0x58, 0x8A, 0x1D, 0x40, 0x4E, 0x0B, 0x74, 0x84,
+ 0x52, 0xCB, 0x60, 0xED, 0xAD, 0x66, 0x43, 0x6C,
+ 0x81, 0xA1, 0x27, 0xB9, 0xBA, 0x4D, 0xF5, 0x04,
+ 0xB8, 0x96, 0xA6, 0xA2, 0x7D, 0xD4, 0xEA, 0x45,
+ 0x4F, 0x55, 0xD3, 0x3E, 0x8E, 0x4C, 0xBF, 0x8B,
+ 0x9A, 0x06, 0x7A, 0xF4, 0x02, 0x88, 0x80, 0x22,
+ 0xF3, 0xBD, 0x78, 0xEE, 0xAF, 0xF8, 0x15, 0x09,
+ 0x0F, 0xB0, 0xDD, 0x99, 0x72, 0xE7, 0x90, 0xE1,
+ 0x25, 0x62, 0x8D, 0x9C, 0x13, 0x08, 0xC9, 0x28,
+ 0x2A, 0x47, 0x69, 0xDE, 0x77, 0x87, 0xBB, 0xE9,
+ 0xAA, 0x33, 0x05, 0x29, 0x34, 0x97, 0xFD, 0xA0,
+ 0x1E, 0xFC, 0xBE, 0xB1, 0x71, 0x9B, 0x50, 0xDC,
+ 0xB7, 0x31, 0x63, 0x3A, 0xDF, 0xC3, 0x1B, 0x7C,
+ 0x0A, 0xD7, 0xF6, 0xDB, 0x49, 0x53, 0x7F, 0xD2,
+ 0x30, 0xA4, 0xB3, 0x6E, 0xB2, 0x6D, 0xCD, 0x7E,
+ 0x26, 0xE8, 0x76, 0xCF, 0xE5, 0xCE, 0x16, 0xF1,
+ 0xC6, 0x68, 0x36, 0x46, 0x1F, 0x38, 0x0D, 0x41,
+ 0x17, 0xBC, 0x86, 0x9E, 0x6A, 0x7B, 0xB4, 0x01,
+ 0xCC, 0x2C, 0xE3, 0x5A, 0xB6, 0xFA, 0x00, 0x75,
+ 0x39, 0xA7, 0xC1, 0xD5, 0x98, 0xAB, 0x1A, 0x85,
+ 0xD8, 0xE4, 0xC4, 0xA8, 0x4B, 0x61, 0x2E, 0x3D,
+ 0xF9, 0x2B, 0x32, 0x8F, 0xFB, 0xC7, 0x07, 0x82 };
+
+unsigned char table_46[256] = {
+ 0x85, 0x78, 0xFE, 0x6C, 0x61, 0xA0, 0x71, 0xCC,
+ 0x45, 0x54, 0x7A, 0xE6, 0x82, 0x1D, 0xA6, 0x02,
+ 0x47, 0xD0, 0x23, 0x55, 0x62, 0xFA, 0x76, 0x3E,
+ 0xE3, 0x66, 0x74, 0x10, 0x5D, 0x49, 0x69, 0x0B,
+ 0x75, 0x12, 0x8D, 0x9F, 0xEE, 0x93, 0x50, 0x70,
+ 0x32, 0xBC, 0x1E, 0xD3, 0xEF, 0x7B, 0xB4, 0x92,
+ 0xFD, 0x16, 0xC2, 0xD8, 0xDE, 0x68, 0xD1, 0x64,
+ 0xC3, 0xA3, 0xB3, 0xC9, 0x08, 0xFB, 0x84, 0xC1,
+ 0x28, 0x53, 0xCF, 0xD2, 0x35, 0xD7, 0x4A, 0x01,
+ 0x44, 0xA4, 0x07, 0xAC, 0x98, 0xF1, 0xB2, 0x9A,
+ 0x94, 0x2D, 0xD4, 0x34, 0x27, 0x60, 0x1A, 0xB9,
+ 0xAF, 0x89, 0xEB, 0x8F, 0x6A, 0x13, 0x05, 0xF0,
+ 0x77, 0x5F, 0x4F, 0x58, 0x2C, 0xE7, 0xCE, 0xED,
+ 0xC0, 0x0D, 0x3A, 0xA7, 0xE2, 0x38, 0x5B, 0xE9,
+ 0x3D, 0xF2, 0xDF, 0x86, 0xE0, 0x72, 0xF7, 0x88,
+ 0xAD, 0xB7, 0x11, 0xDB, 0x73, 0x87, 0xC5, 0x22,
+ 0xE1, 0x5C, 0xD6, 0x57, 0x7E, 0x7D, 0xA2, 0xF9,
+ 0xF5, 0x9C, 0x25, 0x6F, 0x26, 0x51, 0xC8, 0x80,
+ 0x2B, 0xA8, 0x19, 0xD9, 0x65, 0xCD, 0x97, 0xEA,
+ 0xFF, 0x5E, 0x24, 0x3B, 0x4D, 0xB1, 0x1C, 0x79,
+ 0x39, 0x6B, 0xA5, 0x2A, 0x09, 0xCA, 0x04, 0xEC,
+ 0xBA, 0x18, 0x31, 0x46, 0x20, 0xBE, 0x1F, 0x3C,
+ 0x6D, 0xAA, 0xF6, 0xDD, 0xF4, 0x96, 0x03, 0x0A,
+ 0x9E, 0x83, 0xA1, 0x9D, 0xD5, 0xB0, 0x17, 0xBF,
+ 0x56, 0xAB, 0xAE, 0x1B, 0x52, 0xC6, 0x81, 0x4B,
+ 0xDC, 0x90, 0x5A, 0x9B, 0xB6, 0x0F, 0xF3, 0x67,
+ 0x30, 0x63, 0x7C, 0x40, 0x0E, 0x7F, 0x95, 0x36,
+ 0xC4, 0x4E, 0x43, 0xCB, 0x15, 0xB8, 0x00, 0x91,
+ 0x8A, 0x4C, 0x8E, 0x14, 0x06, 0x6E, 0xA9, 0x2E,
+ 0x3F, 0x48, 0x2F, 0x0C, 0xB5, 0x21, 0xBB, 0xDA,
+ 0x8B, 0x42, 0x29, 0x8C, 0x33, 0x59, 0xE8, 0xF8,
+ 0xC7, 0xE4, 0x37, 0xE5, 0xFC, 0xBD, 0x99, 0x41 };
+
+unsigned char table_47[32] = {
+ 0x18, 0x1D, 0x16, 0x10, 0x11, 0x04, 0x1E, 0x08,
+ 0x19, 0x0E, 0x0F, 0x02, 0x14, 0x1C, 0x07, 0x17,
+ 0x0D, 0x09, 0x12, 0x1A, 0x05, 0x01, 0x0B, 0x0A,
+ 0x13, 0x15, 0x0C, 0x00, 0x06, 0x1F, 0x03, 0x1B };
+
+unsigned char table_48[32] = {
+ 0x13, 0x08, 0x15, 0x01, 0x17, 0x10, 0x0F, 0x1F,
+ 0x1D, 0x0D, 0x12, 0x03, 0x06, 0x0A, 0x1C, 0x19,
+ 0x1A, 0x04, 0x1B, 0x02, 0x16, 0x1E, 0x11, 0x00,
+ 0x14, 0x09, 0x0C, 0x18, 0x05, 0x07, 0x0E, 0x0B };
+
+unsigned char table_49[32] = {
+ 0x1F, 0x0F, 0x19, 0x07, 0x18, 0x05, 0x1E, 0x1D,
+ 0x15, 0x08, 0x17, 0x10, 0x0A, 0x0E, 0x0C, 0x1B,
+ 0x02, 0x13, 0x03, 0x0D, 0x04, 0x1A, 0x06, 0x09,
+ 0x12, 0x1C, 0x0B, 0x16, 0x14, 0x01, 0x11, 0x00 };
+
+unsigned char table_50[32] = {
+ 0x16, 0x18, 0x1C, 0x0E, 0x12, 0x00, 0x04, 0x1B,
+ 0x1F, 0x13, 0x17, 0x0A, 0x1E, 0x03, 0x0C, 0x01,
+ 0x0F, 0x10, 0x02, 0x08, 0x14, 0x09, 0x19, 0x15,
+ 0x06, 0x0D, 0x0B, 0x1D, 0x05, 0x07, 0x11, 0x1A };
+
+unsigned char table_51[32] = {
+ 0x1C, 0x0D, 0x1B, 0x07, 0x17, 0x0E, 0x06, 0x01,
+ 0x12, 0x19, 0x03, 0x0B, 0x10, 0x08, 0x00, 0x1E,
+ 0x0A, 0x04, 0x1A, 0x1D, 0x0C, 0x18, 0x02, 0x13,
+ 0x0F, 0x11, 0x05, 0x09, 0x15, 0x16, 0x1F, 0x14 };
+
+unsigned char table_52[256] = {
+ 0x34, 0x0B, 0x47, 0xA3, 0x56, 0x30, 0x73, 0xD4,
+ 0x4B, 0xF6, 0xA6, 0x80, 0x22, 0x95, 0xA5, 0xBB,
+ 0xFE, 0xCD, 0x27, 0x88, 0x87, 0x18, 0x86, 0x6E,
+ 0xB9, 0x07, 0x37, 0x52, 0x0A, 0x28, 0x2C, 0xC4,
+ 0x75, 0xA1, 0x29, 0x54, 0x84, 0x08, 0x72, 0x51,
+ 0xDD, 0xF1, 0x4E, 0x1A, 0x90, 0x57, 0x20, 0xAD,
+ 0x68, 0x61, 0xAF, 0x50, 0x6B, 0x1B, 0x71, 0xEB,
+ 0x63, 0xC9, 0xB0, 0x58, 0x26, 0x40, 0xC7, 0xD9,
+ 0x70, 0xA2, 0x9A, 0x09, 0x3F, 0x92, 0x0D, 0x8C,
+ 0xC1, 0x96, 0x9F, 0x77, 0x4D, 0x5A, 0xEA, 0x11,
+ 0xD7, 0xF3, 0x33, 0x93, 0x10, 0xF2, 0x9D, 0x83,
+ 0xFF, 0x7E, 0xD2, 0x41, 0x24, 0xB4, 0x8D, 0x5C,
+ 0xCF, 0xEF, 0xE9, 0x64, 0x76, 0xD1, 0xDE, 0xE4,
+ 0x91, 0x35, 0x89, 0x19, 0x02, 0x0E, 0xF4, 0x2A,
+ 0x0F, 0xE1, 0xA8, 0x2D, 0x21, 0x23, 0xAA, 0x7C,
+ 0x78, 0x45, 0xA9, 0xDC, 0x06, 0xF9, 0xDF, 0xF7,
+ 0x03, 0xAB, 0xB5, 0x1C, 0x36, 0x7B, 0x97, 0xFA,
+ 0xE5, 0x3B, 0x2F, 0x1F, 0x9E, 0xED, 0xA7, 0x55,
+ 0x42, 0x6F, 0x1E, 0xB7, 0xE6, 0xFB, 0x12, 0xD5,
+ 0x99, 0xC6, 0x66, 0x4A, 0xE8, 0x48, 0x60, 0xB1,
+ 0x05, 0x53, 0x8A, 0xB6, 0x25, 0x8F, 0xA4, 0xD8,
+ 0x9C, 0xC0, 0x59, 0x3A, 0xBD, 0xDB, 0x44, 0x5E,
+ 0xE3, 0xDA, 0x1D, 0x32, 0xF5, 0xBA, 0x43, 0x13,
+ 0x82, 0x4C, 0xE7, 0x17, 0x15, 0x3E, 0x69, 0x2E,
+ 0xC3, 0xF0, 0x5F, 0xFD, 0xCE, 0xD3, 0xCA, 0x39,
+ 0xD6, 0x79, 0x3D, 0xC8, 0x67, 0x8B, 0x31, 0x4F,
+ 0xB3, 0xBC, 0x65, 0x00, 0x7A, 0x98, 0xC5, 0x6C,
+ 0x2B, 0x94, 0x6D, 0x74, 0x14, 0xAC, 0xCC, 0xA0,
+ 0x5B, 0xF8, 0xCB, 0x7F, 0xB2, 0xEC, 0xBF, 0x3C,
+ 0xE0, 0xAE, 0xFC, 0x62, 0x04, 0x8E, 0x85, 0x49,
+ 0x9B, 0xC2, 0x38, 0xD0, 0xEE, 0x81, 0x46, 0xE2,
+ 0x01, 0x0C, 0x5D, 0x7D, 0xB8, 0xBE, 0x6A, 0x16 };
+
+unsigned char table_53[256] = {
+ 0xE3, 0xF4, 0x8D, 0x72, 0x45, 0x32, 0x9D, 0xCE,
+ 0x1F, 0x6B, 0xBC, 0xDC, 0xF1, 0xEC, 0x5A, 0x3B,
+ 0xA5, 0xA2, 0x2B, 0xDD, 0x8A, 0xA3, 0x76, 0xE4,
+ 0xAF, 0xE9, 0xE1, 0x21, 0xDB, 0x9F, 0x19, 0xD3,
+ 0x26, 0x80, 0x15, 0xC2, 0x46, 0xB8, 0x17, 0x56,
+ 0x99, 0x81, 0x08, 0xD7, 0xEF, 0x8E, 0x04, 0x05,
+ 0x97, 0x2F, 0x78, 0xAD, 0xA1, 0x52, 0x36, 0x58,
+ 0x53, 0x68, 0x22, 0x70, 0x0B, 0x79, 0xE6, 0xFA,
+ 0xC3, 0x91, 0xE2, 0xF7, 0xF6, 0x75, 0x2D, 0x0A,
+ 0x90, 0xEB, 0xA6, 0x35, 0xA7, 0x10, 0xB5, 0xFB,
+ 0xE7, 0xAA, 0x1E, 0x43, 0xBB, 0x3C, 0x65, 0x25,
+ 0x2C, 0x59, 0x62, 0x2A, 0xF9, 0x4B, 0x95, 0x5E,
+ 0x20, 0x11, 0x42, 0x27, 0x44, 0xE8, 0x14, 0x6F,
+ 0xD1, 0xD8, 0x00, 0x3A, 0x5B, 0x18, 0x89, 0x02,
+ 0x61, 0xD6, 0xC5, 0x98, 0xD0, 0x5F, 0x34, 0x29,
+ 0xFD, 0x31, 0x1A, 0xCD, 0x0F, 0x9E, 0xCA, 0x7B,
+ 0xEA, 0x93, 0x71, 0x5C, 0x0E, 0x57, 0x33, 0xC4,
+ 0x37, 0xF5, 0x83, 0xB0, 0xDF, 0x49, 0x74, 0x54,
+ 0x1D, 0x24, 0xB9, 0x16, 0x1C, 0x28, 0xDE, 0x4A,
+ 0xF0, 0x01, 0x86, 0x82, 0xCC, 0x12, 0x8C, 0x06,
+ 0x30, 0xA8, 0x7A, 0x73, 0x66, 0x7C, 0xC6, 0xB6,
+ 0xF2, 0x13, 0xBF, 0x40, 0x85, 0x77, 0x09, 0x3D,
+ 0x67, 0x63, 0x3F, 0x7F, 0xF3, 0x87, 0x8F, 0xFF,
+ 0x92, 0xC7, 0x4C, 0x23, 0xBA, 0xCB, 0xB1, 0xED,
+ 0x0C, 0x60, 0x47, 0xFE, 0x38, 0x5D, 0xCF, 0x8B,
+ 0x4D, 0xA9, 0x2E, 0xE5, 0xA4, 0x1B, 0x88, 0x3E,
+ 0x7D, 0xF8, 0xC0, 0xD5, 0x6D, 0x6C, 0x48, 0xAC,
+ 0x9B, 0x51, 0x7E, 0x6E, 0x50, 0x0D, 0x9A, 0xB3,
+ 0xEE, 0x07, 0x4F, 0x69, 0x9C, 0x03, 0xD9, 0xD4,
+ 0xB4, 0xD2, 0xAE, 0x4E, 0x55, 0xB7, 0xC9, 0x41,
+ 0x39, 0x6A, 0xC8, 0xA0, 0xB2, 0xC1, 0x84, 0xFC,
+ 0xAB, 0x64, 0xE0, 0xBE, 0xDA, 0xBD, 0x96, 0x94 };
+
+unsigned char table_54[32] = {
+ 0x01, 0x02, 0x1D, 0x10, 0x0E, 0x11, 0x08, 0x14,
+ 0x12, 0x09, 0x15, 0x17, 0x16, 0x04, 0x06, 0x1B,
+ 0x07, 0x1A, 0x18, 0x13, 0x0A, 0x1E, 0x1C, 0x1F,
+ 0x0C, 0x0B, 0x0D, 0x05, 0x0F, 0x00, 0x19, 0x03 };
+
+unsigned char table_55[32] = {
+ 0x01, 0x12, 0x13, 0x09, 0x0B, 0x19, 0x03, 0x0E,
+ 0x02, 0x1F, 0x1D, 0x1B, 0x1E, 0x11, 0x06, 0x05,
+ 0x00, 0x16, 0x07, 0x0C, 0x15, 0x0D, 0x1A, 0x08,
+ 0x18, 0x10, 0x0F, 0x17, 0x1C, 0x0A, 0x04, 0x14 };
+
+unsigned char table_56[256] = {
+ 0xEF, 0x06, 0x5F, 0x11, 0x4B, 0x60, 0x13, 0xBB,
+ 0x79, 0xD7, 0xE4, 0x6D, 0x22, 0xB4, 0x15, 0x50,
+ 0x29, 0x17, 0xD2, 0xE3, 0x37, 0x8C, 0x46, 0x7C,
+ 0xA2, 0xF5, 0x65, 0x16, 0xCB, 0x04, 0x3E, 0xDF,
+ 0x8E, 0xDE, 0x53, 0xF1, 0xF4, 0xD1, 0x3B, 0xEE,
+ 0x9A, 0x09, 0x9B, 0x6C, 0xF6, 0xCC, 0xFB, 0x40,
+ 0xE0, 0xFD, 0x2B, 0x1D, 0x73, 0x18, 0xCD, 0x31,
+ 0x3F, 0x9E, 0xAD, 0xC9, 0x43, 0x4E, 0x99, 0x3A,
+ 0x8F, 0x92, 0x85, 0xFC, 0x12, 0x41, 0x20, 0xE8,
+ 0x2A, 0xC0, 0x1C, 0x38, 0x74, 0x0B, 0xF3, 0x05,
+ 0x0D, 0x1F, 0x94, 0x9C, 0xAC, 0x00, 0x59, 0x0C,
+ 0xB3, 0x8D, 0xA8, 0x75, 0xB7, 0x68, 0x2F, 0x27,
+ 0x6F, 0x69, 0x76, 0xD8, 0xEC, 0xA5, 0xB2, 0x6A,
+ 0x19, 0x72, 0x1A, 0xB6, 0xE5, 0x77, 0xC6, 0x44,
+ 0x9D, 0xCA, 0x82, 0x35, 0x36, 0x5E, 0xA9, 0x25,
+ 0xFA, 0x5C, 0x24, 0x30, 0x39, 0x0E, 0x2C, 0x7D,
+ 0xE6, 0x88, 0xA0, 0x63, 0xB8, 0x6B, 0x01, 0xDD,
+ 0xDA, 0x9F, 0x45, 0x83, 0xE2, 0x7F, 0x1B, 0x56,
+ 0xAF, 0x14, 0xC3, 0x49, 0xBF, 0x78, 0x70, 0x58,
+ 0x23, 0xA3, 0xBD, 0x34, 0x47, 0x2D, 0x0A, 0xD4,
+ 0x33, 0x03, 0x1E, 0xC1, 0x87, 0xAE, 0x3C, 0x95,
+ 0xB0, 0x42, 0x91, 0xB9, 0x5A, 0x61, 0xAA, 0xCF,
+ 0xF2, 0x51, 0xA6, 0xF8, 0xDC, 0x71, 0xAB, 0x48,
+ 0x66, 0x90, 0x97, 0xC4, 0x08, 0xF9, 0xD0, 0x7B,
+ 0xDB, 0xBA, 0x8B, 0xC2, 0xC5, 0x2E, 0xF7, 0x5B,
+ 0xFF, 0x21, 0x81, 0x54, 0xD3, 0x62, 0x57, 0x4C,
+ 0x6E, 0x02, 0x98, 0xFE, 0x7E, 0xE7, 0xBC, 0x07,
+ 0x28, 0x5D, 0x86, 0xCE, 0xEA, 0x84, 0xF0, 0xE1,
+ 0x93, 0x80, 0xE9, 0xC7, 0x4A, 0xED, 0xB1, 0x26,
+ 0x89, 0x3D, 0x4F, 0xA7, 0xA1, 0xD6, 0xB5, 0x4D,
+ 0x67, 0xA4, 0x55, 0x10, 0x0F, 0xD9, 0x52, 0x32,
+ 0x96, 0xD5, 0xEB, 0x64, 0x8A, 0xC8, 0x7A, 0xBE };
+
+unsigned char table_57[256] = {
+ 0xD1, 0x9B, 0x15, 0x06, 0xB4, 0xF6, 0x97, 0xF0,
+ 0xC6, 0x5B, 0x88, 0x12, 0x25, 0xFA, 0x7B, 0x79,
+ 0xD6, 0xAB, 0xDC, 0x47, 0x85, 0x61, 0x67, 0x0B,
+ 0xF3, 0x20, 0x44, 0x53, 0x2A, 0x3B, 0x2D, 0xE8,
+ 0x17, 0x71, 0xC3, 0xB7, 0x7F, 0x35, 0xEB, 0x10,
+ 0x03, 0x0D, 0x60, 0x96, 0x27, 0xBB, 0x39, 0x50,
+ 0x95, 0x55, 0xCC, 0xD4, 0x2F, 0x51, 0xB3, 0x05,
+ 0xA5, 0xAD, 0xBC, 0x18, 0xE2, 0xAE, 0x07, 0x87,
+ 0xC4, 0x8D, 0xBE, 0x77, 0xC2, 0x16, 0xFC, 0x33,
+ 0x4C, 0x4F, 0xE6, 0xA6, 0x57, 0x9F, 0x37, 0x91,
+ 0xED, 0x4A, 0xF7, 0xB5, 0x52, 0x7C, 0xBD, 0x30,
+ 0xA0, 0x2C, 0x8C, 0xB0, 0x0C, 0xDA, 0x6F, 0x9E,
+ 0xEE, 0x43, 0x40, 0x8F, 0x8B, 0x76, 0xA4, 0x68,
+ 0xFF, 0x6D, 0x58, 0xC9, 0xF9, 0x6E, 0x3F, 0x56,
+ 0xCA, 0x49, 0xC8, 0x5D, 0xCD, 0xC7, 0x99, 0xEC,
+ 0x72, 0x38, 0x0A, 0xA9, 0xC5, 0x04, 0x64, 0xBF,
+ 0xB6, 0x29, 0x80, 0x2E, 0x19, 0x0E, 0x82, 0x45,
+ 0xBA, 0xD7, 0x1E, 0x86, 0xA8, 0xD8, 0x24, 0xDB,
+ 0xCF, 0xE1, 0x54, 0xB2, 0x3E, 0x4D, 0x90, 0x42,
+ 0x5F, 0x59, 0x0F, 0xCE, 0x8E, 0xA2, 0xA7, 0x1D,
+ 0x22, 0xFD, 0x81, 0x63, 0xE5, 0x6A, 0xE7, 0x93,
+ 0x41, 0x46, 0x66, 0x89, 0x13, 0xEA, 0x69, 0x1C,
+ 0x83, 0xF2, 0x08, 0xB8, 0x01, 0x23, 0x26, 0xFB,
+ 0x78, 0xAA, 0x31, 0x11, 0x1B, 0x98, 0xDD, 0xAC,
+ 0xB9, 0xFE, 0x94, 0x74, 0xAF, 0x32, 0xD0, 0x5A,
+ 0xA1, 0xF4, 0x6B, 0x8A, 0xE3, 0x65, 0xDE, 0xCB,
+ 0x73, 0x3D, 0xA3, 0x7E, 0xDF, 0xD2, 0x6C, 0x7A,
+ 0x36, 0xD9, 0x62, 0x4B, 0xEF, 0xC1, 0x1F, 0x00,
+ 0x34, 0xB1, 0xF8, 0xE4, 0xD5, 0x09, 0x1A, 0x9A,
+ 0x70, 0x48, 0x9D, 0xF1, 0xE0, 0x9C, 0xD3, 0x5C,
+ 0x75, 0x02, 0x2B, 0x92, 0x21, 0x7D, 0xF5, 0x5E,
+ 0x4E, 0x3C, 0x84, 0x14, 0x28, 0x3A, 0xE9, 0xC0 };
+
+unsigned char table_58[256] = {
+ 0xE9, 0x81, 0x60, 0xA7, 0x18, 0xA0, 0x0F, 0x55,
+ 0x2B, 0x52, 0xE0, 0x8B, 0x9D, 0x85, 0xD2, 0xA3,
+ 0x3F, 0x6E, 0xB1, 0xAF, 0xE3, 0x36, 0xE2, 0x19,
+ 0x56, 0xB0, 0x09, 0xB5, 0x79, 0x43, 0xE1, 0x06,
+ 0x45, 0xB6, 0xC0, 0x22, 0xEE, 0x41, 0xEC, 0x01,
+ 0x66, 0x2D, 0x87, 0x38, 0x16, 0x37, 0xFA, 0x29,
+ 0x96, 0xA4, 0xC3, 0x23, 0x59, 0x7E, 0x92, 0x78,
+ 0x10, 0x2A, 0x4C, 0x0E, 0x9B, 0x4A, 0x35, 0xF4,
+ 0x42, 0x0C, 0xD8, 0xD7, 0x24, 0x2C, 0xDD, 0x8E,
+ 0x5B, 0xF5, 0x33, 0x48, 0xEF, 0xDE, 0x4B, 0xBC,
+ 0x51, 0xAB, 0x7C, 0xE4, 0x63, 0x70, 0x9A, 0xAC,
+ 0x54, 0x1D, 0x25, 0xC5, 0xEA, 0xB3, 0x05, 0xF7,
+ 0xC1, 0x1F, 0xE8, 0x97, 0xBB, 0x32, 0x6D, 0xC7,
+ 0x28, 0x61, 0xDB, 0x4D, 0x77, 0x72, 0x65, 0x8C,
+ 0x80, 0x3A, 0x76, 0x47, 0xA8, 0x03, 0x04, 0x12,
+ 0xCE, 0xA9, 0x75, 0x3C, 0x49, 0xF8, 0x64, 0xDF,
+ 0x57, 0xA2, 0x69, 0x44, 0xAD, 0x3E, 0x4F, 0x0B,
+ 0x74, 0x67, 0xC9, 0x1A, 0x17, 0xAA, 0x02, 0x6F,
+ 0xDA, 0xF2, 0xC6, 0x27, 0x53, 0xD6, 0xFD, 0xCA,
+ 0x8D, 0x93, 0x89, 0xD5, 0x6B, 0x4E, 0x90, 0x82,
+ 0x30, 0xE7, 0xC4, 0xD9, 0x8A, 0x7F, 0xB4, 0xFC,
+ 0xCF, 0xA1, 0xAE, 0x1C, 0x39, 0x1B, 0x7B, 0x5E,
+ 0x88, 0x7D, 0xD3, 0x71, 0x2E, 0x98, 0x13, 0x8F,
+ 0xCC, 0x84, 0x73, 0xCD, 0x21, 0x0D, 0x5C, 0xA5,
+ 0x3D, 0x9E, 0x99, 0xC2, 0xF3, 0x34, 0x14, 0x62,
+ 0x46, 0x0A, 0x07, 0x08, 0xFF, 0xFB, 0xB7, 0xBF,
+ 0x5D, 0x91, 0xB8, 0x83, 0xBE, 0x94, 0xBA, 0xF9,
+ 0xEB, 0xE5, 0xCB, 0x95, 0x40, 0x31, 0xE6, 0x86,
+ 0xD4, 0xFE, 0xD0, 0x7A, 0x26, 0xB9, 0xDC, 0x2F,
+ 0xBD, 0xF0, 0x5F, 0x00, 0x9C, 0x6A, 0x5A, 0x3B,
+ 0xF1, 0xC8, 0x9F, 0xED, 0x50, 0x20, 0x15, 0x11,
+ 0x68, 0x1E, 0xF6, 0xA6, 0x6C, 0xB2, 0xD1, 0x58 };
+
+unsigned char table_59[256] = {
+ 0x4C, 0x85, 0x2B, 0x14, 0xCC, 0x4D, 0x5F, 0xD7,
+ 0xCE, 0x28, 0xC5, 0x0B, 0xA1, 0x99, 0x08, 0xDE,
+ 0x42, 0xD1, 0x82, 0x5C, 0xC9, 0x8F, 0x72, 0x12,
+ 0xCB, 0x0D, 0x04, 0xFA, 0xCD, 0xE5, 0x9A, 0x6F,
+ 0xCF, 0x92, 0xB5, 0x88, 0x87, 0xBF, 0x90, 0x7C,
+ 0xAC, 0xBE, 0x36, 0x21, 0x7D, 0x7F, 0xC7, 0x9F,
+ 0x75, 0xBB, 0x61, 0x16, 0x17, 0x63, 0xAE, 0xC4,
+ 0x23, 0x89, 0xE0, 0x37, 0x91, 0x5E, 0xC8, 0xE4,
+ 0xFD, 0xD5, 0xA2, 0xC6, 0x5A, 0xEF, 0x9B, 0xD6,
+ 0x27, 0xEE, 0x60, 0x1C, 0xDF, 0xDA, 0xF1, 0xD2,
+ 0x1E, 0x01, 0x9D, 0x44, 0x03, 0xD8, 0x11, 0x53,
+ 0x4F, 0x6C, 0x8B, 0xB7, 0x40, 0xF2, 0x79, 0x20,
+ 0x74, 0x97, 0x3E, 0x3D, 0x05, 0xD4, 0x70, 0x30,
+ 0x54, 0x59, 0xE7, 0x15, 0xE1, 0xEB, 0x71, 0x83,
+ 0xFE, 0x66, 0xB1, 0xA6, 0xF7, 0x8E, 0x6A, 0xEA,
+ 0x65, 0x7E, 0xA3, 0xCA, 0x2D, 0x4B, 0xB8, 0x9C,
+ 0x35, 0xC3, 0xB6, 0x49, 0x32, 0x25, 0xB3, 0xB0,
+ 0x76, 0xC0, 0xF5, 0x00, 0x8A, 0xAF, 0x19, 0xDB,
+ 0xDD, 0x47, 0xDC, 0x07, 0xB2, 0x4A, 0x55, 0xE6,
+ 0x69, 0xEC, 0xED, 0x06, 0x94, 0xB9, 0xA7, 0x56,
+ 0x2C, 0xAA, 0xE3, 0x22, 0x3B, 0x98, 0x77, 0x52,
+ 0x3C, 0x64, 0xF8, 0x13, 0x78, 0xFC, 0xFB, 0xF3,
+ 0xD3, 0xF9, 0x29, 0x45, 0x51, 0x8C, 0xA0, 0x38,
+ 0xD9, 0xA5, 0x62, 0x3A, 0x6E, 0xD0, 0xE8, 0x7A,
+ 0x33, 0x1D, 0xB4, 0x73, 0x02, 0xFF, 0x10, 0x80,
+ 0x6B, 0xF0, 0xA4, 0xBA, 0xF6, 0xC2, 0x0E, 0xE2,
+ 0x81, 0x43, 0x84, 0x86, 0x1F, 0x31, 0x2F, 0xA9,
+ 0x1B, 0x2A, 0x4E, 0xF4, 0x95, 0x5B, 0x3F, 0x34,
+ 0x39, 0x7B, 0x0A, 0x26, 0x6D, 0x57, 0x50, 0x09,
+ 0x9E, 0xA8, 0xBC, 0x24, 0x93, 0x67, 0x41, 0x96,
+ 0x0C, 0x46, 0xBD, 0xE9, 0x68, 0x18, 0xAB, 0x2E,
+ 0x5D, 0x1A, 0x8D, 0xC1, 0x58, 0x48, 0xAD, 0x0F };
+
+unsigned char table_60[32] = {
+ 0x1C, 0x06, 0x1E, 0x10, 0x1D, 0x05, 0x00, 0x0E,
+ 0x0C, 0x02, 0x11, 0x19, 0x15, 0x18, 0x16, 0x07,
+ 0x1F, 0x0B, 0x14, 0x01, 0x0F, 0x09, 0x0D, 0x13,
+ 0x03, 0x08, 0x12, 0x04, 0x1B, 0x0A, 0x17, 0x1A };
+
+unsigned char table_61[256] = {
+ 0xC5, 0xA6, 0xF2, 0x6B, 0x4B, 0x58, 0xE0, 0x41,
+ 0xC6, 0x2F, 0x13, 0xFE, 0xC1, 0x34, 0x3F, 0x24,
+ 0x10, 0xBF, 0x8B, 0xC9, 0x26, 0x2E, 0x68, 0xBE,
+ 0x28, 0x54, 0x93, 0x11, 0x21, 0x03, 0xFF, 0x50,
+ 0x31, 0x71, 0x2C, 0x6C, 0x91, 0x8F, 0x3B, 0x40,
+ 0x3E, 0xE5, 0xA5, 0x80, 0xEA, 0x7C, 0x9D, 0x18,
+ 0x84, 0x5A, 0x73, 0x3A, 0x33, 0x43, 0xA1, 0x47,
+ 0xB1, 0xEE, 0xFB, 0x79, 0x5E, 0xAF, 0xB9, 0x48,
+ 0x0F, 0x88, 0x65, 0x67, 0x6F, 0xDB, 0x25, 0xE4,
+ 0xB0, 0x87, 0xD0, 0x46, 0xB5, 0xB7, 0x53, 0xD4,
+ 0x1E, 0x76, 0xB4, 0x90, 0xDD, 0xA3, 0xF7, 0x57,
+ 0xD2, 0xCC, 0x5D, 0xE3, 0xB3, 0xD8, 0x5F, 0x2B,
+ 0x69, 0x4A, 0x9B, 0x39, 0x1A, 0x8D, 0x05, 0x8A,
+ 0x44, 0x15, 0xAE, 0xF3, 0xA8, 0x92, 0x02, 0xAB,
+ 0xB8, 0xDA, 0x0A, 0x0C, 0xED, 0xD7, 0x77, 0x98,
+ 0x3D, 0x19, 0x95, 0x36, 0xE7, 0x7F, 0x66, 0xEF,
+ 0x86, 0xDC, 0xCB, 0x9C, 0x63, 0xE6, 0x1D, 0x14,
+ 0x9A, 0x22, 0xBD, 0xD6, 0x89, 0x2D, 0xD1, 0xF9,
+ 0xA2, 0xDE, 0xF5, 0x5C, 0x8E, 0x2A, 0x29, 0xCA,
+ 0x7A, 0x8C, 0x38, 0x9F, 0xBB, 0xDF, 0xEC, 0x30,
+ 0x00, 0xFC, 0xAC, 0x81, 0xB2, 0xE8, 0xC0, 0xA7,
+ 0x7B, 0x07, 0x52, 0x74, 0x70, 0x0E, 0x51, 0x6A,
+ 0x62, 0x0D, 0x85, 0x1B, 0x4F, 0x96, 0x55, 0x1C,
+ 0x32, 0x6E, 0x01, 0xF6, 0x08, 0xFD, 0x17, 0x35,
+ 0xF0, 0x16, 0xC8, 0x23, 0xE9, 0x59, 0x3C, 0x37,
+ 0x5B, 0x42, 0xD3, 0x49, 0x7D, 0x83, 0x78, 0xAD,
+ 0x94, 0x9E, 0x56, 0xB6, 0xF1, 0xC3, 0x75, 0xF8,
+ 0xFA, 0x09, 0x4C, 0xD9, 0x97, 0xF4, 0x7E, 0x6D,
+ 0xBC, 0x4D, 0x64, 0xCD, 0x12, 0x99, 0x45, 0xCE,
+ 0x61, 0x20, 0x0B, 0xA0, 0x82, 0xD5, 0xE1, 0x72,
+ 0xA9, 0x1F, 0x06, 0x27, 0xC7, 0x04, 0xE2, 0xBA,
+ 0xCF, 0x60, 0xAA, 0xA4, 0xEB, 0xC4, 0x4E, 0xC2 };
+
+unsigned char table_62[256] = {
+ 0x01, 0x59, 0xEC, 0xFC, 0x51, 0xD2, 0xE4, 0x9D,
+ 0xAA, 0x61, 0xD5, 0xCA, 0x63, 0x5D, 0xCE, 0x36,
+ 0xB9, 0x49, 0x76, 0xA9, 0x14, 0x4C, 0x90, 0x28,
+ 0x66, 0x17, 0x4F, 0x1E, 0x1A, 0x47, 0x30, 0xE8,
+ 0xFD, 0x86, 0x2E, 0x7B, 0x7E, 0xCC, 0x34, 0x13,
+ 0x94, 0x45, 0x38, 0x74, 0x29, 0xB0, 0x37, 0xC3,
+ 0x26, 0x6C, 0x39, 0xA3, 0x89, 0xEB, 0xA2, 0x20,
+ 0x00, 0xE0, 0x73, 0xE7, 0xB5, 0xCB, 0xED, 0x3E,
+ 0x79, 0x09, 0xFA, 0x32, 0x54, 0xBA, 0x05, 0x96,
+ 0xDE, 0x23, 0xD0, 0xA1, 0xAB, 0xFE, 0xF2, 0x22,
+ 0xB2, 0x9B, 0x7D, 0x44, 0x12, 0x3D, 0x40, 0x82,
+ 0xA0, 0xA8, 0x33, 0xDC, 0xF7, 0xFB, 0xAC, 0x41,
+ 0x8A, 0x9C, 0x60, 0x11, 0xC8, 0xF0, 0xEA, 0x57,
+ 0x3A, 0x42, 0xCD, 0x1D, 0x3C, 0xC6, 0x97, 0x62,
+ 0x55, 0x9F, 0xF3, 0x93, 0x91, 0xDA, 0x6A, 0xE5,
+ 0x27, 0x8E, 0x4E, 0xFF, 0xA4, 0x80, 0x04, 0xE1,
+ 0x2B, 0x5E, 0xC0, 0x64, 0xC2, 0xD8, 0x46, 0x8C,
+ 0xD4, 0x0F, 0xC4, 0x43, 0xD9, 0x9E, 0x4B, 0x5C,
+ 0x0A, 0x8B, 0xBF, 0xD7, 0x7A, 0x81, 0x3B, 0x4A,
+ 0x58, 0xB6, 0x21, 0x1F, 0xC1, 0xBD, 0xB1, 0x77,
+ 0x72, 0x1C, 0x4D, 0xBC, 0xA5, 0x65, 0xC7, 0xF5,
+ 0xB4, 0x2D, 0x69, 0x71, 0xE6, 0x8F, 0xBB, 0x03,
+ 0xAF, 0xD6, 0x08, 0x75, 0xB7, 0x31, 0xF4, 0x2A,
+ 0x48, 0x70, 0x0C, 0x8D, 0xD1, 0x87, 0x2F, 0x16,
+ 0x5A, 0x5B, 0x98, 0xA6, 0xC5, 0x99, 0x50, 0x07,
+ 0xDD, 0x92, 0x25, 0x68, 0x0D, 0xBE, 0x78, 0x0B,
+ 0xAD, 0x84, 0x6B, 0x19, 0x52, 0x7C, 0xF6, 0xB3,
+ 0x56, 0x83, 0x88, 0xEE, 0x2C, 0x1B, 0x6E, 0x53,
+ 0x67, 0xE2, 0x6F, 0x15, 0x06, 0x10, 0x18, 0x85,
+ 0xF1, 0x6D, 0xF9, 0xC9, 0xAE, 0x3F, 0xB8, 0x95,
+ 0x35, 0xDF, 0xEF, 0xA7, 0x7F, 0x24, 0xF8, 0xE3,
+ 0xCF, 0xE9, 0xDB, 0xD3, 0x02, 0x9A, 0x0E, 0x5F };
+
+unsigned char table_63[256] = {
+ 0x0C, 0x02, 0xEE, 0x94, 0x2D, 0x76, 0x96, 0x75,
+ 0x21, 0xDC, 0x37, 0x03, 0xC0, 0xF7, 0xDF, 0xEF,
+ 0xB1, 0x1D, 0xCF, 0x15, 0x5A, 0xB4, 0xCC, 0x81,
+ 0x89, 0x6B, 0xA5, 0x2E, 0x6D, 0xD4, 0x08, 0x44,
+ 0x2A, 0x60, 0x50, 0xBF, 0x40, 0x7D, 0x5F, 0x64,
+ 0x93, 0x70, 0xA4, 0x7F, 0xC9, 0xEB, 0x0A, 0xF8,
+ 0x9F, 0xA8, 0xBC, 0x25, 0xE5, 0xF3, 0x1B, 0xD7,
+ 0x29, 0x13, 0x0D, 0x69, 0x20, 0x5C, 0x0F, 0x91,
+ 0x4F, 0x62, 0x06, 0x26, 0x41, 0xED, 0xDA, 0x53,
+ 0x65, 0xFF, 0xCD, 0x3F, 0xF6, 0x01, 0xCE, 0xA2,
+ 0x04, 0xDE, 0x27, 0x87, 0xBA, 0x86, 0x24, 0x78,
+ 0xAF, 0xE1, 0x3D, 0xD0, 0xC8, 0x1F, 0x4A, 0x2C,
+ 0x9A, 0xF0, 0xCB, 0xAD, 0x0B, 0x59, 0xC5, 0x58,
+ 0xEA, 0x8A, 0xA1, 0x45, 0xB7, 0x5D, 0xB5, 0x77,
+ 0x2B, 0x47, 0x05, 0x00, 0xAC, 0x61, 0xFA, 0x33,
+ 0x74, 0x31, 0xCA, 0x22, 0x42, 0x8B, 0xFE, 0x09,
+ 0xB2, 0x6E, 0x1A, 0xBE, 0xAA, 0x7B, 0xEC, 0xF4,
+ 0x51, 0x66, 0x28, 0x12, 0xFC, 0x5E, 0x67, 0xF5,
+ 0xB9, 0x82, 0x90, 0x8E, 0x8D, 0x17, 0xE7, 0xE8,
+ 0xB0, 0xC3, 0x16, 0xA0, 0x4B, 0xB6, 0xFB, 0x7E,
+ 0xC4, 0x85, 0x4C, 0x1E, 0xC7, 0x39, 0x4E, 0xA9,
+ 0xE3, 0x4D, 0x32, 0x72, 0x35, 0x80, 0xE0, 0x34,
+ 0xB8, 0x73, 0x98, 0x49, 0x92, 0x30, 0xD5, 0xD2,
+ 0xA3, 0x54, 0x7A, 0x84, 0x8F, 0x6C, 0xFD, 0x43,
+ 0x3A, 0x36, 0x3B, 0xD9, 0x48, 0x6A, 0x14, 0x79,
+ 0xD1, 0x57, 0x88, 0xDB, 0xE4, 0x9B, 0xF9, 0x99,
+ 0x10, 0x71, 0xC1, 0x68, 0x9E, 0x11, 0xAB, 0xBD,
+ 0x7C, 0x3E, 0x3C, 0x18, 0x9D, 0x97, 0xF2, 0xE6,
+ 0xA6, 0xF1, 0x46, 0xC2, 0x19, 0xBB, 0x52, 0xD8,
+ 0x95, 0xD3, 0x23, 0xAE, 0x07, 0x2F, 0xE9, 0x63,
+ 0x1C, 0x55, 0x6F, 0x9C, 0x56, 0x38, 0xC6, 0x5B,
+ 0x8C, 0xE2, 0x83, 0xA7, 0xD6, 0x0E, 0xB3, 0xDD };
+
+unsigned char table_64[32] = {
+ 0x03, 0x05, 0x0D, 0x09, 0x1A, 0x16, 0x08, 0x10,
+ 0x06, 0x1E, 0x1C, 0x15, 0x02, 0x04, 0x17, 0x0C,
+ 0x18, 0x0B, 0x19, 0x11, 0x1B, 0x14, 0x13, 0x0A,
+ 0x0E, 0x00, 0x1D, 0x1F, 0x01, 0x0F, 0x07, 0x12 };
+
+unsigned char table_65[32] = {
+ 0x01, 0x0A, 0x1E, 0x14, 0x10, 0x1D, 0x0D, 0x17,
+ 0x0E, 0x0C, 0x0F, 0x12, 0x04, 0x1A, 0x05, 0x02,
+ 0x08, 0x1C, 0x09, 0x1F, 0x0B, 0x13, 0x19, 0x1B,
+ 0x11, 0x00, 0x16, 0x06, 0x03, 0x18, 0x15, 0x07 };
+
+unsigned char table_66[32] = {
+ 0x1C, 0x18, 0x0C, 0x09, 0x05, 0x03, 0x15, 0x12,
+ 0x0D, 0x02, 0x08, 0x0E, 0x19, 0x07, 0x13, 0x17,
+ 0x1E, 0x1D, 0x1F, 0x11, 0x06, 0x0A, 0x0B, 0x14,
+ 0x0F, 0x10, 0x01, 0x1B, 0x00, 0x04, 0x1A, 0x16 };
+
+unsigned char table_67[256] = {
+ 0x6B, 0x49, 0xC8, 0x86, 0xFF, 0xC0, 0x5D, 0xEF,
+ 0xF7, 0x06, 0xE0, 0x98, 0xA9, 0x72, 0x71, 0xD5,
+ 0xBA, 0x7F, 0x10, 0xD1, 0xBE, 0x41, 0x9C, 0x40,
+ 0x28, 0x8E, 0xE5, 0x74, 0x47, 0x9E, 0x3E, 0x7C,
+ 0xB5, 0xCD, 0x3F, 0x20, 0xF2, 0xA6, 0xDC, 0x97,
+ 0x32, 0x6D, 0x52, 0xF5, 0x16, 0x05, 0xFE, 0x04,
+ 0x3D, 0x53, 0x50, 0x23, 0x39, 0x77, 0x08, 0x60,
+ 0x75, 0x18, 0x4A, 0xC6, 0xBB, 0xE7, 0xF1, 0xAB,
+ 0xEB, 0x88, 0xB6, 0x82, 0x6E, 0x91, 0xF3, 0x34,
+ 0x3A, 0x42, 0x1A, 0xDF, 0xA1, 0xB3, 0x92, 0xBF,
+ 0xB7, 0x00, 0xD4, 0xDE, 0x31, 0xF0, 0x1C, 0xDA,
+ 0x4F, 0x61, 0x67, 0x2C, 0x07, 0xF9, 0x15, 0xA4,
+ 0x7A, 0x26, 0x45, 0x2A, 0x12, 0x9F, 0xF4, 0x14,
+ 0x8C, 0x90, 0xFC, 0xC5, 0x4B, 0x87, 0xE2, 0xC7,
+ 0xD0, 0x8A, 0xE8, 0xDD, 0xEE, 0x3C, 0x2F, 0x22,
+ 0x6A, 0x54, 0x37, 0x9B, 0x84, 0x25, 0x8F, 0xE3,
+ 0xD7, 0xD8, 0x4E, 0xAD, 0x0F, 0x4C, 0x56, 0xA2,
+ 0xD3, 0xB0, 0x73, 0x0B, 0xAE, 0xEA, 0x1D, 0x01,
+ 0x36, 0xB4, 0x2D, 0xC4, 0x19, 0x58, 0x1E, 0x62,
+ 0xE9, 0xB2, 0x5B, 0x5A, 0xBD, 0xD6, 0x65, 0x94,
+ 0x9A, 0x55, 0xCC, 0x99, 0x1B, 0x85, 0x2B, 0xBC,
+ 0x8D, 0x46, 0x81, 0xB8, 0xA3, 0x29, 0x5F, 0x35,
+ 0x5C, 0xB1, 0x1F, 0x13, 0x17, 0xCB, 0x51, 0x02,
+ 0x09, 0x7E, 0xA7, 0x69, 0x6F, 0x95, 0x30, 0x7B,
+ 0xCA, 0x48, 0xAF, 0xAA, 0x0E, 0x44, 0x38, 0xB9,
+ 0x0D, 0x11, 0xA0, 0xD9, 0x0C, 0xDB, 0xF8, 0x68,
+ 0x33, 0x79, 0x59, 0x66, 0x4D, 0x03, 0xE1, 0x89,
+ 0xE4, 0x3B, 0x78, 0xC2, 0x64, 0x6C, 0x27, 0xC9,
+ 0xCF, 0xAC, 0xED, 0xFA, 0x5E, 0x2E, 0x76, 0x57,
+ 0x93, 0xEC, 0x80, 0xA8, 0xE6, 0xCE, 0xC1, 0xA5,
+ 0x9D, 0xD2, 0xC3, 0x0A, 0x7D, 0x70, 0xF6, 0x63,
+ 0x24, 0x43, 0x21, 0x83, 0xFB, 0xFD, 0x8B, 0x96 };
+
+unsigned char table_68[256] = {
+ 0x93, 0xFF, 0x83, 0x70, 0x12, 0x2D, 0x1C, 0xD6,
+ 0xF9, 0xEE, 0xCF, 0x94, 0x7B, 0xB5, 0xA4, 0x84,
+ 0x99, 0xF7, 0x67, 0x32, 0xFC, 0x8A, 0xE3, 0xE4,
+ 0xCE, 0xC6, 0x77, 0x7E, 0xDA, 0x42, 0x85, 0xF0,
+ 0x7D, 0x48, 0x28, 0x79, 0xDE, 0x5B, 0xE2, 0x0F,
+ 0x75, 0xC5, 0x2C, 0x4F, 0xF3, 0xEC, 0x14, 0x10,
+ 0x9C, 0x6E, 0x59, 0x4A, 0x20, 0x34, 0xA3, 0x89,
+ 0xE0, 0x4E, 0x52, 0x88, 0x81, 0x5F, 0x6F, 0x71,
+ 0x17, 0x3B, 0x21, 0xB4, 0xCB, 0x9B, 0x18, 0x13,
+ 0xE8, 0xE1, 0x02, 0x2E, 0xED, 0x00, 0xA7, 0x1B,
+ 0x06, 0xF4, 0x27, 0xDC, 0x35, 0x2F, 0x08, 0x9D,
+ 0x7C, 0xC0, 0x36, 0xA6, 0x6B, 0xDF, 0x4C, 0xBC,
+ 0xFE, 0xDB, 0xA5, 0xA8, 0x8D, 0x73, 0x7F, 0xC7,
+ 0x8E, 0x60, 0x31, 0x61, 0x4B, 0x29, 0xD7, 0xE9,
+ 0xBD, 0xAB, 0xCC, 0xFA, 0xD9, 0xEF, 0xC2, 0xD4,
+ 0x19, 0x11, 0x15, 0xC9, 0xB1, 0xD5, 0x64, 0x97,
+ 0xE7, 0x8F, 0x05, 0x44, 0xF8, 0xF1, 0x58, 0x47,
+ 0x2A, 0x03, 0x1F, 0xAF, 0x0D, 0x04, 0x23, 0xB8,
+ 0x24, 0x51, 0xB2, 0x54, 0x41, 0x53, 0x5C, 0xAE,
+ 0xB7, 0xB3, 0xB6, 0x3D, 0x37, 0x39, 0x55, 0xBF,
+ 0x0B, 0x7A, 0x57, 0x3C, 0x0E, 0x40, 0x6A, 0xF5,
+ 0x72, 0xDD, 0xBB, 0x8B, 0xAA, 0x46, 0xA0, 0x30,
+ 0x56, 0x78, 0x38, 0xBA, 0x9E, 0x92, 0x87, 0xFB,
+ 0x66, 0x90, 0x1E, 0xB9, 0x96, 0x65, 0xA2, 0x50,
+ 0x1D, 0xC3, 0x26, 0x22, 0xD0, 0x0A, 0x43, 0xF2,
+ 0xB0, 0xEB, 0xAC, 0x62, 0x98, 0x3F, 0xD3, 0x69,
+ 0xA1, 0x9F, 0x16, 0x95, 0xE6, 0xF6, 0x2B, 0x25,
+ 0x1A, 0xD2, 0xBE, 0x09, 0x5D, 0x45, 0xC4, 0xFD,
+ 0x5A, 0x07, 0x0C, 0x82, 0x3E, 0x49, 0x74, 0x6C,
+ 0x68, 0x5E, 0xCA, 0xEA, 0xCD, 0x9A, 0xAD, 0xD1,
+ 0x33, 0x86, 0x76, 0x80, 0xE5, 0xC8, 0xD8, 0xA9,
+ 0x8C, 0x6D, 0x91, 0x63, 0x3A, 0x4D, 0xC1, 0x01 };
+
+unsigned char table_69[256] = {
+ 0x21, 0x6B, 0x9B, 0xAE, 0x11, 0x5A, 0x91, 0xC2,
+ 0x47, 0x8E, 0x87, 0x86, 0x4F, 0xFC, 0x8F, 0x66,
+ 0x97, 0x2F, 0x61, 0x9C, 0x5B, 0x4C, 0xB3, 0x14,
+ 0x77, 0x48, 0x62, 0xE1, 0x54, 0x64, 0xDD, 0xCD,
+ 0x30, 0xB7, 0x2D, 0xD2, 0xC3, 0xC0, 0x0B, 0xD8,
+ 0x53, 0x98, 0x16, 0x56, 0x7A, 0x35, 0x50, 0xD9,
+ 0xE8, 0x2C, 0x32, 0x55, 0x17, 0x5D, 0x79, 0xEB,
+ 0xC8, 0x75, 0x67, 0xE2, 0x4B, 0xBA, 0xFE, 0x57,
+ 0x10, 0xF4, 0x70, 0x2A, 0xBB, 0xA6, 0x72, 0x36,
+ 0xAF, 0x8D, 0xAB, 0x90, 0xE3, 0x2B, 0xB2, 0x26,
+ 0x93, 0x01, 0xBD, 0x71, 0xF9, 0x05, 0xC7, 0x80,
+ 0x29, 0xCC, 0x3B, 0x22, 0xF2, 0x12, 0x81, 0x34,
+ 0xF6, 0x1A, 0x8B, 0xDF, 0x28, 0x46, 0x9E, 0x6A,
+ 0x23, 0x85, 0x74, 0xE7, 0xE6, 0x52, 0xA0, 0x49,
+ 0xF0, 0x19, 0x25, 0xAC, 0x78, 0x42, 0xD6, 0xA2,
+ 0x37, 0x65, 0x4D, 0x94, 0x02, 0x6F, 0xB4, 0xC6,
+ 0x99, 0xD3, 0x9A, 0x33, 0xB8, 0x00, 0xCA, 0xE4,
+ 0x45, 0xAD, 0x1B, 0x6C, 0x03, 0xA8, 0x07, 0x8A,
+ 0x60, 0x69, 0xFF, 0xF7, 0xA7, 0x27, 0x95, 0xF5,
+ 0x82, 0xCB, 0xEC, 0xED, 0x4E, 0xFB, 0xA4, 0x59,
+ 0xDA, 0xCF, 0x2E, 0x20, 0xFA, 0x31, 0xD1, 0xEA,
+ 0x4A, 0xE9, 0x5E, 0xA9, 0xA1, 0x08, 0x1C, 0x96,
+ 0x38, 0xB9, 0xEE, 0x7F, 0xAA, 0xF1, 0x7D, 0x3A,
+ 0xA5, 0x43, 0xC5, 0xE0, 0x24, 0x39, 0x0D, 0xDE,
+ 0xB0, 0xF8, 0xBE, 0x58, 0x7E, 0x51, 0xD4, 0x89,
+ 0x15, 0x40, 0x3E, 0xB1, 0x1F, 0x5F, 0x68, 0x63,
+ 0x84, 0x3D, 0x88, 0xBC, 0x41, 0xEF, 0xB5, 0xBF,
+ 0x06, 0x6E, 0x9D, 0x3F, 0x0E, 0x76, 0x5C, 0xDC,
+ 0x13, 0xF3, 0xE5, 0x8C, 0x7C, 0x04, 0x0A, 0xD5,
+ 0x18, 0xC4, 0x44, 0x09, 0xC9, 0x1D, 0x9F, 0xFD,
+ 0xD0, 0x0F, 0x6D, 0xD7, 0x92, 0x7B, 0x0C, 0xA3,
+ 0x73, 0xDB, 0xB6, 0x83, 0xCE, 0x1E, 0xC1, 0x3C };
+
+unsigned char table_70[256] = {
+ 0x54, 0x23, 0xF1, 0x09, 0x9D, 0xEB, 0x26, 0xD9,
+ 0x6C, 0xC1, 0xBC, 0x3D, 0x6E, 0xB0, 0x5F, 0xE2,
+ 0x59, 0x4D, 0x95, 0xFA, 0xD8, 0x29, 0xAA, 0x8E,
+ 0xF5, 0xEF, 0x43, 0x76, 0xFD, 0x0D, 0x4F, 0xAD,
+ 0xB7, 0xFC, 0xA8, 0x9F, 0x62, 0xC2, 0x7B, 0x10,
+ 0x0B, 0xF2, 0x73, 0xA9, 0x46, 0x4C, 0x53, 0xD7,
+ 0x0A, 0x50, 0x89, 0x63, 0x48, 0xD6, 0xA2, 0x44,
+ 0xE6, 0x8D, 0x69, 0x2C, 0xF9, 0xC0, 0x35, 0x06,
+ 0x66, 0x21, 0x9E, 0xD2, 0x98, 0xF7, 0x9B, 0xE7,
+ 0x12, 0xB8, 0xA5, 0xBA, 0xE0, 0x79, 0x71, 0x7E,
+ 0x8C, 0x24, 0xED, 0x7C, 0x60, 0x81, 0xC3, 0x5C,
+ 0x2B, 0xE5, 0xEE, 0xB5, 0xA4, 0x05, 0x03, 0x34,
+ 0x16, 0x2A, 0xA3, 0x2D, 0x3F, 0xDF, 0x07, 0x5B,
+ 0xAE, 0x47, 0x61, 0x08, 0x18, 0xDB, 0x6D, 0x3C,
+ 0x96, 0xD5, 0xAB, 0x78, 0x94, 0x45, 0x20, 0x9A,
+ 0xE4, 0x13, 0x68, 0xDD, 0xDE, 0x31, 0x14, 0x57,
+ 0x02, 0x52, 0x56, 0x1C, 0x1B, 0xE9, 0xD0, 0xA1,
+ 0x22, 0x64, 0xB2, 0x7A, 0xCF, 0x5D, 0x00, 0x0F,
+ 0xF8, 0x5E, 0x36, 0x58, 0x40, 0xAF, 0x19, 0x32,
+ 0x2E, 0xB3, 0x72, 0xBE, 0xB9, 0xD3, 0xCD, 0x7D,
+ 0x4A, 0x1D, 0x33, 0x2F, 0xAC, 0x27, 0x41, 0xE8,
+ 0x55, 0xCB, 0x0E, 0x5A, 0x77, 0xFB, 0x8B, 0x86,
+ 0x75, 0x8A, 0x51, 0xEC, 0xDA, 0xC6, 0xA6, 0xCC,
+ 0x91, 0x4B, 0x11, 0xF6, 0xEA, 0xD1, 0xB6, 0x4E,
+ 0x82, 0x04, 0x92, 0x30, 0xF4, 0x25, 0x88, 0x1E,
+ 0x9C, 0xA0, 0xC8, 0x6A, 0x93, 0x87, 0x1F, 0xB4,
+ 0xB1, 0x8F, 0x65, 0xCA, 0xFE, 0xFF, 0x97, 0x15,
+ 0x99, 0x28, 0x80, 0x42, 0x70, 0x85, 0x0C, 0x3B,
+ 0xBD, 0xE1, 0xA7, 0x17, 0xC9, 0x3A, 0xBB, 0x6B,
+ 0x37, 0xF0, 0xC5, 0x39, 0x6F, 0x01, 0x83, 0x67,
+ 0x74, 0xCE, 0xDC, 0x90, 0x3E, 0xF3, 0x7F, 0xC4,
+ 0x49, 0x84, 0x38, 0xC7, 0xE3, 0xD4, 0x1A, 0xBF };
+
+unsigned char table_71[32] = {
+ 0x17, 0x13, 0x0E, 0x1A, 0x0D, 0x18, 0x19, 0x10,
+ 0x14, 0x11, 0x16, 0x05, 0x04, 0x00, 0x12, 0x0A,
+ 0x02, 0x07, 0x03, 0x0B, 0x09, 0x1F, 0x1C, 0x0F,
+ 0x0C, 0x06, 0x1B, 0x08, 0x1D, 0x01, 0x15, 0x1E };
+
+unsigned char table_72[256] = {
+ 0xC9, 0xA7, 0x1B, 0xEC, 0x2B, 0x8B, 0xB0, 0xEB,
+ 0x7F, 0x39, 0x25, 0xD9, 0x1D, 0xD5, 0x67, 0xA0,
+ 0xB3, 0xAC, 0x3B, 0xC8, 0x82, 0xC0, 0xE3, 0x9E,
+ 0x4C, 0x9B, 0xAF, 0xFD, 0x91, 0x86, 0x5F, 0x92,
+ 0xB4, 0x42, 0x3C, 0x45, 0x12, 0xC4, 0xE2, 0xE1,
+ 0x6C, 0x1F, 0xC6, 0x40, 0x93, 0x2A, 0xC2, 0x72,
+ 0x2E, 0x14, 0x51, 0xA5, 0x70, 0xBD, 0xA2, 0xC7,
+ 0x7D, 0xF1, 0x9F, 0x64, 0xC1, 0xF7, 0x80, 0xFF,
+ 0x50, 0x49, 0x8C, 0x66, 0x13, 0x48, 0x6A, 0x0A,
+ 0x26, 0x94, 0x83, 0x1E, 0x84, 0xBB, 0x57, 0x27,
+ 0x44, 0x5B, 0x62, 0xF6, 0x09, 0x4F, 0x77, 0x76,
+ 0x2D, 0x7E, 0xCD, 0x0B, 0x24, 0xFE, 0x81, 0xB8,
+ 0x21, 0x85, 0xCF, 0xA8, 0x75, 0x56, 0x37, 0x17,
+ 0xAA, 0x23, 0xE5, 0xE8, 0x9A, 0x9D, 0x2F, 0x04,
+ 0x31, 0x4A, 0x7C, 0xFC, 0xD6, 0xE4, 0x29, 0xC3,
+ 0xFB, 0x36, 0x1C, 0x0C, 0xCE, 0xEE, 0x0D, 0xF3,
+ 0x46, 0xF8, 0x41, 0x0E, 0x68, 0xAB, 0x2C, 0x69,
+ 0x96, 0x90, 0x28, 0xED, 0x02, 0x63, 0x07, 0xAD,
+ 0xB2, 0xDC, 0x05, 0xE6, 0x78, 0x03, 0xA4, 0x7A,
+ 0x5C, 0x52, 0x95, 0x5D, 0x88, 0x01, 0xDF, 0x35,
+ 0x5E, 0xB6, 0x06, 0x4D, 0x15, 0x89, 0x59, 0x3F,
+ 0xF0, 0xA1, 0xA3, 0x99, 0x19, 0xEA, 0xDB, 0xE0,
+ 0x6B, 0x71, 0x6E, 0xB7, 0x65, 0x54, 0x9C, 0xBC,
+ 0x98, 0xDD, 0x4B, 0x60, 0x3D, 0xBF, 0xF5, 0xD1,
+ 0xD7, 0xF9, 0x55, 0x61, 0xA9, 0xB1, 0x6D, 0xDE,
+ 0x79, 0xAE, 0x1A, 0x34, 0x3A, 0x4E, 0xCB, 0x38,
+ 0xBA, 0x97, 0x00, 0x74, 0xEF, 0xD8, 0x18, 0x33,
+ 0x7B, 0xFA, 0x22, 0x32, 0x20, 0xCA, 0x8A, 0xBE,
+ 0xA6, 0x43, 0x11, 0x10, 0xD0, 0xD3, 0x87, 0x73,
+ 0x6F, 0xF4, 0x8D, 0xCC, 0x30, 0x0F, 0x16, 0xDA,
+ 0xB5, 0xC5, 0xD4, 0x47, 0x8E, 0xE7, 0x58, 0x8F,
+ 0x08, 0x53, 0xF2, 0xB9, 0x5A, 0x3E, 0xE9, 0xD2 };
+
+unsigned char table_73[256] = {
+ 0x36, 0x37, 0xED, 0xD8, 0xBF, 0xD7, 0x12, 0xB7,
+ 0x40, 0x32, 0x19, 0x4A, 0x44, 0x2A, 0xCE, 0xA5,
+ 0x29, 0x13, 0x43, 0x51, 0x5C, 0xD0, 0x76, 0x6E,
+ 0x41, 0xD6, 0xE2, 0x4F, 0xB8, 0x27, 0x2E, 0xCF,
+ 0xD9, 0xE0, 0x69, 0xC0, 0x59, 0x77, 0x62, 0x6F,
+ 0x53, 0xE7, 0x93, 0xD4, 0xAD, 0xC8, 0x4C, 0xC2,
+ 0x2C, 0xBE, 0xAA, 0xA0, 0x22, 0x78, 0x14, 0xB3,
+ 0xB0, 0xEA, 0xBA, 0x9A, 0x33, 0x1B, 0x31, 0x6C,
+ 0xFC, 0x0A, 0x0B, 0xA1, 0xE4, 0x75, 0x7C, 0xE3,
+ 0x65, 0x21, 0xA9, 0xA4, 0x4E, 0x3C, 0x5F, 0x39,
+ 0x74, 0xA2, 0x9E, 0x03, 0x70, 0xD2, 0xFD, 0x1D,
+ 0x25, 0x72, 0x73, 0x8E, 0x7B, 0xB2, 0x6A, 0x92,
+ 0x81, 0xF3, 0xF0, 0x46, 0x08, 0x85, 0xE6, 0x30,
+ 0x05, 0x7E, 0xEC, 0x0D, 0xDD, 0x42, 0x2F, 0x5B,
+ 0xB9, 0xCB, 0x84, 0x0C, 0x16, 0xC7, 0x24, 0xFA,
+ 0xF9, 0x8F, 0x20, 0xAC, 0x10, 0x55, 0xC3, 0x1A,
+ 0x8B, 0x94, 0x3D, 0xDB, 0xC9, 0x04, 0xB5, 0xCC,
+ 0xC6, 0x98, 0xB6, 0x8D, 0x0F, 0x3A, 0x06, 0x4B,
+ 0xEF, 0x35, 0x68, 0x3F, 0xEE, 0xE5, 0x63, 0xC5,
+ 0x60, 0x88, 0x52, 0x2D, 0x6D, 0xAB, 0xCD, 0xC4,
+ 0x1F, 0xF4, 0xCA, 0x67, 0x7D, 0x1C, 0xDA, 0x34,
+ 0xDE, 0x86, 0xAE, 0xF1, 0x61, 0x09, 0xF5, 0xF6,
+ 0x49, 0xE9, 0xF2, 0x48, 0x1E, 0xD3, 0x56, 0x18,
+ 0x9B, 0xB1, 0x57, 0x9D, 0xBB, 0x5E, 0xAF, 0x87,
+ 0x9F, 0x8A, 0xC1, 0x79, 0xA7, 0xA8, 0xFB, 0xDC,
+ 0x47, 0x3E, 0x97, 0x80, 0x91, 0xA6, 0x7A, 0xA3,
+ 0x9C, 0x11, 0x02, 0x2B, 0x58, 0xD1, 0xF7, 0x00,
+ 0x83, 0x01, 0xE8, 0xFE, 0x50, 0x23, 0x66, 0x4D,
+ 0xD5, 0x82, 0x89, 0x3B, 0xEB, 0xE1, 0xF8, 0x5A,
+ 0x15, 0x7F, 0x8C, 0x17, 0x96, 0x28, 0x5D, 0x64,
+ 0x26, 0x38, 0x71, 0x0E, 0x45, 0xDF, 0xB4, 0x99,
+ 0xFF, 0x90, 0x6B, 0xBC, 0x54, 0x95, 0xBD, 0x07 };
+
+unsigned char table_74[256] = {
+ 0xA7, 0xCF, 0x99, 0x1A, 0x13, 0xC7, 0xE9, 0xC4,
+ 0xB6, 0x0E, 0x15, 0x09, 0xFF, 0xDF, 0xBE, 0x03,
+ 0xAD, 0xF1, 0xB0, 0x3C, 0x4A, 0x9B, 0xF5, 0x12,
+ 0xA1, 0x2C, 0xDB, 0x51, 0x5E, 0x6F, 0xE6, 0x49,
+ 0x27, 0xBB, 0xAE, 0x56, 0xC0, 0x0C, 0x77, 0x60,
+ 0x5B, 0x69, 0xA2, 0xF0, 0x24, 0x8E, 0xE1, 0xA4,
+ 0xBC, 0x9F, 0x50, 0xD4, 0x61, 0x19, 0x67, 0x00,
+ 0x7B, 0xAB, 0xDD, 0x26, 0xCD, 0x6C, 0xE8, 0xA8,
+ 0x7A, 0x93, 0xEF, 0x20, 0x52, 0x1F, 0x1B, 0x46,
+ 0x25, 0x3B, 0x1E, 0x65, 0xC2, 0xF9, 0x10, 0xB2,
+ 0xB3, 0xD9, 0x21, 0xD2, 0x11, 0x94, 0xE2, 0xFC,
+ 0x38, 0x9E, 0x36, 0x87, 0xAA, 0x53, 0x45, 0x68,
+ 0x2B, 0xE7, 0x07, 0xFA, 0xD3, 0x8D, 0x3F, 0x17,
+ 0xC1, 0x06, 0x72, 0x62, 0x8C, 0x55, 0x73, 0x8A,
+ 0xC9, 0x2E, 0x5A, 0x7D, 0x02, 0x6D, 0xF8, 0x4B,
+ 0xE4, 0xBF, 0xEC, 0xB7, 0x31, 0xDC, 0xF4, 0xB8,
+ 0x47, 0x64, 0x0A, 0x33, 0x48, 0xAC, 0xFB, 0x05,
+ 0x3E, 0x34, 0x1C, 0x97, 0x1D, 0x63, 0x37, 0x2D,
+ 0xB1, 0x92, 0xED, 0x9D, 0x4C, 0xD5, 0x4E, 0x9A,
+ 0x0D, 0x79, 0x0F, 0xBD, 0x95, 0xBA, 0x08, 0x2A,
+ 0xC6, 0x7E, 0x88, 0xCB, 0xA6, 0x29, 0x70, 0x35,
+ 0x66, 0xCA, 0x89, 0x75, 0x6A, 0x4F, 0xB5, 0x6B,
+ 0x74, 0xDE, 0x01, 0x04, 0x81, 0x91, 0x90, 0x18,
+ 0x32, 0x0B, 0x7F, 0x44, 0xB4, 0xAF, 0xF2, 0xEB,
+ 0x22, 0xFD, 0x14, 0xA0, 0xFE, 0x8B, 0xB9, 0x16,
+ 0x86, 0xE3, 0xD7, 0xDA, 0xC5, 0x3A, 0x41, 0x83,
+ 0xD1, 0x28, 0x54, 0x30, 0xE0, 0x40, 0xA5, 0x57,
+ 0x8F, 0x84, 0xD6, 0x96, 0x39, 0xE5, 0x42, 0x80,
+ 0xA9, 0x58, 0xCE, 0x5D, 0xEE, 0x5F, 0xA3, 0xD0,
+ 0xC8, 0x59, 0x43, 0x4D, 0x5C, 0xF7, 0xCC, 0x76,
+ 0x6E, 0xF3, 0x23, 0x3D, 0x85, 0x82, 0x78, 0xF6,
+ 0x2F, 0xD8, 0xC3, 0x7C, 0x9C, 0x98, 0xEA, 0x71 };
+
+unsigned char table_75[256] = {
+ 0xE7, 0xA5, 0x30, 0xE1, 0x9D, 0x81, 0xBE, 0x83,
+ 0xB2, 0x1E, 0xE4, 0x69, 0x2F, 0x2B, 0x0D, 0xEB,
+ 0x7C, 0x59, 0x2D, 0xAA, 0x01, 0x0C, 0xDB, 0xED,
+ 0xC4, 0xEE, 0x5D, 0x38, 0x72, 0xD8, 0x70, 0xCE,
+ 0x0B, 0xF6, 0x7F, 0x48, 0x26, 0x9E, 0xA3, 0x44,
+ 0xD6, 0xCF, 0x0F, 0x6B, 0xFD, 0x23, 0x98, 0xAB,
+ 0x11, 0xD4, 0x92, 0x91, 0x5E, 0x08, 0x4D, 0xC6,
+ 0xF0, 0xA8, 0x7E, 0x8A, 0x1D, 0xA1, 0x97, 0x76,
+ 0x3E, 0x64, 0x07, 0x24, 0xDE, 0x75, 0xA4, 0xCC,
+ 0x1A, 0x04, 0x4B, 0x6C, 0xFA, 0xB0, 0xC7, 0x35,
+ 0xE2, 0x56, 0x61, 0xA0, 0xE9, 0x27, 0xDF, 0xC3,
+ 0xE5, 0xF4, 0x8D, 0xB4, 0xD3, 0x52, 0xD7, 0x49,
+ 0xCD, 0x31, 0x6E, 0x3F, 0x4E, 0x6A, 0x5B, 0x65,
+ 0xCA, 0x14, 0x71, 0x53, 0xD9, 0x47, 0x28, 0x7D,
+ 0x17, 0x06, 0x5C, 0xFE, 0xBA, 0xB8, 0xAC, 0x15,
+ 0xE8, 0xE0, 0x9A, 0xDD, 0x1F, 0xBC, 0x95, 0x42,
+ 0xCB, 0x58, 0x00, 0x85, 0xD5, 0x62, 0xC9, 0xB6,
+ 0x05, 0x80, 0x4C, 0x3C, 0x1C, 0xF5, 0x03, 0xF8,
+ 0x96, 0x77, 0x02, 0x19, 0xF2, 0xFB, 0x5F, 0xC2,
+ 0xAE, 0x60, 0x1B, 0xAD, 0x8F, 0xC1, 0x33, 0xA6,
+ 0x20, 0xBF, 0xA7, 0xC8, 0x74, 0x18, 0x90, 0xE3,
+ 0x68, 0x09, 0x7A, 0x79, 0xB5, 0xDA, 0xF3, 0x0E,
+ 0x66, 0x84, 0xB3, 0xBB, 0xE6, 0xF7, 0xB7, 0x7B,
+ 0x39, 0x4A, 0x12, 0x4F, 0xC5, 0x41, 0x54, 0xD0,
+ 0xFF, 0x87, 0x63, 0x40, 0x99, 0x21, 0x29, 0xD2,
+ 0x3D, 0x37, 0x3A, 0x93, 0xFC, 0x25, 0xF1, 0xD1,
+ 0x2C, 0x6D, 0x8C, 0x5A, 0x8E, 0x9B, 0xBD, 0xAF,
+ 0x10, 0x55, 0xF9, 0x9F, 0x43, 0x0A, 0x50, 0x16,
+ 0x57, 0xB1, 0xC0, 0x73, 0x82, 0xEF, 0x88, 0x6F,
+ 0xEA, 0x2A, 0xEC, 0x2E, 0x86, 0x45, 0x51, 0x22,
+ 0xA9, 0x34, 0x94, 0x3B, 0xB9, 0x9C, 0xA2, 0x13,
+ 0x89, 0x46, 0x78, 0xDC, 0x32, 0x8B, 0x67, 0x36 };
+
+unsigned char table_76[256] = {
+ 0x3D, 0x66, 0x40, 0xC5, 0x1D, 0xF5, 0xE7, 0xB7,
+ 0x2C, 0x23, 0x09, 0xC2, 0x68, 0xE6, 0xD3, 0x8D,
+ 0x35, 0x94, 0x93, 0xF0, 0x43, 0x97, 0x2B, 0x4B,
+ 0x1A, 0xEB, 0x00, 0x4C, 0x6F, 0xE4, 0x92, 0xEA,
+ 0xB8, 0xA3, 0xA6, 0xEC, 0x11, 0x5E, 0x61, 0x81,
+ 0xE1, 0x48, 0xC9, 0xCB, 0xDB, 0x2E, 0x3B, 0xED,
+ 0x36, 0x52, 0x3A, 0xD2, 0x4F, 0x4E, 0x22, 0x96,
+ 0x57, 0x2D, 0x62, 0x53, 0xCF, 0xD9, 0x5B, 0x9F,
+ 0x8E, 0x78, 0xC6, 0x07, 0x7D, 0xA1, 0x02, 0xB4,
+ 0xF4, 0xB6, 0x34, 0x98, 0xDA, 0xA9, 0xD4, 0x54,
+ 0x99, 0x82, 0x0A, 0xD8, 0x88, 0x5D, 0x3C, 0xD0,
+ 0xAB, 0x31, 0xFB, 0x03, 0x17, 0x46, 0xE8, 0xE2,
+ 0xA4, 0xFF, 0xB0, 0xAA, 0xAD, 0x7C, 0x55, 0x49,
+ 0x75, 0x6B, 0x10, 0x24, 0xC0, 0x04, 0xB1, 0xBF,
+ 0x6A, 0xF6, 0x15, 0xEF, 0x5C, 0x60, 0x27, 0x3E,
+ 0x38, 0x63, 0xC1, 0x76, 0xFD, 0x84, 0xE0, 0xCD,
+ 0xFE, 0x30, 0xCE, 0xBB, 0xDC, 0x1E, 0x1B, 0xBC,
+ 0xB5, 0xE9, 0x9E, 0x8F, 0x0D, 0x3F, 0x91, 0x19,
+ 0x28, 0x37, 0x26, 0x42, 0x08, 0x9A, 0x0C, 0x83,
+ 0x90, 0x6D, 0x74, 0x65, 0xF2, 0x4A, 0xDE, 0x8B,
+ 0x67, 0x0E, 0x8C, 0x5F, 0xF9, 0x7F, 0x5A, 0x86,
+ 0x69, 0x45, 0x44, 0xD5, 0xF7, 0xE5, 0x8A, 0xA8,
+ 0xC8, 0x7E, 0x05, 0x64, 0xEE, 0x79, 0xBE, 0x7A,
+ 0x14, 0xD6, 0x50, 0x18, 0x25, 0xBD, 0x85, 0xE3,
+ 0xA2, 0x70, 0xCC, 0x59, 0x71, 0x77, 0xFA, 0x47,
+ 0x9B, 0x1F, 0x9D, 0xBA, 0x29, 0x4D, 0xF8, 0xDF,
+ 0xC4, 0x72, 0x2F, 0xAE, 0x06, 0x51, 0x41, 0xAF,
+ 0xF3, 0xDD, 0x87, 0xB2, 0x9C, 0xC7, 0x12, 0x16,
+ 0x20, 0xA7, 0x21, 0x73, 0xF1, 0x58, 0xD7, 0x7B,
+ 0xB9, 0xB3, 0x32, 0x01, 0x80, 0x1C, 0x39, 0x0B,
+ 0x13, 0x56, 0x6C, 0x89, 0x33, 0x6E, 0x2A, 0xA5,
+ 0xD1, 0x95, 0xC3, 0xA0, 0x0F, 0xCA, 0xAC, 0xFC };
+
+unsigned char table_77[32] = {
+ 0x1C, 0x0D, 0x1E, 0x01, 0x06, 0x16, 0x18, 0x17,
+ 0x0B, 0x1F, 0x04, 0x0F, 0x00, 0x19, 0x08, 0x0A,
+ 0x11, 0x03, 0x05, 0x07, 0x09, 0x0C, 0x15, 0x14,
+ 0x1A, 0x12, 0x13, 0x0E, 0x1D, 0x10, 0x02, 0x1B };
+
+unsigned char table_78[32] = {
+ 0x0E, 0x02, 0x17, 0x12, 0x1E, 0x09, 0x15, 0x03,
+ 0x01, 0x0B, 0x0F, 0x11, 0x10, 0x0A, 0x16, 0x06,
+ 0x07, 0x00, 0x1C, 0x1D, 0x1F, 0x0C, 0x18, 0x04,
+ 0x13, 0x0D, 0x1B, 0x08, 0x19, 0x14, 0x05, 0x1A };
+
+unsigned char table_79[32] = {
+ 0x12, 0x0B, 0x11, 0x01, 0x07, 0x0E, 0x1A, 0x0D,
+ 0x1E, 0x18, 0x14, 0x1F, 0x0A, 0x17, 0x19, 0x1B,
+ 0x00, 0x10, 0x0C, 0x08, 0x13, 0x02, 0x0F, 0x1D,
+ 0x09, 0x06, 0x04, 0x16, 0x15, 0x1C, 0x05, 0x03 };
+
+unsigned char table_80[256] = {
+ 0x14, 0xE7, 0x31, 0x0F, 0xD1, 0x5F, 0xED, 0x1E,
+ 0xA6, 0x77, 0x20, 0x57, 0x34, 0x64, 0x33, 0x0B,
+ 0x5A, 0xB4, 0x83, 0x62, 0xFD, 0x8E, 0xE4, 0xF3,
+ 0xBD, 0xA5, 0xC8, 0x6D, 0x3E, 0x4F, 0x01, 0x7A,
+ 0xD3, 0x45, 0x3C, 0xF2, 0x68, 0xFF, 0xE6, 0x84,
+ 0xC2, 0xC1, 0x53, 0x72, 0x8C, 0xA1, 0xC7, 0x00,
+ 0x89, 0x97, 0x69, 0xA4, 0xF8, 0xAA, 0xAD, 0x8F,
+ 0x24, 0xC6, 0x9A, 0xAC, 0xE5, 0xAB, 0x6B, 0x79,
+ 0x99, 0x60, 0x28, 0x2B, 0x3B, 0xAF, 0x1C, 0x80,
+ 0xA3, 0x8A, 0x1A, 0xB5, 0xE1, 0x9F, 0xDA, 0x78,
+ 0xD7, 0xC4, 0x87, 0x5D, 0xE9, 0x27, 0xFB, 0x18,
+ 0x94, 0x3A, 0xCE, 0x3F, 0xF6, 0x12, 0x75, 0x37,
+ 0x6E, 0x9E, 0x29, 0x6C, 0xF7, 0x7D, 0x92, 0x08,
+ 0x42, 0xB2, 0xBF, 0x0C, 0xB6, 0x25, 0xE0, 0x49,
+ 0x43, 0x91, 0x98, 0xBB, 0xDC, 0x63, 0xEA, 0xA8,
+ 0x74, 0x38, 0x35, 0xCD, 0x07, 0x70, 0x81, 0x41,
+ 0xC9, 0x51, 0xBC, 0xA9, 0x59, 0xD4, 0xB8, 0x2C,
+ 0x7C, 0x2D, 0xB3, 0x6F, 0x11, 0x86, 0x9D, 0x46,
+ 0xF0, 0x65, 0x76, 0x04, 0x0E, 0xCA, 0xBE, 0x5C,
+ 0xF9, 0x71, 0x9C, 0x21, 0x4C, 0x02, 0xFE, 0x8D,
+ 0xD5, 0x26, 0x40, 0xC3, 0x32, 0x9B, 0xB0, 0x5E,
+ 0x48, 0xC5, 0x85, 0x4B, 0x0A, 0xCC, 0x58, 0x52,
+ 0x61, 0x13, 0xEF, 0x4A, 0xEE, 0x03, 0xD9, 0xDE,
+ 0xA7, 0x19, 0x09, 0x7F, 0x5B, 0x96, 0xBA, 0x0D,
+ 0xCF, 0xD2, 0x06, 0x1F, 0xD8, 0xDB, 0xEC, 0xA0,
+ 0xDD, 0x66, 0x10, 0xA2, 0xDF, 0x30, 0xF4, 0x88,
+ 0xCB, 0x36, 0x82, 0xE3, 0x73, 0x17, 0x55, 0x15,
+ 0xF5, 0xB7, 0x23, 0xB1, 0xD6, 0xE2, 0x47, 0x7E,
+ 0x67, 0xE8, 0x1D, 0x16, 0x8B, 0xEB, 0xD0, 0x3D,
+ 0x6A, 0x54, 0x2A, 0x4E, 0x93, 0xFA, 0x44, 0x05,
+ 0x2F, 0x50, 0x2E, 0x95, 0xAE, 0x1B, 0x56, 0x7B,
+ 0x39, 0xB9, 0xC0, 0x22, 0xF1, 0x4D, 0x90, 0xFC };
+
+unsigned char table_81[32] = {
+ 0x03, 0x02, 0x1D, 0x0E, 0x09, 0x1A, 0x0C, 0x11,
+ 0x1C, 0x0D, 0x08, 0x12, 0x19, 0x10, 0x04, 0x17,
+ 0x15, 0x05, 0x0A, 0x00, 0x13, 0x16, 0x1B, 0x18,
+ 0x1E, 0x0B, 0x0F, 0x01, 0x07, 0x14, 0x1F, 0x06 };
+
+unsigned char table_82[256] = {
+ 0x53, 0xD3, 0x64, 0x89, 0x7D, 0xA5, 0x66, 0xA4,
+ 0x09, 0x46, 0x17, 0x2C, 0xAF, 0x8C, 0x21, 0x5F,
+ 0x3B, 0x22, 0xE3, 0x05, 0x07, 0x28, 0x2F, 0xAB,
+ 0xF4, 0x8E, 0x51, 0x31, 0x02, 0xC7, 0x48, 0x13,
+ 0x24, 0x12, 0xB8, 0xE5, 0xBD, 0xAE, 0x7E, 0xCC,
+ 0xC9, 0x98, 0x08, 0xEE, 0xDB, 0x1B, 0xE8, 0x3D,
+ 0x8F, 0xF2, 0xFB, 0x36, 0x4D, 0x94, 0x9C, 0x16,
+ 0xF7, 0x42, 0x9B, 0x2B, 0xFD, 0x7B, 0x77, 0x3F,
+ 0xC3, 0xFC, 0x23, 0x93, 0x50, 0x0C, 0x79, 0x18,
+ 0x47, 0xE1, 0xCB, 0xA7, 0xB6, 0x85, 0xE6, 0x61,
+ 0x2D, 0xD8, 0x9F, 0x80, 0xE9, 0x14, 0x0B, 0x1C,
+ 0x40, 0x76, 0x2A, 0x25, 0x0E, 0x99, 0xAC, 0xC4,
+ 0xEB, 0x29, 0x41, 0x8A, 0x73, 0x06, 0x57, 0xC6,
+ 0x8D, 0xFA, 0x5A, 0xCD, 0x67, 0xB2, 0xD9, 0x0A,
+ 0x1E, 0xEF, 0x3E, 0xA0, 0x45, 0x03, 0x27, 0xF1,
+ 0x38, 0x54, 0xC1, 0x7A, 0xFE, 0x52, 0x75, 0xD4,
+ 0x74, 0x7C, 0xD2, 0x68, 0xEA, 0x4C, 0x97, 0xF9,
+ 0xF5, 0x8B, 0x0F, 0x84, 0xA8, 0x6E, 0x9E, 0x11,
+ 0x6B, 0xBC, 0x4B, 0x6C, 0x9A, 0xF0, 0xA3, 0x1F,
+ 0x92, 0x19, 0xA2, 0x3A, 0x15, 0x04, 0xC5, 0x62,
+ 0xD5, 0x96, 0x90, 0x32, 0xAA, 0xD6, 0xCF, 0x35,
+ 0xB4, 0x81, 0x2E, 0x01, 0x10, 0x49, 0x70, 0xDE,
+ 0xDD, 0x88, 0xB9, 0x6D, 0x60, 0xBB, 0x44, 0xF8,
+ 0x3C, 0xEC, 0x34, 0x82, 0x95, 0x72, 0x58, 0x4E,
+ 0xE4, 0x0D, 0xBE, 0xDA, 0x83, 0x4A, 0x00, 0xBF,
+ 0xD0, 0xC8, 0x26, 0xB3, 0x65, 0x1A, 0x69, 0xCA,
+ 0xF3, 0xD7, 0x6F, 0x55, 0xE2, 0xFF, 0x5D, 0xDC,
+ 0x20, 0xF6, 0x63, 0xED, 0xE0, 0x59, 0x9D, 0xB1,
+ 0x1D, 0xAD, 0x91, 0xA1, 0xB7, 0xA9, 0xDF, 0xC0,
+ 0x39, 0xD1, 0x43, 0xCE, 0x4F, 0x5C, 0xE7, 0x37,
+ 0x5E, 0x33, 0x5B, 0xA6, 0xC2, 0xB0, 0xBA, 0x30,
+ 0x6A, 0x78, 0xB5, 0x71, 0x56, 0x87, 0x7F, 0x86 };
+
+unsigned char table_83[32] = {
+ 0x1B, 0x0A, 0x1F, 0x01, 0x10, 0x08, 0x0E, 0x18,
+ 0x06, 0x04, 0x00, 0x1C, 0x0C, 0x19, 0x0D, 0x16,
+ 0x02, 0x03, 0x09, 0x07, 0x13, 0x0F, 0x05, 0x12,
+ 0x17, 0x1E, 0x1A, 0x1D, 0x0B, 0x11, 0x14, 0x15 };
+
+unsigned char table_84[32] = {
+ 0x02, 0x1A, 0x0D, 0x15, 0x01, 0x16, 0x1E, 0x00,
+ 0x08, 0x1B, 0x04, 0x10, 0x1C, 0x18, 0x19, 0x14,
+ 0x0C, 0x11, 0x0B, 0x0E, 0x03, 0x0A, 0x07, 0x12,
+ 0x1D, 0x17, 0x13, 0x06, 0x0F, 0x05, 0x09, 0x1F };
+
+unsigned char table_85[256] = {
+ 0xC6, 0x7C, 0xCE, 0xBD, 0x84, 0x3E, 0x0B, 0xD8,
+ 0xFE, 0xCC, 0x46, 0x50, 0xD1, 0xFB, 0xA0, 0x6D,
+ 0xEA, 0xE2, 0x40, 0x51, 0x13, 0xB0, 0xD6, 0xB1,
+ 0xA8, 0xDF, 0x61, 0xA4, 0x80, 0x21, 0xB3, 0x33,
+ 0x06, 0x6B, 0xE3, 0x8C, 0xA1, 0x18, 0xBA, 0x03,
+ 0xD7, 0x8D, 0x54, 0x12, 0x4C, 0xEE, 0x9E, 0xCF,
+ 0x04, 0x2A, 0x08, 0xBB, 0xC2, 0xD4, 0xC3, 0x4A,
+ 0xD5, 0xFA, 0x36, 0x2F, 0x14, 0x3F, 0xED, 0x05,
+ 0x17, 0x28, 0x75, 0xFC, 0xA2, 0x1F, 0x4B, 0x6F,
+ 0x91, 0x7E, 0x4E, 0x96, 0x3B, 0xF3, 0x1D, 0x78,
+ 0xEB, 0x68, 0xF1, 0xA7, 0x9F, 0xC7, 0x59, 0x6C,
+ 0x92, 0xE6, 0x66, 0x07, 0x8A, 0x25, 0x26, 0x72,
+ 0x30, 0x5A, 0x81, 0x2C, 0x58, 0x32, 0xCB, 0xE0,
+ 0xF9, 0x48, 0x83, 0x9B, 0xA5, 0xE1, 0xA6, 0x64,
+ 0xFF, 0xC9, 0x8F, 0x53, 0x3D, 0x24, 0xC8, 0xDE,
+ 0x02, 0x7D, 0x09, 0xB4, 0x0A, 0x95, 0x0F, 0xE4,
+ 0xDB, 0xB7, 0x71, 0x4D, 0x1C, 0xAC, 0x35, 0xCD,
+ 0x29, 0xDD, 0xC1, 0xF2, 0xF4, 0xC0, 0x5C, 0x74,
+ 0xDC, 0x87, 0xFD, 0x4F, 0x11, 0x0E, 0x5D, 0x3C,
+ 0x01, 0x73, 0xE9, 0xD9, 0x10, 0x9A, 0x5B, 0xC5,
+ 0x98, 0x34, 0x15, 0xAE, 0xF7, 0xAA, 0x67, 0x23,
+ 0xBC, 0x8B, 0x7B, 0x65, 0xA9, 0xB6, 0x77, 0x00,
+ 0x19, 0x0C, 0x5E, 0x99, 0xF0, 0x55, 0x86, 0x97,
+ 0x69, 0xDA, 0x38, 0x9C, 0x16, 0xE8, 0x27, 0xAF,
+ 0x2E, 0x47, 0x6A, 0xD0, 0x79, 0x44, 0x45, 0x2B,
+ 0x5F, 0x85, 0xF5, 0x62, 0x70, 0x22, 0x7F, 0xF6,
+ 0x88, 0x93, 0x60, 0x42, 0x3A, 0x39, 0x49, 0x6E,
+ 0x89, 0x52, 0x20, 0xF8, 0xCA, 0xD2, 0x76, 0xB9,
+ 0xAB, 0x7A, 0x9D, 0xD3, 0xBE, 0x1A, 0xAD, 0x41,
+ 0x56, 0x31, 0x90, 0xB5, 0xB2, 0xEC, 0xA3, 0xE5,
+ 0x8E, 0x1B, 0xEF, 0xBF, 0x94, 0xC4, 0x0D, 0xB8,
+ 0x2D, 0x57, 0xE7, 0x82, 0x1E, 0x37, 0x63, 0x43 };
+
+unsigned char table_86[32] = {
+ 0x11, 0x07, 0x0F, 0x0A, 0x19, 0x1D, 0x0B, 0x09,
+ 0x1C, 0x1E, 0x14, 0x06, 0x0C, 0x16, 0x13, 0x04,
+ 0x15, 0x18, 0x00, 0x0D, 0x12, 0x05, 0x08, 0x02,
+ 0x10, 0x1A, 0x1F, 0x01, 0x17, 0x0E, 0x03, 0x1B };
+
+unsigned char table_87[32] = {
+ 0x17, 0x0E, 0x1D, 0x13, 0x0B, 0x19, 0x03, 0x06,
+ 0x09, 0x01, 0x0D, 0x15, 0x1C, 0x16, 0x18, 0x1B,
+ 0x11, 0x10, 0x00, 0x1E, 0x1F, 0x08, 0x12, 0x0F,
+ 0x02, 0x04, 0x07, 0x1A, 0x14, 0x0A, 0x0C, 0x05 };
+
+unsigned char table_88[32] = {
+ 0x09, 0x08, 0x17, 0x10, 0x0A, 0x07, 0x1C, 0x1F,
+ 0x04, 0x0E, 0x01, 0x0C, 0x0D, 0x1B, 0x03, 0x15,
+ 0x02, 0x1E, 0x18, 0x19, 0x0F, 0x06, 0x1A, 0x0B,
+ 0x05, 0x11, 0x14, 0x00, 0x16, 0x1D, 0x12, 0x13 };
+
+unsigned char table_89[32] = {
+ 0x15, 0x1C, 0x1D, 0x14, 0x0F, 0x1A, 0x05, 0x02,
+ 0x07, 0x09, 0x06, 0x08, 0x1F, 0x00, 0x10, 0x13,
+ 0x0D, 0x03, 0x0C, 0x18, 0x0E, 0x16, 0x1B, 0x1E,
+ 0x12, 0x04, 0x11, 0x0A, 0x01, 0x0B, 0x17, 0x19 };
+
+unsigned char table_90[256] = {
+ 0x62, 0x36, 0x64, 0x0E, 0x4C, 0x6C, 0xBE, 0xCF,
+ 0x25, 0x5A, 0x3D, 0x12, 0x54, 0x9F, 0xE7, 0xA5,
+ 0xDE, 0xD7, 0xB2, 0x60, 0x18, 0x8D, 0x89, 0x70,
+ 0x48, 0x66, 0x1C, 0xA6, 0x17, 0x9B, 0xDF, 0x9A,
+ 0x82, 0xB9, 0x2E, 0xFA, 0x83, 0x5B, 0x7A, 0x61,
+ 0xFC, 0x6B, 0x8B, 0x4E, 0x0F, 0xAD, 0x78, 0xE1,
+ 0xE8, 0x15, 0x1A, 0xF7, 0xA3, 0x3A, 0x04, 0xE3,
+ 0x30, 0x8C, 0x06, 0xC4, 0x05, 0x32, 0x1F, 0x6A,
+ 0xB8, 0x37, 0x58, 0xF5, 0x74, 0x63, 0xD4, 0xAC,
+ 0xA4, 0xF3, 0xEC, 0xBB, 0x8E, 0x65, 0xA0, 0xEE,
+ 0x6D, 0x11, 0xDD, 0xEA, 0x68, 0x2B, 0xDA, 0x0B,
+ 0xEF, 0xC3, 0x8F, 0x03, 0x77, 0x1B, 0xFB, 0x1E,
+ 0x5C, 0xD9, 0xCB, 0x33, 0x55, 0xF1, 0xA1, 0xF9,
+ 0x7C, 0x38, 0x95, 0x00, 0x6E, 0x85, 0xC2, 0x7F,
+ 0xBF, 0x84, 0x2A, 0x13, 0x72, 0x81, 0xE9, 0x59,
+ 0x41, 0x69, 0x3B, 0x0C, 0x90, 0xB4, 0x51, 0x2F,
+ 0xA2, 0xFE, 0xF8, 0x49, 0x57, 0xE5, 0x96, 0xFF,
+ 0xCD, 0xD5, 0xCE, 0xAA, 0x40, 0xB0, 0x4D, 0xBA,
+ 0xDB, 0xC7, 0x46, 0x86, 0xD1, 0xCA, 0xC0, 0x67,
+ 0x9C, 0x21, 0xAE, 0xB3, 0x7B, 0x87, 0xE2, 0x71,
+ 0xE6, 0x39, 0xA8, 0x22, 0x07, 0x2C, 0x44, 0x52,
+ 0xA7, 0xF0, 0x4A, 0x92, 0x56, 0x28, 0x43, 0x8A,
+ 0x5E, 0x53, 0x93, 0x47, 0x97, 0x88, 0x76, 0x79,
+ 0x91, 0x26, 0xC1, 0x3F, 0xB7, 0xF6, 0x3E, 0x80,
+ 0xA9, 0xC6, 0x01, 0xD2, 0xEB, 0x9E, 0x4B, 0xBC,
+ 0xC8, 0xB5, 0x02, 0x5F, 0x98, 0x9D, 0x5D, 0x35,
+ 0xD0, 0x16, 0xB1, 0x23, 0x7D, 0xAF, 0x10, 0x3C,
+ 0xAB, 0x14, 0x09, 0x2D, 0x0D, 0xC5, 0x1D, 0xD6,
+ 0x42, 0xF2, 0x34, 0x73, 0xF4, 0xFD, 0xE0, 0x24,
+ 0x6F, 0xD3, 0x75, 0xD8, 0xCC, 0xB6, 0x99, 0x4F,
+ 0x29, 0x0A, 0x08, 0xE4, 0x27, 0x19, 0x31, 0xC9,
+ 0x20, 0x94, 0x45, 0xED, 0xDC, 0xBD, 0x7E, 0x50 };
+
+unsigned char table_91[32] = {
+ 0x03, 0x04, 0x0C, 0x18, 0x10, 0x0D, 0x13, 0x1B,
+ 0x1F, 0x07, 0x11, 0x17, 0x1C, 0x1D, 0x05, 0x06,
+ 0x0A, 0x12, 0x02, 0x1A, 0x0B, 0x01, 0x0E, 0x08,
+ 0x14, 0x16, 0x00, 0x15, 0x19, 0x09, 0x0F, 0x1E };
+
+unsigned char table_92[32] = {
+ 0x1E, 0x10, 0x01, 0x07, 0x11, 0x16, 0x15, 0x17,
+ 0x1F, 0x14, 0x0C, 0x1C, 0x06, 0x03, 0x00, 0x18,
+ 0x08, 0x0E, 0x02, 0x1B, 0x09, 0x0D, 0x19, 0x05,
+ 0x0F, 0x12, 0x0B, 0x13, 0x0A, 0x04, 0x1D, 0x1A };
+
+unsigned char table_93[256] = {
+ 0x76, 0x78, 0xA2, 0x94, 0x0E, 0x7F, 0xDF, 0xC1,
+ 0xB9, 0xE1, 0x3D, 0x59, 0x6F, 0x1E, 0x53, 0x99,
+ 0x80, 0xE3, 0x21, 0xF8, 0x65, 0xB8, 0x08, 0xBC,
+ 0x29, 0x17, 0xFD, 0x33, 0x35, 0xF2, 0x70, 0xC7,
+ 0x25, 0xD0, 0xCD, 0x7A, 0xB7, 0x9B, 0xA5, 0xC3,
+ 0x00, 0x90, 0xDC, 0xB1, 0x0C, 0x20, 0x67, 0x8D,
+ 0x43, 0x49, 0xF3, 0x96, 0x14, 0x1A, 0xC8, 0x19,
+ 0x72, 0xD7, 0x8A, 0x38, 0x66, 0xDA, 0xDD, 0x2E,
+ 0xBE, 0xD5, 0x91, 0x7C, 0x3A, 0x92, 0x8E, 0xE7,
+ 0x51, 0xB5, 0xA8, 0xD9, 0x0B, 0x2A, 0xBA, 0x81,
+ 0x41, 0x0F, 0xBD, 0x4E, 0x31, 0x23, 0x9C, 0x8B,
+ 0x2B, 0x1D, 0x04, 0x3E, 0x8C, 0xF0, 0x45, 0xA0,
+ 0x1C, 0x44, 0x55, 0x5E, 0xF1, 0x98, 0x54, 0x5D,
+ 0x9D, 0x84, 0xAE, 0x09, 0xA9, 0xC5, 0x83, 0x60,
+ 0x86, 0x95, 0xB4, 0xFA, 0x6B, 0xA7, 0x9A, 0xCA,
+ 0x8F, 0x4F, 0x0A, 0x7B, 0xB0, 0x02, 0xEA, 0xA4,
+ 0x18, 0xDB, 0xD3, 0x64, 0xEB, 0xFC, 0xC4, 0xC9,
+ 0xF5, 0xD6, 0xCC, 0x75, 0x0D, 0x5C, 0x93, 0x4A,
+ 0x6D, 0xC0, 0x1F, 0x50, 0xE6, 0x16, 0xEE, 0x07,
+ 0xFB, 0x74, 0x56, 0x58, 0x52, 0x89, 0x79, 0x68,
+ 0xB6, 0xFE, 0x01, 0xD4, 0x7E, 0x06, 0xBF, 0xCB,
+ 0x5B, 0xC2, 0xC6, 0x32, 0xAC, 0x26, 0x22, 0xD2,
+ 0x82, 0x46, 0x69, 0x15, 0x2C, 0xF7, 0xAD, 0x13,
+ 0x4D, 0xA3, 0xF6, 0x2D, 0x48, 0x71, 0x57, 0x11,
+ 0x63, 0x05, 0x5F, 0x9E, 0x4B, 0xAB, 0xA6, 0x61,
+ 0xBB, 0xA1, 0x3C, 0x97, 0xF9, 0x03, 0x40, 0x12,
+ 0xCF, 0x37, 0xE4, 0x10, 0x6A, 0xED, 0xFF, 0x62,
+ 0x42, 0x4C, 0xAF, 0x9F, 0xE5, 0xE8, 0xD8, 0xD1,
+ 0x28, 0x3F, 0x1B, 0xE9, 0xCE, 0x6C, 0x27, 0x88,
+ 0xEF, 0x2F, 0xE0, 0x30, 0x87, 0x5A, 0x73, 0xB3,
+ 0x6E, 0x3B, 0x7D, 0x77, 0x36, 0xAA, 0x39, 0xDE,
+ 0x24, 0x34, 0xE2, 0xEC, 0x85, 0x47, 0xF4, 0xB2 };
+
+unsigned char table_94[32] = {
+ 0x1C, 0x07, 0x05, 0x1A, 0x10, 0x1D, 0x14, 0x12,
+ 0x08, 0x0F, 0x0C, 0x01, 0x04, 0x1B, 0x16, 0x0A,
+ 0x11, 0x02, 0x1F, 0x13, 0x0D, 0x1E, 0x17, 0x06,
+ 0x0E, 0x09, 0x15, 0x19, 0x03, 0x18, 0x00, 0x0B };
+
+unsigned char table_95[32] = {
+ 0x12, 0x10, 0x11, 0x15, 0x03, 0x0A, 0x14, 0x05,
+ 0x1D, 0x07, 0x17, 0x0D, 0x09, 0x08, 0x1B, 0x1F,
+ 0x0B, 0x06, 0x19, 0x0E, 0x18, 0x04, 0x00, 0x02,
+ 0x1E, 0x1C, 0x01, 0x0C, 0x1A, 0x0F, 0x13, 0x16 };
+
+unsigned char table_96[256] = {
+ 0x1C, 0x6E, 0xCD, 0xB4, 0xB3, 0x93, 0xA8, 0x2E,
+ 0x4F, 0x09, 0xE3, 0x72, 0x64, 0x13, 0x21, 0xF5,
+ 0x89, 0xB2, 0xD2, 0x22, 0x5D, 0x63, 0x90, 0xC4,
+ 0x42, 0x9B, 0x07, 0xCA, 0x16, 0x19, 0x5C, 0x2B,
+ 0x3D, 0xA0, 0x69, 0x5F, 0x52, 0x41, 0x66, 0xC0,
+ 0x55, 0xDA, 0x82, 0x40, 0x25, 0x02, 0x3C, 0xDD,
+ 0xAE, 0xD7, 0xD6, 0xDB, 0x04, 0x78, 0x05, 0x4A,
+ 0x4C, 0x81, 0x00, 0xBE, 0x45, 0xC5, 0x30, 0xB0,
+ 0x65, 0x5A, 0xA9, 0x38, 0x75, 0x26, 0x85, 0x4E,
+ 0xF0, 0xA2, 0x91, 0x8A, 0x54, 0xD0, 0x3E, 0x0D,
+ 0xFE, 0xF2, 0x0A, 0x23, 0x24, 0x37, 0x32, 0x0B,
+ 0xCB, 0xB5, 0x28, 0x6A, 0x95, 0x49, 0x53, 0x9A,
+ 0xEE, 0x2C, 0x9D, 0xD4, 0x1D, 0x46, 0xC9, 0x79,
+ 0xCC, 0xDF, 0x17, 0xE8, 0x6D, 0x29, 0x0E, 0x80,
+ 0xE0, 0x62, 0xA1, 0xFA, 0x10, 0xF6, 0x03, 0xC1,
+ 0x15, 0x14, 0x1F, 0x99, 0x97, 0xD5, 0x9E, 0x3F,
+ 0x7B, 0x2F, 0xEF, 0x2A, 0x68, 0x83, 0xE2, 0x1B,
+ 0xC8, 0x87, 0x12, 0x70, 0xC7, 0x36, 0xD3, 0x73,
+ 0x8B, 0x7D, 0x47, 0x9F, 0xD9, 0xFB, 0x6C, 0x5B,
+ 0xFC, 0xAA, 0xB9, 0xB1, 0x0C, 0x31, 0x8E, 0xF3,
+ 0x92, 0xA3, 0x4B, 0xF1, 0xC2, 0x3A, 0x67, 0xEA,
+ 0x77, 0x11, 0xB6, 0xE4, 0x1A, 0x33, 0xD1, 0xBA,
+ 0xF9, 0xAC, 0x43, 0xE5, 0xC3, 0xC6, 0xFD, 0xF4,
+ 0x44, 0x6F, 0xB7, 0x88, 0xA7, 0xF8, 0x34, 0x94,
+ 0x6B, 0x27, 0xDE, 0x1E, 0xDC, 0x01, 0x61, 0x50,
+ 0xAD, 0x74, 0x4D, 0x86, 0xF7, 0x8D, 0x9C, 0x0F,
+ 0x5E, 0xBD, 0x08, 0x84, 0x18, 0xED, 0xA5, 0x39,
+ 0xAB, 0x98, 0x48, 0xE6, 0x2D, 0x96, 0xCF, 0x7F,
+ 0xFF, 0xBB, 0x8F, 0xEC, 0xBF, 0xE7, 0x56, 0xA4,
+ 0x35, 0x76, 0xA6, 0xAF, 0xBC, 0x71, 0xE9, 0xB8,
+ 0x7E, 0x7C, 0x06, 0x3B, 0xEB, 0x60, 0x7A, 0x8C,
+ 0x59, 0xCE, 0xE1, 0x57, 0x20, 0x58, 0x51, 0xD8 };
+
+unsigned char table_97[256] = {
+ 0x15, 0x2D, 0xAF, 0x36, 0xCF, 0xD3, 0xD0, 0xED,
+ 0xB2, 0x1B, 0xFE, 0x92, 0xBD, 0xAD, 0x58, 0x0F,
+ 0x76, 0x3C, 0x47, 0x03, 0x2E, 0x4C, 0x40, 0xF7,
+ 0x39, 0xA7, 0x72, 0x22, 0x95, 0xF3, 0x8C, 0xE0,
+ 0x79, 0xB6, 0x75, 0x82, 0x94, 0x8F, 0x44, 0xFC,
+ 0xB0, 0x05, 0xE9, 0x10, 0x68, 0xE7, 0xF1, 0xA5,
+ 0xA8, 0xE2, 0x6F, 0xBE, 0xE5, 0x54, 0xA2, 0xC6,
+ 0xDB, 0x1C, 0x9E, 0x6D, 0x14, 0xA1, 0x26, 0x34,
+ 0x1E, 0x1A, 0x06, 0x53, 0xEE, 0x67, 0xA9, 0x73,
+ 0xD5, 0x59, 0x2F, 0x61, 0xE6, 0x74, 0xD6, 0x97,
+ 0xC0, 0x0C, 0xB1, 0x6E, 0x6C, 0x33, 0xC8, 0x77,
+ 0x8B, 0x49, 0x43, 0xE3, 0xB5, 0xDE, 0x6A, 0xA0,
+ 0x78, 0x2A, 0xC9, 0xF9, 0x9A, 0xDC, 0x90, 0x55,
+ 0xF4, 0x16, 0x5E, 0x3F, 0xC5, 0x7C, 0xFA, 0x09,
+ 0x8E, 0x87, 0xF2, 0x9D, 0x70, 0x27, 0x9B, 0xC4,
+ 0xCD, 0x91, 0x4B, 0xB4, 0x18, 0xE1, 0x3D, 0x5D,
+ 0x7A, 0xEA, 0xF0, 0x65, 0xB9, 0xF6, 0xC3, 0x66,
+ 0x21, 0x96, 0xD1, 0xB8, 0x56, 0x62, 0x48, 0x28,
+ 0x3A, 0x86, 0x63, 0xD4, 0xD7, 0x41, 0x8D, 0x20,
+ 0xC2, 0x98, 0x37, 0xD8, 0x85, 0x42, 0x0D, 0x31,
+ 0x84, 0x4E, 0x11, 0x46, 0x2B, 0x19, 0xCC, 0xB7,
+ 0x69, 0x13, 0x6B, 0x29, 0x38, 0x7E, 0x0E, 0xD2,
+ 0x3B, 0x60, 0x89, 0x7F, 0xEF, 0x07, 0x08, 0xCA,
+ 0xBF, 0x3E, 0xA3, 0xAA, 0x52, 0x4A, 0x45, 0x00,
+ 0xC7, 0xF8, 0x57, 0xEB, 0x93, 0x9C, 0x4D, 0x7B,
+ 0x2C, 0xBB, 0xFB, 0xFF, 0x35, 0x4F, 0x32, 0xA6,
+ 0x23, 0x8A, 0xDD, 0x12, 0xA4, 0x81, 0x17, 0x1D,
+ 0x1F, 0xCB, 0x0A, 0x71, 0x02, 0xAC, 0xDF, 0x24,
+ 0xAB, 0x7D, 0x30, 0x5C, 0x01, 0x5A, 0xBA, 0xEC,
+ 0x51, 0xF5, 0x0B, 0x64, 0xCE, 0xAE, 0x5B, 0x50,
+ 0x80, 0x88, 0xE8, 0x5F, 0x04, 0xDA, 0xE4, 0xBC,
+ 0x83, 0x25, 0x9F, 0xD9, 0x99, 0xC1, 0xFD, 0xB3 };
+
+unsigned char table_98[256] = {
+ 0xC8, 0xE6, 0x38, 0x93, 0xE5, 0x03, 0x18, 0x1F,
+ 0xE9, 0x5A, 0xB6, 0xAF, 0xC3, 0x95, 0x00, 0x51,
+ 0xC0, 0xFD, 0x32, 0xE8, 0x96, 0x57, 0xF0, 0xAA,
+ 0xDC, 0x71, 0xF8, 0x01, 0x40, 0x0A, 0x4F, 0xB0,
+ 0x1B, 0x9D, 0x16, 0x92, 0xF3, 0x5E, 0xA9, 0x3C,
+ 0xBE, 0x6A, 0xA7, 0xE3, 0x35, 0x0D, 0xAD, 0xDB,
+ 0x48, 0xE0, 0x7E, 0xC6, 0xB4, 0x6D, 0x17, 0x41,
+ 0x3E, 0xE2, 0x87, 0x12, 0xE1, 0x53, 0xD9, 0x8A,
+ 0xAC, 0xA6, 0xD8, 0xFA, 0x36, 0x0B, 0x06, 0xDF,
+ 0x6C, 0x4E, 0xA4, 0xBC, 0xC9, 0xEE, 0x44, 0x26,
+ 0xF2, 0xE4, 0x9E, 0x34, 0xEF, 0x05, 0x0F, 0x7F,
+ 0xD1, 0xCD, 0x67, 0x28, 0xC1, 0x8E, 0x7D, 0x90,
+ 0x8F, 0x60, 0x1E, 0x19, 0xBD, 0x77, 0xB8, 0xD5,
+ 0x3D, 0x8C, 0x31, 0x99, 0x08, 0xDD, 0x04, 0x30,
+ 0x61, 0xFB, 0xEB, 0x98, 0x15, 0xFC, 0x10, 0xDE,
+ 0x20, 0xBA, 0xA1, 0xB3, 0xD4, 0x91, 0x6F, 0x9F,
+ 0x94, 0x5B, 0x42, 0xCB, 0x75, 0x1C, 0xBB, 0x5C,
+ 0x5D, 0xD6, 0x66, 0x50, 0xB9, 0xF1, 0x82, 0x7B,
+ 0x33, 0x23, 0x4A, 0xA5, 0x55, 0x97, 0xEA, 0x37,
+ 0xF4, 0x64, 0x6E, 0xBF, 0x8B, 0xB1, 0x07, 0x9A,
+ 0x43, 0x11, 0x65, 0xC2, 0x02, 0xDA, 0x9B, 0x25,
+ 0xCA, 0x3B, 0x7A, 0xCE, 0xA8, 0xCF, 0xF7, 0x56,
+ 0x6B, 0xF9, 0x47, 0x2A, 0x2E, 0x1D, 0x2D, 0xE7,
+ 0x46, 0xD0, 0x62, 0x4C, 0x80, 0x4B, 0x2B, 0xF5,
+ 0x69, 0x9C, 0x45, 0xED, 0x83, 0xAB, 0x74, 0x39,
+ 0xA3, 0x85, 0xD7, 0x5F, 0xB2, 0x86, 0x22, 0x29,
+ 0x89, 0x49, 0x1A, 0xC4, 0x52, 0xEC, 0x8D, 0x73,
+ 0xD3, 0x7C, 0x79, 0xD2, 0x14, 0x4D, 0x84, 0xA2,
+ 0x0E, 0x70, 0x78, 0x72, 0xB7, 0xA0, 0xC5, 0x81,
+ 0x58, 0x0C, 0x68, 0x27, 0xFF, 0xF6, 0xAE, 0xCC,
+ 0x88, 0xFE, 0x24, 0x2F, 0x76, 0x3F, 0x59, 0x21,
+ 0x54, 0x3A, 0x13, 0x09, 0x2C, 0xB5, 0xC7, 0x63 };
+
+unsigned char table_99[32] = {
+ 0x19, 0x00, 0x10, 0x18, 0x09, 0x11, 0x13, 0x1D,
+ 0x08, 0x1A, 0x02, 0x05, 0x03, 0x17, 0x12, 0x01,
+ 0x1F, 0x14, 0x06, 0x07, 0x15, 0x0D, 0x0F, 0x0B,
+ 0x0E, 0x16, 0x1E, 0x04, 0x1B, 0x0A, 0x0C, 0x1C };
+
+unsigned char table_100[256] = {
+ 0x9B, 0x3A, 0xAE, 0x60, 0x27, 0x67, 0x1E, 0x4E,
+ 0x91, 0xDA, 0x85, 0x43, 0x5C, 0xCC, 0x89, 0x55,
+ 0x75, 0x56, 0xF2, 0x86, 0xEB, 0xC4, 0x0D, 0xE6,
+ 0x63, 0x88, 0x38, 0x59, 0x68, 0xD0, 0x18, 0xF0,
+ 0xBA, 0x28, 0xF5, 0x80, 0x02, 0x5B, 0xE1, 0xA4,
+ 0x7A, 0x4B, 0x8E, 0xF7, 0x9E, 0x99, 0x70, 0xEF,
+ 0x66, 0x50, 0xB1, 0xCD, 0x9A, 0xAF, 0x5F, 0x21,
+ 0xE5, 0x5D, 0x14, 0xD4, 0x34, 0x22, 0xC3, 0x0F,
+ 0x44, 0xB6, 0x92, 0xCE, 0xB4, 0x6E, 0xB0, 0x00,
+ 0xF9, 0xB5, 0x10, 0xEA, 0x45, 0x2F, 0x2B, 0xF4,
+ 0xF6, 0xFE, 0xCB, 0x0A, 0x42, 0xF8, 0xE7, 0xFD,
+ 0xC8, 0xC2, 0x6C, 0x9C, 0x57, 0xA1, 0x46, 0x04,
+ 0xE9, 0x97, 0x40, 0x32, 0x19, 0xFA, 0x51, 0xD1,
+ 0x6D, 0x4C, 0x2A, 0xD9, 0x95, 0x26, 0x72, 0x1B,
+ 0x83, 0x93, 0x5A, 0x15, 0x33, 0xC5, 0x77, 0x13,
+ 0xE0, 0x36, 0x37, 0xDB, 0xA7, 0xC7, 0x81, 0x62,
+ 0xC1, 0x47, 0x64, 0x74, 0x1D, 0x84, 0x29, 0x39,
+ 0x41, 0x35, 0x09, 0x90, 0x20, 0x9F, 0x8C, 0x7D,
+ 0x3E, 0x07, 0xB9, 0x76, 0x06, 0xA3, 0x31, 0x7F,
+ 0x49, 0x6F, 0x3D, 0xD5, 0x25, 0xAC, 0xDF, 0x0B,
+ 0x3C, 0x79, 0x01, 0x8F, 0x82, 0x2E, 0xFC, 0x98,
+ 0xA5, 0x58, 0xA0, 0x4A, 0x7C, 0x24, 0xDD, 0x05,
+ 0x4D, 0x12, 0xBC, 0xAA, 0xE2, 0xAB, 0xD3, 0xBF,
+ 0x94, 0x2D, 0x54, 0xBB, 0xAD, 0xB7, 0x6A, 0xE3,
+ 0xBD, 0x5E, 0x8D, 0x08, 0x3B, 0xB8, 0x73, 0x8A,
+ 0x16, 0xD2, 0x69, 0xE8, 0xEE, 0x53, 0xD8, 0xDC,
+ 0x48, 0xCF, 0xC6, 0xA9, 0x1A, 0xCA, 0x17, 0x11,
+ 0xED, 0xC0, 0xA6, 0x1F, 0x96, 0x8B, 0xFF, 0x78,
+ 0x03, 0x61, 0x1C, 0xA8, 0x3F, 0x9D, 0x0E, 0xC9,
+ 0xE4, 0xA2, 0x52, 0xEC, 0x4F, 0xD6, 0xF3, 0x6B,
+ 0x87, 0xB3, 0x7E, 0xDE, 0xD7, 0x71, 0x65, 0xF1,
+ 0x30, 0x0C, 0xB2, 0x7B, 0xBE, 0xFB, 0x23, 0x2C };
+
+unsigned char table_101[32] = {
+ 0x18, 0x08, 0x14, 0x17, 0x03, 0x10, 0x19, 0x04,
+ 0x0D, 0x1C, 0x06, 0x1D, 0x1E, 0x12, 0x11, 0x0B,
+ 0x0F, 0x02, 0x0E, 0x1B, 0x13, 0x05, 0x07, 0x16,
+ 0x15, 0x0A, 0x0C, 0x1A, 0x00, 0x01, 0x1F, 0x09 };
+
+unsigned char table_102[32] = {
+ 0x17, 0x1F, 0x0E, 0x05, 0x13, 0x0C, 0x14, 0x1A,
+ 0x0F, 0x01, 0x12, 0x1C, 0x00, 0x07, 0x0D, 0x02,
+ 0x10, 0x16, 0x04, 0x11, 0x1D, 0x03, 0x1E, 0x18,
+ 0x06, 0x15, 0x0A, 0x19, 0x09, 0x08, 0x1B, 0x0B };
+
+unsigned char table_103[32] = {
+ 0x0F, 0x09, 0x1E, 0x11, 0x0D, 0x08, 0x10, 0x00,
+ 0x01, 0x1F, 0x1D, 0x1C, 0x12, 0x04, 0x07, 0x05,
+ 0x19, 0x14, 0x1B, 0x02, 0x1A, 0x15, 0x17, 0x16,
+ 0x18, 0x0B, 0x0A, 0x13, 0x0C, 0x0E, 0x03, 0x06 };
+
+unsigned char table_104[256] = {
+ 0xA4, 0x9F, 0x78, 0x39, 0x3D, 0x81, 0x51, 0x24,
+ 0x46, 0x2A, 0x56, 0xE8, 0xDF, 0x73, 0xA8, 0xA2,
+ 0x0D, 0xDC, 0xA5, 0x4F, 0xF0, 0x93, 0xC0, 0x76,
+ 0x38, 0x70, 0xB0, 0x30, 0x98, 0x13, 0x8B, 0x14,
+ 0x26, 0x45, 0x0F, 0x7D, 0x34, 0x72, 0x6B, 0x89,
+ 0x43, 0xE2, 0x96, 0x5B, 0xEF, 0x2B, 0xF9, 0xDE,
+ 0x82, 0xB5, 0x61, 0x4A, 0x17, 0xC2, 0x5A, 0xCB,
+ 0xB2, 0x8D, 0xE4, 0xEC, 0xD9, 0x80, 0xBC, 0x62,
+ 0x67, 0x11, 0xA9, 0x3A, 0xE1, 0xC4, 0xEA, 0xD2,
+ 0x71, 0xD0, 0xDB, 0xE5, 0x7B, 0x08, 0x77, 0xD6,
+ 0x10, 0x19, 0x48, 0xEB, 0xAA, 0x2C, 0x0C, 0x59,
+ 0xBE, 0xF6, 0x28, 0x50, 0x90, 0x87, 0xCD, 0x04,
+ 0x1F, 0x79, 0x99, 0x5C, 0x49, 0x06, 0x8A, 0x3E,
+ 0x5F, 0x5E, 0x15, 0x23, 0x2D, 0xB6, 0xA6, 0x7A,
+ 0x03, 0x20, 0xDA, 0xFB, 0x35, 0x75, 0xC7, 0x47,
+ 0xB9, 0x7C, 0xA1, 0xCE, 0xC5, 0xDD, 0xFD, 0x6C,
+ 0x05, 0xAC, 0x09, 0xB4, 0x95, 0xD1, 0xB1, 0x63,
+ 0xFF, 0xAE, 0xD5, 0x25, 0x1E, 0x6E, 0x57, 0x18,
+ 0x74, 0xE6, 0x2F, 0x9A, 0xE7, 0x42, 0x65, 0xF5,
+ 0x58, 0x27, 0x33, 0x9C, 0xCF, 0xB7, 0xC3, 0xF1,
+ 0x12, 0x1D, 0xB8, 0xF4, 0x64, 0x4D, 0xD4, 0xBD,
+ 0xE3, 0xAB, 0x44, 0x60, 0xAF, 0xCC, 0x0A, 0xFC,
+ 0xD3, 0x21, 0x0B, 0x1A, 0x6D, 0x83, 0xA7, 0x8E,
+ 0x3C, 0xC1, 0xED, 0xF3, 0x2E, 0x86, 0xC9, 0x41,
+ 0x02, 0xF7, 0xC8, 0x40, 0x1B, 0xF8, 0xF2, 0x07,
+ 0x5D, 0x4E, 0xC6, 0x29, 0xD7, 0x4B, 0x7E, 0x31,
+ 0x94, 0x32, 0x01, 0x92, 0xE9, 0x36, 0x0E, 0x7F,
+ 0x85, 0x16, 0xFA, 0x00, 0x88, 0x3F, 0x68, 0x4C,
+ 0x22, 0x55, 0xBF, 0x9D, 0xE0, 0x6A, 0xAD, 0xBA,
+ 0x91, 0xCA, 0xA3, 0x1C, 0xEE, 0xD8, 0x3B, 0x66,
+ 0x69, 0x9B, 0x84, 0xA0, 0xB3, 0x6F, 0xFE, 0x52,
+ 0x97, 0xBB, 0x37, 0x8C, 0x54, 0x53, 0x9E, 0x8F };
+
+unsigned char table_105[256] = {
+ 0x7B, 0x35, 0x11, 0x79, 0x07, 0x2F, 0xF6, 0x82,
+ 0x8E, 0xB4, 0x6E, 0xD2, 0x6D, 0xC5, 0x8C, 0x1C,
+ 0xE0, 0xD6, 0x34, 0xF0, 0x4F, 0x25, 0x59, 0xE8,
+ 0xDF, 0x1D, 0xEB, 0x32, 0x86, 0x51, 0xA4, 0xF2,
+ 0x5C, 0xD1, 0xC8, 0x41, 0xEC, 0x9D, 0x62, 0xAC,
+ 0xDD, 0x3E, 0xB8, 0x65, 0x75, 0x89, 0x12, 0x6C,
+ 0x40, 0x4E, 0xC7, 0x27, 0xE1, 0x37, 0xCF, 0x09,
+ 0x16, 0x78, 0xAA, 0x58, 0x0D, 0xE6, 0x54, 0xFE,
+ 0x8F, 0xFD, 0xF9, 0x61, 0x26, 0x3F, 0x2E, 0xCD,
+ 0x2C, 0x04, 0xB2, 0x80, 0x0F, 0x14, 0x6F, 0xC6,
+ 0xAB, 0xFB, 0x13, 0xDB, 0x9A, 0x21, 0xB3, 0xC0,
+ 0xA9, 0x19, 0x70, 0xF3, 0x2B, 0xAE, 0x9B, 0x49,
+ 0xB7, 0xA8, 0x24, 0x1B, 0x48, 0xEA, 0xED, 0xD9,
+ 0x47, 0x9E, 0x9C, 0x69, 0x3C, 0x66, 0xBB, 0x06,
+ 0x46, 0x38, 0x17, 0xB5, 0xCB, 0x05, 0x4A, 0x5E,
+ 0x15, 0x20, 0xB9, 0xB6, 0x33, 0x4C, 0x7D, 0xA3,
+ 0xD7, 0xB1, 0x23, 0x72, 0xC3, 0x4B, 0x63, 0xBE,
+ 0xF7, 0x5B, 0x74, 0x64, 0x77, 0xCC, 0xD3, 0x85,
+ 0xDE, 0x1A, 0x31, 0x97, 0xA2, 0x8B, 0xFC, 0x10,
+ 0x5F, 0xDC, 0xD5, 0xB0, 0xBD, 0x55, 0xC1, 0xE7,
+ 0x0C, 0x50, 0x43, 0x39, 0x71, 0x52, 0xE5, 0xAF,
+ 0x8A, 0x60, 0x92, 0x2D, 0xD8, 0x03, 0xF5, 0x28,
+ 0xCA, 0xEF, 0xD0, 0xC2, 0x53, 0x91, 0xA6, 0x73,
+ 0x56, 0xA5, 0xF1, 0x57, 0x42, 0xF4, 0xD4, 0x36,
+ 0x8D, 0xBC, 0xE9, 0x7E, 0x02, 0x76, 0x18, 0x0B,
+ 0x84, 0x5A, 0xE2, 0xBF, 0x68, 0x95, 0x29, 0x98,
+ 0xAD, 0x88, 0x1F, 0x81, 0x67, 0xA1, 0x3A, 0xA7,
+ 0x22, 0xF8, 0x01, 0xA0, 0xCE, 0x7A, 0xDA, 0x30,
+ 0xC4, 0xE4, 0xEE, 0x7C, 0x3B, 0x4D, 0x3D, 0xE3,
+ 0xFA, 0x6A, 0x7F, 0x99, 0x00, 0x93, 0x0E, 0xFF,
+ 0x90, 0x0A, 0x2A, 0x5D, 0x96, 0x08, 0x6B, 0x83,
+ 0xBA, 0x1E, 0x44, 0x87, 0x45, 0x9F, 0xC9, 0x94 };
+
+unsigned char table_106[32] = {
+ 0x03, 0x11, 0x07, 0x1B, 0x0F, 0x14, 0x0C, 0x01,
+ 0x04, 0x02, 0x09, 0x0A, 0x05, 0x12, 0x06, 0x1F,
+ 0x1C, 0x0E, 0x0D, 0x15, 0x18, 0x08, 0x00, 0x10,
+ 0x1E, 0x1D, 0x17, 0x19, 0x13, 0x16, 0x0B, 0x1A };
+
+unsigned char table_107[32] = {
+ 0x13, 0x1B, 0x06, 0x11, 0x1C, 0x07, 0x08, 0x0E,
+ 0x10, 0x05, 0x09, 0x18, 0x04, 0x15, 0x1E, 0x0F,
+ 0x1F, 0x12, 0x02, 0x00, 0x17, 0x19, 0x1A, 0x0D,
+ 0x03, 0x0C, 0x0A, 0x1D, 0x14, 0x01, 0x16, 0x0B };
+
+unsigned char table_108[256] = {
+ 0x99, 0xA3, 0x48, 0xE8, 0x5A, 0x7D, 0x97, 0xCA,
+ 0x7F, 0x06, 0x9B, 0x04, 0xE0, 0xF3, 0x18, 0xAE,
+ 0x59, 0xA0, 0x2B, 0x15, 0x85, 0x3E, 0x12, 0x93,
+ 0x3D, 0x28, 0x32, 0xF5, 0x20, 0x5D, 0x86, 0x00,
+ 0x1B, 0x2E, 0x36, 0x10, 0x5E, 0x6C, 0xD8, 0x29,
+ 0xB6, 0x3F, 0x05, 0x1C, 0xCE, 0xC2, 0x34, 0x5F,
+ 0x5C, 0x79, 0xD1, 0x1F, 0xA2, 0xEE, 0x8A, 0x69,
+ 0xB5, 0x87, 0x96, 0x6D, 0x4D, 0xC1, 0x61, 0x2C,
+ 0x11, 0xE7, 0x8E, 0xBF, 0x1E, 0x53, 0xD0, 0x58,
+ 0x76, 0xA4, 0x60, 0xA9, 0xB0, 0xF9, 0xEA, 0x3C,
+ 0x52, 0x9A, 0x24, 0xF1, 0x9F, 0xD3, 0x40, 0x0A,
+ 0x63, 0x78, 0x6A, 0x8B, 0x08, 0x22, 0x16, 0x83,
+ 0x6B, 0xD2, 0x49, 0x19, 0xBD, 0xFD, 0x62, 0x72,
+ 0xA8, 0x55, 0xAB, 0x0C, 0xB9, 0x13, 0xD5, 0xF0,
+ 0xF2, 0x84, 0xAF, 0x2F, 0x7B, 0x2A, 0x21, 0x0F,
+ 0xDA, 0x30, 0x71, 0xD6, 0x81, 0xE6, 0xEC, 0x41,
+ 0x90, 0x50, 0x66, 0x0E, 0xA7, 0xB8, 0xF7, 0x3A,
+ 0xB2, 0xCF, 0x3B, 0xFC, 0x56, 0x6F, 0xC3, 0xA6,
+ 0xC9, 0xA1, 0x8D, 0xBB, 0x9D, 0x75, 0xF6, 0xAA,
+ 0x7E, 0xF8, 0x33, 0xEF, 0xBC, 0x7C, 0x23, 0x1A,
+ 0x92, 0x6E, 0x2D, 0x8F, 0xED, 0xB7, 0xB1, 0x1D,
+ 0x67, 0x39, 0xAC, 0x0D, 0x74, 0xDB, 0x7A, 0x94,
+ 0x07, 0x09, 0xC0, 0xD7, 0xAD, 0xFE, 0x54, 0x91,
+ 0xDE, 0x45, 0xA5, 0x77, 0xCB, 0x37, 0xC6, 0x38,
+ 0x89, 0x88, 0x17, 0xD9, 0x4F, 0xDF, 0x25, 0xFB,
+ 0xFA, 0x4C, 0x80, 0x35, 0x82, 0xF4, 0x95, 0xC8,
+ 0xFF, 0xE9, 0x31, 0x01, 0x14, 0xB3, 0x02, 0x9E,
+ 0x4E, 0x43, 0x46, 0xC7, 0xEB, 0x51, 0xE5, 0x47,
+ 0xB4, 0xE3, 0xDC, 0x57, 0xC4, 0x98, 0x03, 0xE1,
+ 0xBA, 0x68, 0xCD, 0x27, 0xC5, 0x0B, 0xD4, 0x64,
+ 0x4B, 0x9C, 0x70, 0x65, 0x4A, 0xE4, 0x42, 0xDD,
+ 0xCC, 0xE2, 0x44, 0x73, 0xBE, 0x26, 0x8C, 0x5B };
+
+unsigned char table_109[256] = {
+ 0xE3, 0x95, 0xDB, 0x09, 0x82, 0x0A, 0x8F, 0x9E,
+ 0xC9, 0xDC, 0x28, 0x35, 0x0F, 0x8B, 0xA8, 0xA5,
+ 0x7F, 0x3D, 0x8C, 0xD1, 0x93, 0x57, 0x04, 0xAA,
+ 0x6A, 0x98, 0x81, 0xDD, 0x16, 0x67, 0x2E, 0xDF,
+ 0xED, 0xF7, 0xB2, 0xBD, 0x14, 0xB6, 0x76, 0xC8,
+ 0x75, 0x9F, 0x48, 0xAE, 0xBB, 0xB0, 0xF3, 0xE2,
+ 0xD4, 0x59, 0xD8, 0x9C, 0x64, 0xC1, 0x73, 0x21,
+ 0x6D, 0x96, 0x7B, 0x62, 0x56, 0x55, 0xCC, 0xFD,
+ 0xCE, 0x41, 0xA3, 0x43, 0x33, 0xAF, 0x23, 0x9D,
+ 0x6F, 0x65, 0x19, 0x52, 0xAD, 0xC6, 0xD3, 0x3F,
+ 0x66, 0xFF, 0xD0, 0x30, 0x6C, 0xC0, 0xEB, 0xCF,
+ 0x51, 0x88, 0x38, 0x72, 0x69, 0x77, 0x3B, 0xFA,
+ 0xBA, 0xB7, 0xA1, 0x91, 0xE0, 0x89, 0xAB, 0x44,
+ 0x1B, 0x05, 0x5B, 0xB9, 0x71, 0x47, 0x7E, 0xFB,
+ 0x02, 0xC7, 0x99, 0x6E, 0x42, 0x20, 0x90, 0x1F,
+ 0x4A, 0x85, 0x1A, 0xEA, 0x0C, 0x0D, 0xB3, 0xDA,
+ 0xE7, 0x13, 0xE6, 0xD7, 0x6B, 0x12, 0x46, 0x53,
+ 0xB5, 0xF8, 0x1D, 0x83, 0x54, 0x49, 0x8A, 0x26,
+ 0x4D, 0xDE, 0xF6, 0x03, 0xA2, 0x7D, 0x0E, 0xA0,
+ 0x68, 0x79, 0xCA, 0x0B, 0x5D, 0x40, 0x4F, 0x80,
+ 0xC2, 0xD6, 0x87, 0x70, 0xF0, 0xD2, 0x92, 0xEE,
+ 0xBE, 0x74, 0x5F, 0xBC, 0xA4, 0x4B, 0xFE, 0x37,
+ 0x60, 0xA9, 0x06, 0xA7, 0xE1, 0xF5, 0x2B, 0x10,
+ 0xEF, 0x2C, 0x07, 0x86, 0x7A, 0x27, 0xE9, 0xC5,
+ 0xAC, 0x32, 0x22, 0xF2, 0xE5, 0x8D, 0x31, 0x01,
+ 0x34, 0xA6, 0xB8, 0xC3, 0x3C, 0xE4, 0x08, 0x94,
+ 0x15, 0x4E, 0xB4, 0x39, 0x58, 0x00, 0x3E, 0x29,
+ 0x45, 0x3A, 0x84, 0x36, 0xF1, 0x2A, 0x50, 0x11,
+ 0xC4, 0x5A, 0xFC, 0xBF, 0xD9, 0xF9, 0x17, 0x9B,
+ 0x8E, 0x18, 0x63, 0x4C, 0x2F, 0x78, 0x2D, 0x5E,
+ 0x9A, 0xCD, 0x24, 0xEC, 0x7C, 0x97, 0x61, 0xCB,
+ 0x1E, 0xF4, 0xD5, 0xB1, 0x5C, 0x25, 0xE8, 0x1C };
+
+unsigned char table_110[256] = {
+ 0xC3, 0x06, 0x3C, 0xCB, 0xD2, 0x44, 0x9D, 0x48,
+ 0x28, 0xAA, 0xA9, 0xD0, 0x64, 0x25, 0x56, 0xCA,
+ 0xC2, 0xF8, 0x5C, 0xAE, 0x4E, 0x63, 0xB2, 0xE9,
+ 0x35, 0x11, 0xA8, 0x1A, 0x76, 0x15, 0xE0, 0x26,
+ 0x97, 0x99, 0xD4, 0x43, 0x80, 0xEE, 0xC1, 0x69,
+ 0xA6, 0x1E, 0x7A, 0x42, 0x55, 0x38, 0xBF, 0x75,
+ 0x0E, 0x29, 0xF5, 0xF3, 0x36, 0x7D, 0x51, 0xE8,
+ 0xE5, 0xEB, 0x68, 0x60, 0x0C, 0x70, 0xFD, 0xCC,
+ 0xE3, 0x23, 0x09, 0x6D, 0x2D, 0x6C, 0x5E, 0xB6,
+ 0x98, 0x8B, 0x1F, 0x50, 0x34, 0x8D, 0x10, 0x92,
+ 0x82, 0x85, 0xD5, 0x79, 0x02, 0xA4, 0x0A, 0xBC,
+ 0x40, 0xC6, 0xA3, 0x72, 0x8F, 0xC4, 0xA5, 0xE4,
+ 0x49, 0xD6, 0xCE, 0xA1, 0x12, 0x4F, 0x30, 0x31,
+ 0xDE, 0x2A, 0xF7, 0x95, 0xB5, 0x96, 0x14, 0x08,
+ 0xE6, 0x3D, 0x86, 0xF2, 0x47, 0x74, 0xB8, 0x5D,
+ 0x1D, 0x2B, 0x3A, 0x93, 0x7C, 0x6A, 0x01, 0xA0,
+ 0x9A, 0x4D, 0xB7, 0x71, 0xA7, 0x41, 0xC5, 0x65,
+ 0xC8, 0x89, 0xD1, 0x3E, 0x0D, 0xD8, 0xFF, 0x6F,
+ 0x7F, 0xA2, 0xFE, 0xD9, 0xF0, 0x4A, 0x07, 0x1C,
+ 0x0F, 0x6E, 0x03, 0x81, 0x1B, 0x05, 0xDF, 0x52,
+ 0xF1, 0x8A, 0xF9, 0xDD, 0x91, 0x3B, 0xD7, 0xE1,
+ 0x54, 0xAD, 0x90, 0x5A, 0x7B, 0xC7, 0x32, 0x62,
+ 0x16, 0x27, 0xB9, 0x66, 0x21, 0x88, 0xBD, 0x18,
+ 0x77, 0x8E, 0x94, 0x8C, 0x9B, 0x46, 0x9C, 0xB1,
+ 0xD3, 0x53, 0xB0, 0xBE, 0xAC, 0xAF, 0x73, 0x24,
+ 0xDA, 0x58, 0xE2, 0xFC, 0x78, 0xEA, 0xCD, 0xFA,
+ 0x37, 0xED, 0x13, 0x19, 0xC0, 0x59, 0x83, 0xBA,
+ 0x3F, 0x57, 0x00, 0x7E, 0xC9, 0x2E, 0x17, 0x5B,
+ 0x84, 0xF6, 0xE7, 0x22, 0xFB, 0x5F, 0x4C, 0x2C,
+ 0x61, 0x9F, 0x45, 0x39, 0xB3, 0xEC, 0x04, 0x87,
+ 0x67, 0xDC, 0x0B, 0xF4, 0x20, 0xAB, 0x6B, 0x9E,
+ 0x4B, 0xCF, 0xB4, 0x2F, 0xBB, 0xEF, 0xDB, 0x33 };
+
+unsigned char table_111[32] = {
+ 0x09, 0x0F, 0x00, 0x15, 0x12, 0x17, 0x1A, 0x0D,
+ 0x1C, 0x0B, 0x01, 0x0A, 0x05, 0x1E, 0x1D, 0x0C,
+ 0x1B, 0x08, 0x19, 0x18, 0x14, 0x07, 0x0E, 0x03,
+ 0x10, 0x16, 0x11, 0x1F, 0x04, 0x06, 0x02, 0x13 };
+
+unsigned char table_112[256] = {
+ 0xF9, 0x7D, 0xBE, 0xD5, 0x9F, 0xB8, 0x95, 0x43,
+ 0xDB, 0xAE, 0x7E, 0xEC, 0x5B, 0x58, 0x18, 0x49,
+ 0x4B, 0x9D, 0x1C, 0x3E, 0x61, 0xD1, 0xF6, 0x2F,
+ 0x41, 0x82, 0x51, 0x37, 0x72, 0x79, 0x05, 0x2A,
+ 0xC2, 0xB0, 0xE2, 0xE7, 0xB2, 0xF3, 0x1B, 0x92,
+ 0x86, 0xBB, 0xDC, 0x90, 0x1A, 0x19, 0xD7, 0xBA,
+ 0x2C, 0x7B, 0xEF, 0xC7, 0x8A, 0x81, 0xEB, 0xDE,
+ 0x73, 0x4E, 0xB7, 0x97, 0xCA, 0x29, 0x85, 0xC1,
+ 0xA5, 0x7F, 0xFE, 0x56, 0xE9, 0x9E, 0x21, 0x76,
+ 0x3A, 0x88, 0x70, 0xC6, 0xD3, 0x8C, 0x47, 0xC8,
+ 0x83, 0x48, 0xC3, 0x6A, 0x9C, 0x80, 0x53, 0xBD,
+ 0xFD, 0x54, 0x09, 0x91, 0x94, 0xAA, 0x7A, 0x59,
+ 0x71, 0xDD, 0xA8, 0x07, 0xCB, 0x0F, 0xE0, 0x9A,
+ 0x36, 0x4C, 0x4D, 0x0D, 0xA4, 0x96, 0x6F, 0x14,
+ 0x22, 0x38, 0xAD, 0x02, 0xF4, 0x0B, 0xEA, 0x93,
+ 0x20, 0x04, 0xBC, 0xE8, 0x6C, 0xFB, 0x10, 0x6B,
+ 0x40, 0xB6, 0x24, 0x17, 0x06, 0x31, 0xD9, 0x33,
+ 0xF5, 0x99, 0x57, 0xCD, 0xAB, 0x67, 0x5C, 0x30,
+ 0x1E, 0x34, 0xB4, 0x3F, 0x16, 0x42, 0xA2, 0x68,
+ 0x27, 0xB3, 0x1D, 0xED, 0x5F, 0x52, 0xF7, 0x3C,
+ 0x65, 0x5D, 0xE5, 0x23, 0x0C, 0x6D, 0x84, 0x6E,
+ 0xDA, 0x77, 0xF8, 0x15, 0xFA, 0x69, 0xD0, 0xA7,
+ 0x11, 0xAC, 0xA6, 0xA3, 0x1F, 0x2E, 0xBF, 0x4A,
+ 0x8F, 0xFC, 0xEE, 0xC9, 0x26, 0x12, 0xC0, 0xB1,
+ 0x45, 0x0E, 0x3D, 0x7C, 0xCE, 0x13, 0x8E, 0x98,
+ 0x46, 0x2B, 0xC5, 0x66, 0x28, 0x32, 0xD2, 0x03,
+ 0xE3, 0xC4, 0x9B, 0x89, 0x5E, 0xF0, 0xCF, 0x3B,
+ 0x2D, 0x50, 0xB5, 0x00, 0x0A, 0xD6, 0x55, 0xE1,
+ 0x62, 0x63, 0x64, 0x87, 0xAF, 0x78, 0xB9, 0xF2,
+ 0x25, 0x44, 0xFF, 0x39, 0xF1, 0x08, 0x4F, 0x74,
+ 0xA9, 0x8B, 0x75, 0x01, 0xA0, 0xE4, 0x35, 0x8D,
+ 0xA1, 0xCC, 0xDF, 0x60, 0xD8, 0x5A, 0xE6, 0xD4 };
+
+unsigned char table_113[256] = {
+ 0x46, 0x9D, 0x39, 0xB2, 0x8D, 0x3B, 0x59, 0x5A,
+ 0xD0, 0x9C, 0xE4, 0x04, 0x01, 0xE2, 0xB3, 0xD2,
+ 0xD7, 0x18, 0x40, 0xD8, 0xF1, 0xEF, 0x3A, 0x1D,
+ 0x8E, 0xE5, 0xD9, 0xD3, 0xCB, 0x49, 0x4C, 0xCF,
+ 0xC0, 0xD6, 0xB5, 0x73, 0x77, 0x82, 0x54, 0xA2,
+ 0xB1, 0xB0, 0x84, 0x5D, 0xC7, 0xDE, 0x31, 0x2F,
+ 0x50, 0x78, 0xBE, 0x94, 0x64, 0x44, 0x60, 0x7A,
+ 0x1A, 0x6E, 0x09, 0x6F, 0xBF, 0x76, 0x81, 0x38,
+ 0x22, 0xC3, 0xEE, 0x8F, 0xFB, 0x32, 0xED, 0x92,
+ 0xAE, 0xE6, 0x5F, 0xAA, 0xAC, 0x0D, 0xA3, 0x47,
+ 0x1F, 0x11, 0xC1, 0x29, 0xAF, 0xFD, 0x1C, 0xDB,
+ 0x00, 0x23, 0xB9, 0xB8, 0x91, 0x41, 0x27, 0x37,
+ 0x43, 0x02, 0x26, 0xF6, 0x7D, 0x0A, 0x85, 0x93,
+ 0x97, 0x2E, 0x20, 0x55, 0x13, 0x4B, 0x6C, 0xE7,
+ 0xFC, 0x25, 0xFA, 0x9E, 0x5B, 0xA1, 0xDF, 0x2C,
+ 0x3E, 0xBC, 0xEA, 0x42, 0x7C, 0x36, 0x30, 0xEB,
+ 0xBD, 0x8B, 0x87, 0x16, 0x3D, 0x5C, 0x07, 0xBA,
+ 0xB4, 0x1B, 0xC2, 0xE3, 0x71, 0x9A, 0x5E, 0x4D,
+ 0xF2, 0xCC, 0x0E, 0xE1, 0x34, 0x75, 0x58, 0x89,
+ 0x17, 0xD4, 0x68, 0x80, 0x2B, 0x74, 0x70, 0x8A,
+ 0x63, 0xE8, 0x56, 0x24, 0xD1, 0x57, 0x35, 0x6D,
+ 0x3C, 0xA6, 0xC8, 0x7E, 0xA8, 0x4E, 0xC4, 0x33,
+ 0xA9, 0x62, 0x61, 0x7F, 0x21, 0x98, 0x2A, 0xAD,
+ 0xB6, 0xA7, 0xF5, 0x3F, 0x15, 0x45, 0xF8, 0xA4,
+ 0x95, 0x88, 0xDC, 0x96, 0x90, 0x08, 0x9B, 0xF9,
+ 0x06, 0x14, 0x05, 0xF0, 0xF7, 0xA0, 0xE0, 0x65,
+ 0xCA, 0xA5, 0x9F, 0x79, 0xCD, 0x4F, 0x72, 0xB7,
+ 0x4A, 0x0F, 0x66, 0xC5, 0x0C, 0x52, 0xF3, 0x69,
+ 0x83, 0x03, 0x99, 0x1E, 0x2D, 0xDA, 0x8C, 0x53,
+ 0x28, 0xDD, 0xE9, 0x0B, 0xC9, 0xF4, 0x48, 0x12,
+ 0x6A, 0x19, 0xCE, 0xAB, 0x51, 0xD5, 0x6B, 0xBB,
+ 0xFE, 0x7B, 0x67, 0xFF, 0x10, 0xEC, 0xC6, 0x86 };
+
+unsigned char table_114[32] = {
+ 0x11, 0x10, 0x04, 0x1D, 0x08, 0x15, 0x1A, 0x1B,
+ 0x14, 0x18, 0x0F, 0x17, 0x16, 0x07, 0x1E, 0x0E,
+ 0x12, 0x0A, 0x13, 0x0B, 0x0C, 0x00, 0x06, 0x02,
+ 0x1F, 0x19, 0x09, 0x1C, 0x01, 0x0D, 0x03, 0x05 };
+
+unsigned char table_115[256] = {
+ 0xB7, 0xBB, 0x63, 0x0D, 0xF0, 0x33, 0x5A, 0x05,
+ 0xF2, 0x7F, 0x64, 0xDB, 0x51, 0xC9, 0x2C, 0x85,
+ 0x4F, 0x41, 0xA4, 0x42, 0xCF, 0xA6, 0x52, 0x2F,
+ 0x26, 0xEF, 0xFB, 0x29, 0x40, 0x16, 0xF7, 0xED,
+ 0x23, 0x69, 0x8A, 0xDF, 0x77, 0x28, 0x93, 0x14,
+ 0x82, 0x0C, 0xBE, 0x3D, 0x20, 0xB4, 0x79, 0x94,
+ 0x54, 0xF8, 0x07, 0xB1, 0xE1, 0x66, 0x73, 0xD3,
+ 0x19, 0x15, 0xFF, 0x03, 0x6A, 0x9A, 0xDC, 0x1C,
+ 0xB3, 0x5D, 0x76, 0x68, 0x47, 0x6C, 0xF9, 0xFD,
+ 0xE9, 0xDD, 0x01, 0x65, 0xBD, 0x80, 0x0E, 0x7A,
+ 0x8D, 0x99, 0x13, 0x7C, 0xA5, 0xA7, 0x1A, 0xCC,
+ 0xB8, 0xE6, 0x2B, 0xB2, 0xB6, 0xD0, 0x62, 0x2D,
+ 0x4D, 0xD2, 0xB9, 0x04, 0x46, 0xAE, 0xAA, 0x44,
+ 0xDA, 0x92, 0x4B, 0x4E, 0xC4, 0xE2, 0xFE, 0xA2,
+ 0x75, 0x7B, 0xC3, 0xFA, 0x9F, 0x37, 0x9D, 0x1E,
+ 0x72, 0xD4, 0x1F, 0x4A, 0x9B, 0xE5, 0x6D, 0xEC,
+ 0x5C, 0x7D, 0x98, 0xE8, 0xEE, 0x86, 0xD1, 0xC8,
+ 0xEA, 0x55, 0xBF, 0xAF, 0xDE, 0x32, 0x09, 0x3A,
+ 0x8F, 0x57, 0x83, 0x43, 0x61, 0xC6, 0x8E, 0x96,
+ 0x22, 0xA3, 0x97, 0x91, 0x5F, 0x11, 0x3B, 0x5B,
+ 0x1B, 0x34, 0x49, 0x95, 0xF1, 0x6F, 0x89, 0xA8,
+ 0xC0, 0x36, 0x0A, 0x3F, 0x60, 0x50, 0xE7, 0x08,
+ 0xCE, 0x25, 0xC1, 0x71, 0xF6, 0x59, 0x58, 0x56,
+ 0x4C, 0xAB, 0x27, 0xAC, 0x06, 0xCB, 0x00, 0x30,
+ 0x84, 0x3E, 0xC2, 0x1D, 0x02, 0xE0, 0xC5, 0xD6,
+ 0x18, 0x70, 0xA9, 0x88, 0xD9, 0x39, 0x8B, 0x6E,
+ 0xF4, 0x24, 0xA0, 0x48, 0x45, 0x21, 0x87, 0x78,
+ 0x38, 0x90, 0xE3, 0xCA, 0xF5, 0xD7, 0x2A, 0x53,
+ 0x9C, 0xCD, 0x31, 0x35, 0xAD, 0x74, 0xD8, 0x12,
+ 0xBC, 0x9E, 0x6B, 0x67, 0xB0, 0xBA, 0xE4, 0x10,
+ 0x5E, 0xFC, 0xC7, 0x0F, 0x2E, 0x81, 0x7E, 0xA1,
+ 0x8C, 0x17, 0xB5, 0xEB, 0xD5, 0xF3, 0x0B, 0x3C };
+
+unsigned char table_116[32] = {
+ 0x00, 0x05, 0x10, 0x1C, 0x0C, 0x1A, 0x04, 0x1B,
+ 0x0A, 0x0D, 0x14, 0x0B, 0x07, 0x03, 0x12, 0x1E,
+ 0x06, 0x11, 0x01, 0x08, 0x15, 0x09, 0x1F, 0x0F,
+ 0x19, 0x18, 0x16, 0x02, 0x13, 0x0E, 0x17, 0x1D };
+
+unsigned char table_117[256] = {
+ 0xD0, 0x9A, 0xAB, 0xA8, 0xA7, 0xDF, 0x28, 0xCE,
+ 0x3E, 0x51, 0xBF, 0x76, 0x03, 0xA0, 0x53, 0x3F,
+ 0x90, 0x93, 0x87, 0x67, 0x98, 0x3D, 0xEA, 0x8B,
+ 0x55, 0xCF, 0x10, 0xF3, 0x25, 0xFC, 0x9F, 0x41,
+ 0x6B, 0x54, 0x6E, 0x0B, 0x83, 0x35, 0x69, 0x7D,
+ 0xE0, 0x88, 0x4B, 0xE9, 0x1E, 0x96, 0x91, 0x57,
+ 0xBD, 0x72, 0x21, 0x3C, 0xA6, 0x99, 0x6C, 0xF6,
+ 0x13, 0xFA, 0x29, 0xED, 0xDB, 0x16, 0x4D, 0x07,
+ 0x45, 0xA5, 0xE3, 0x0E, 0x31, 0xBC, 0x56, 0x5C,
+ 0xB2, 0x23, 0xDA, 0x74, 0xFF, 0x02, 0x8F, 0xF4,
+ 0x2A, 0xC9, 0x89, 0xAA, 0x05, 0xB1, 0xD1, 0x1F,
+ 0x4F, 0xB0, 0x7A, 0x2C, 0x14, 0xD9, 0xE7, 0x66,
+ 0x62, 0x1A, 0x4C, 0xC0, 0xC6, 0x63, 0x7F, 0xB4,
+ 0xF1, 0x43, 0xFE, 0x61, 0xA3, 0xCC, 0xE8, 0x6D,
+ 0xBA, 0x65, 0x42, 0x2B, 0xCA, 0xD5, 0x52, 0x3A,
+ 0xCD, 0x1D, 0x24, 0xD7, 0x47, 0xDE, 0x9E, 0x95,
+ 0x85, 0x48, 0x86, 0xE1, 0xC5, 0xD2, 0x34, 0xAF,
+ 0x40, 0xFB, 0xE6, 0x4E, 0xC8, 0xF5, 0x7B, 0x5A,
+ 0xCB, 0xD4, 0x97, 0x6F, 0x0C, 0x79, 0x9C, 0x20,
+ 0x59, 0x19, 0x68, 0x2E, 0x09, 0x64, 0x73, 0x50,
+ 0xC2, 0x2F, 0x0D, 0xEF, 0x9D, 0x94, 0x00, 0x81,
+ 0xE2, 0x46, 0x5F, 0xB8, 0x0A, 0x12, 0x75, 0x1C,
+ 0x8C, 0xB6, 0x71, 0xAC, 0x04, 0x60, 0xA9, 0x5B,
+ 0xF8, 0x30, 0x49, 0x44, 0x4A, 0xBE, 0x6A, 0xEB,
+ 0xD3, 0xD8, 0x36, 0xB3, 0x3B, 0x17, 0x80, 0xA4,
+ 0xEC, 0x26, 0x82, 0xB5, 0x37, 0x5D, 0x1B, 0x2D,
+ 0xE5, 0xA2, 0x0F, 0xB7, 0xC4, 0xF2, 0x70, 0x39,
+ 0xF9, 0xC7, 0xBB, 0x8A, 0x32, 0x78, 0xC3, 0x5E,
+ 0xD6, 0xE4, 0x22, 0x9B, 0x18, 0x8E, 0xEE, 0x27,
+ 0x8D, 0x33, 0x11, 0x77, 0x01, 0x06, 0x38, 0xF0,
+ 0x7E, 0x08, 0x15, 0xB9, 0x7C, 0xAD, 0x84, 0xDD,
+ 0xC1, 0xFD, 0x92, 0xA1, 0xF7, 0xAE, 0xDC, 0x58 };
+
+unsigned char table_118[256] = {
+ 0x38, 0xA0, 0xA6, 0xFC, 0x7C, 0x5A, 0x97, 0x1D,
+ 0xFD, 0x00, 0x20, 0xA2, 0x72, 0x10, 0x1F, 0x48,
+ 0x98, 0x7E, 0xDF, 0x2D, 0x80, 0x0A, 0x27, 0xDC,
+ 0xCF, 0xBF, 0x92, 0x94, 0x53, 0xCC, 0x0E, 0x74,
+ 0xA7, 0x60, 0x08, 0x15, 0x87, 0x6F, 0xB3, 0xA3,
+ 0xED, 0x59, 0x09, 0x4F, 0x9E, 0x9A, 0xEE, 0x83,
+ 0x56, 0x32, 0x34, 0xC7, 0x24, 0xE7, 0x96, 0x4D,
+ 0xAE, 0xE3, 0xBD, 0xE2, 0x36, 0x4A, 0xB6, 0x8B,
+ 0xF2, 0xC1, 0xD7, 0x40, 0x31, 0x4B, 0xDA, 0xF1,
+ 0xB1, 0x70, 0xA8, 0xC3, 0xC6, 0x8A, 0xE6, 0x77,
+ 0x21, 0x7D, 0xD5, 0x0C, 0x43, 0xC4, 0xF0, 0x1B,
+ 0x18, 0xA1, 0x85, 0xE1, 0xFF, 0x8D, 0xE5, 0x6E,
+ 0x9B, 0x51, 0x1C, 0xA4, 0x5C, 0x8E, 0x69, 0x49,
+ 0x23, 0xCD, 0x52, 0xF8, 0x3E, 0x91, 0x5E, 0x1E,
+ 0x25, 0xB4, 0x93, 0xCB, 0xE0, 0x47, 0xBC, 0x4E,
+ 0x33, 0xB7, 0x75, 0x1A, 0x11, 0x9C, 0x3F, 0xEC,
+ 0xD1, 0x46, 0xDD, 0xAA, 0xB8, 0x99, 0x86, 0x67,
+ 0x58, 0xF9, 0x16, 0x17, 0x6D, 0x5F, 0x2B, 0xA5,
+ 0xD3, 0x8F, 0x55, 0x71, 0xD2, 0xBA, 0x5B, 0x3C,
+ 0x82, 0xB5, 0x41, 0xE4, 0x90, 0x45, 0x6C, 0xF6,
+ 0xDE, 0xA9, 0x84, 0x62, 0x19, 0x3B, 0xB9, 0xC8,
+ 0x2C, 0xB0, 0x76, 0x57, 0xD8, 0x26, 0x9D, 0x89,
+ 0xC9, 0x54, 0xFB, 0x07, 0xCE, 0x22, 0x5D, 0x64,
+ 0x65, 0xAD, 0x01, 0xDB, 0x14, 0x4C, 0x37, 0x03,
+ 0x6B, 0xAF, 0xD0, 0x7F, 0x9F, 0xBB, 0xEB, 0xC0,
+ 0x50, 0x66, 0x68, 0x0B, 0x42, 0x2A, 0xD4, 0xF5,
+ 0x61, 0x63, 0xF3, 0x39, 0xBE, 0xC5, 0xEF, 0x28,
+ 0x3A, 0xAB, 0x79, 0x05, 0xE9, 0x12, 0x73, 0x3D,
+ 0xB2, 0x8C, 0xCA, 0x29, 0x0F, 0xF4, 0x7B, 0x13,
+ 0x88, 0x44, 0xC2, 0x2E, 0xFA, 0xFE, 0x04, 0x35,
+ 0xE8, 0x06, 0x7A, 0x78, 0x0D, 0x81, 0xF7, 0xEA,
+ 0xD9, 0x2F, 0x02, 0xAC, 0x30, 0x6A, 0xD6, 0x95 };
+
+unsigned char table_119[32] = {
+ 0x14, 0x0A, 0x1C, 0x00, 0x0C, 0x1F, 0x1E, 0x0B,
+ 0x12, 0x1D, 0x17, 0x08, 0x07, 0x04, 0x09, 0x10,
+ 0x03, 0x1B, 0x0E, 0x1A, 0x05, 0x0D, 0x11, 0x15,
+ 0x18, 0x02, 0x06, 0x01, 0x19, 0x16, 0x13, 0x0F };
+
+unsigned char table_120[256] = {
+ 0xCE, 0x89, 0xB2, 0x72, 0x04, 0x77, 0x64, 0xAE,
+ 0x80, 0x99, 0xB5, 0x00, 0x7B, 0x50, 0x9D, 0xE3,
+ 0x87, 0x37, 0x6D, 0x3D, 0x32, 0xBA, 0x20, 0xF0,
+ 0xDC, 0xBD, 0x61, 0x26, 0xD4, 0xA6, 0x70, 0x54,
+ 0xC1, 0x7D, 0x82, 0xFF, 0x81, 0x83, 0x2F, 0xF5,
+ 0x3B, 0x42, 0x08, 0x5C, 0x30, 0x59, 0xBB, 0xC2,
+ 0x33, 0x5D, 0xEE, 0xB7, 0xF7, 0x2B, 0x76, 0xD0,
+ 0x43, 0x1C, 0x48, 0xFC, 0x01, 0xCD, 0x27, 0x1D,
+ 0x5A, 0x96, 0x95, 0x03, 0xC6, 0x1F, 0x09, 0xCB,
+ 0xF6, 0x47, 0xA9, 0x93, 0xA7, 0xD2, 0xDB, 0x51,
+ 0xB0, 0x7A, 0xE6, 0x62, 0x0F, 0x12, 0x57, 0xF4,
+ 0x35, 0xFE, 0xA4, 0xDF, 0x5B, 0xF3, 0x67, 0x85,
+ 0x98, 0xE4, 0xAB, 0x75, 0x4C, 0xE2, 0x25, 0x74,
+ 0x3A, 0x45, 0xDE, 0xEF, 0x4A, 0x97, 0x86, 0x24,
+ 0xE9, 0x8F, 0xD8, 0xD7, 0x60, 0xAD, 0x36, 0x8E,
+ 0x1E, 0xB9, 0x4F, 0x6B, 0x8C, 0x06, 0x23, 0x94,
+ 0x0E, 0xD3, 0x49, 0x14, 0x90, 0xAF, 0x65, 0xEC,
+ 0xF9, 0x0D, 0xED, 0x6C, 0xBE, 0x7F, 0xA5, 0xC5,
+ 0xEA, 0x78, 0x2E, 0xBC, 0xD5, 0xDA, 0x18, 0xE1,
+ 0x10, 0x2D, 0xB4, 0x16, 0x4B, 0xE8, 0xC4, 0x8D,
+ 0x19, 0x1B, 0x02, 0x66, 0xB6, 0xE7, 0x9C, 0x7C,
+ 0xC9, 0xA0, 0x2A, 0x53, 0x13, 0xDD, 0xF8, 0xA8,
+ 0x0A, 0x6E, 0xCF, 0x6F, 0x7E, 0xE0, 0x3E, 0xE5,
+ 0x07, 0xCC, 0x38, 0xD1, 0xF2, 0x2C, 0x9A, 0xAC,
+ 0x88, 0x79, 0xB8, 0xC8, 0xBF, 0x63, 0x71, 0x69,
+ 0x52, 0x39, 0x9F, 0x22, 0x3F, 0x9E, 0x44, 0xFA,
+ 0x73, 0x6A, 0x8B, 0xA2, 0xD6, 0x1A, 0x9B, 0xB1,
+ 0x8A, 0x4D, 0x58, 0xA1, 0x46, 0x5F, 0x55, 0x56,
+ 0x21, 0x05, 0x15, 0x92, 0xAA, 0xEB, 0x31, 0x68,
+ 0xFB, 0x41, 0xC3, 0x4E, 0xB3, 0x40, 0x34, 0x17,
+ 0xD9, 0x29, 0x3C, 0x0C, 0xF1, 0x0B, 0x28, 0x84,
+ 0x5E, 0xCA, 0xFD, 0x11, 0xA3, 0xC7, 0xC0, 0x91 };
+
+unsigned char table_121[32] = {
+ 0x1E, 0x12, 0x06, 0x1D, 0x15, 0x1F, 0x13, 0x0B,
+ 0x10, 0x0D, 0x1C, 0x01, 0x0A, 0x0E, 0x02, 0x19,
+ 0x04, 0x1A, 0x03, 0x11, 0x00, 0x16, 0x0C, 0x17,
+ 0x14, 0x08, 0x18, 0x05, 0x09, 0x0F, 0x1B, 0x07 };
+
+unsigned char table_122[256] = {
+ 0x85, 0xDF, 0x7F, 0x7C, 0x56, 0xF0, 0x0C, 0x7D,
+ 0x76, 0xA8, 0x58, 0x31, 0x25, 0x8A, 0x0D, 0x23,
+ 0x05, 0x0F, 0x12, 0x64, 0x8E, 0x5D, 0xF4, 0x2C,
+ 0x18, 0xFA, 0x4B, 0xFE, 0x91, 0xBF, 0x95, 0x0B,
+ 0xF1, 0x88, 0x10, 0xD8, 0x3E, 0x53, 0x96, 0xB5,
+ 0x75, 0x24, 0x8F, 0xD6, 0x68, 0x5C, 0x93, 0x1F,
+ 0x6B, 0xC2, 0xAB, 0xED, 0x1E, 0xC0, 0xBC, 0x47,
+ 0xE9, 0xD1, 0xDE, 0xCA, 0xF6, 0x62, 0x43, 0xEB,
+ 0xA2, 0xB4, 0x08, 0xE6, 0x74, 0x0E, 0xA1, 0x72,
+ 0x66, 0x61, 0x21, 0x2E, 0x32, 0x63, 0x29, 0xD7,
+ 0x1C, 0x22, 0xAC, 0xE7, 0x54, 0xF3, 0x65, 0x17,
+ 0x9F, 0x78, 0x79, 0x4C, 0xDD, 0x27, 0x90, 0x36,
+ 0x19, 0x44, 0x03, 0xD9, 0x4A, 0x5A, 0x34, 0xF9,
+ 0x97, 0xA6, 0x70, 0x39, 0x28, 0x77, 0x6E, 0xB7,
+ 0x8C, 0x02, 0x5E, 0x9B, 0x8D, 0x59, 0x6F, 0xA5,
+ 0x07, 0xE2, 0x41, 0x51, 0xC9, 0x3C, 0xE8, 0xE1,
+ 0xB3, 0x16, 0x50, 0x04, 0xE3, 0x1D, 0x3B, 0xD2,
+ 0x4D, 0x35, 0x71, 0xDA, 0x9E, 0xA7, 0xE4, 0xE0,
+ 0xB6, 0x2B, 0xEA, 0x84, 0x55, 0xF8, 0x57, 0x3D,
+ 0x73, 0x42, 0xC6, 0x0A, 0x92, 0x6A, 0xAE, 0xF5,
+ 0xFC, 0xD5, 0x15, 0x52, 0x7E, 0x14, 0x81, 0x13,
+ 0xE5, 0x49, 0x38, 0x2A, 0x94, 0x5B, 0xA3, 0x11,
+ 0x8B, 0x80, 0xBB, 0x01, 0x9C, 0xA4, 0xDB, 0xF7,
+ 0xA9, 0x20, 0xF2, 0x1A, 0xDC, 0x33, 0x3A, 0xEF,
+ 0xD3, 0xFD, 0x30, 0xB0, 0x1B, 0xC4, 0x06, 0xD4,
+ 0x6D, 0x87, 0x2F, 0x60, 0x5F, 0xC5, 0x09, 0x37,
+ 0xAF, 0x00, 0xCB, 0x9D, 0xA0, 0xB9, 0x45, 0x86,
+ 0x4F, 0x6C, 0x67, 0xFB, 0x40, 0x3F, 0xCC, 0xB8,
+ 0xC8, 0x82, 0x98, 0x99, 0x7B, 0xB1, 0xCD, 0xD0,
+ 0xBD, 0x48, 0xAD, 0x26, 0x7A, 0x9A, 0x46, 0xFF,
+ 0x89, 0xC7, 0xC1, 0xCF, 0xBE, 0xAA, 0xEC, 0xBA,
+ 0xCE, 0x2D, 0x4E, 0x83, 0xC3, 0x69, 0xEE, 0xB2 };
+
+unsigned char table_123[256] = {
+ 0x9D, 0xFB, 0x3C, 0x81, 0xAA, 0x05, 0xB2, 0xBE,
+ 0xD1, 0x5F, 0x4C, 0xE0, 0xA3, 0xF4, 0xDE, 0x35,
+ 0xFE, 0x1B, 0x37, 0x99, 0x94, 0x7A, 0x10, 0xAB,
+ 0xC0, 0xA4, 0xB5, 0xFF, 0x8F, 0x3B, 0xB4, 0x51,
+ 0x04, 0xE9, 0xB9, 0xC1, 0x98, 0xC5, 0x82, 0x38,
+ 0x4D, 0x71, 0xFC, 0x33, 0xC4, 0x50, 0x5D, 0x88,
+ 0xB8, 0x5C, 0x32, 0xE2, 0xBB, 0xCD, 0x60, 0x2C,
+ 0xD4, 0x7E, 0x27, 0x59, 0x2B, 0x1F, 0x53, 0xF6,
+ 0x25, 0x86, 0xAE, 0x21, 0xFA, 0x31, 0xD7, 0x0F,
+ 0x17, 0xDA, 0x7F, 0xC9, 0x46, 0x19, 0x08, 0xA8,
+ 0xCF, 0x13, 0xCC, 0x03, 0x3F, 0x22, 0x6E, 0xEB,
+ 0x4A, 0x63, 0x73, 0xBD, 0x36, 0xED, 0x30, 0x57,
+ 0x65, 0xF8, 0x41, 0x61, 0x1E, 0xA0, 0xC6, 0x45,
+ 0x3E, 0x75, 0x28, 0x87, 0xCB, 0xD6, 0x16, 0xD8,
+ 0xDF, 0xEF, 0xEA, 0xA7, 0x58, 0xB0, 0x1D, 0xE6,
+ 0x47, 0x76, 0xD9, 0x96, 0xE7, 0xDC, 0x00, 0x80,
+ 0xDD, 0xB7, 0x9A, 0xE1, 0xF5, 0x9C, 0x4B, 0xE3,
+ 0xBC, 0x8D, 0xF2, 0x2F, 0x9F, 0x6C, 0x93, 0xAF,
+ 0xA9, 0xC2, 0x5E, 0x24, 0x15, 0xD2, 0x09, 0x0D,
+ 0xDB, 0x4F, 0x91, 0x0E, 0x64, 0x34, 0x4E, 0xAD,
+ 0x62, 0x44, 0x23, 0x85, 0xB6, 0xAC, 0xC7, 0xCA,
+ 0x84, 0xF9, 0x8C, 0xBF, 0x14, 0x7C, 0x8E, 0x92,
+ 0xF0, 0x0B, 0xCE, 0x90, 0x7D, 0x70, 0x9E, 0x54,
+ 0x39, 0x5B, 0x6D, 0x52, 0xEE, 0xA2, 0x6F, 0x78,
+ 0x2D, 0x95, 0x8B, 0x02, 0x3D, 0x7B, 0x69, 0xC3,
+ 0x49, 0xA5, 0x1A, 0x26, 0xD5, 0x6B, 0xE8, 0xFD,
+ 0xB3, 0xD3, 0x20, 0x55, 0x18, 0x06, 0xF3, 0xB1,
+ 0x0C, 0xC8, 0x07, 0x12, 0xF7, 0x01, 0x2E, 0x72,
+ 0x97, 0xA6, 0x11, 0x89, 0x56, 0x5A, 0x29, 0xBA,
+ 0x67, 0x42, 0x83, 0x6A, 0x2A, 0xF1, 0xA1, 0x9B,
+ 0xE5, 0xE4, 0x74, 0x66, 0x1C, 0x68, 0xEC, 0x40,
+ 0x48, 0x77, 0xD0, 0x0A, 0x8A, 0x3A, 0x43, 0x79 };
+
+unsigned char table_124[256] = {
+ 0x6C, 0xC3, 0x28, 0x2F, 0x42, 0x4B, 0x7C, 0x3C,
+ 0xCE, 0x24, 0xC8, 0x51, 0x25, 0x3F, 0x49, 0x8D,
+ 0x1E, 0x5C, 0x89, 0x3A, 0x98, 0x47, 0x0B, 0x12,
+ 0xA9, 0xB1, 0xD7, 0xB6, 0x5D, 0xF9, 0x5A, 0xBC,
+ 0xFA, 0x06, 0x7D, 0x08, 0xFC, 0x37, 0x54, 0x4F,
+ 0xD4, 0xCD, 0xA7, 0x5E, 0xE0, 0x92, 0x82, 0x56,
+ 0xF1, 0x2B, 0xC4, 0xE2, 0x29, 0xEA, 0x35, 0x57,
+ 0x33, 0x4E, 0x1A, 0x17, 0x8B, 0x85, 0xBF, 0xD5,
+ 0x18, 0xB3, 0x0D, 0x71, 0x45, 0x81, 0xB4, 0x27,
+ 0xD1, 0xE1, 0xFF, 0x44, 0x9E, 0xA4, 0x15, 0x9A,
+ 0x90, 0xC7, 0x79, 0xE3, 0x4C, 0xE9, 0x3D, 0x6B,
+ 0xF5, 0xF4, 0xEE, 0xAA, 0xDB, 0x07, 0x09, 0xCF,
+ 0x7B, 0x95, 0xA0, 0x53, 0x8F, 0xA1, 0x9D, 0xBE,
+ 0x6F, 0xAE, 0x96, 0x46, 0x59, 0x01, 0x84, 0xCC,
+ 0x3B, 0x8E, 0xF7, 0x4D, 0x6E, 0xDC, 0xE8, 0x36,
+ 0x7A, 0xE5, 0xBD, 0xE7, 0x9F, 0x2C, 0x52, 0xAB,
+ 0x55, 0x13, 0x1D, 0xFB, 0x58, 0x9C, 0xDF, 0xC0,
+ 0x30, 0x73, 0x67, 0x39, 0x74, 0xD3, 0x11, 0xD2,
+ 0x0E, 0x20, 0xB7, 0x02, 0xB9, 0x1C, 0x86, 0x76,
+ 0x10, 0x68, 0x9B, 0x63, 0x48, 0x8A, 0xB2, 0xB8,
+ 0xAF, 0x26, 0x99, 0x04, 0xB0, 0xE4, 0xEF, 0xEB,
+ 0xEC, 0x6D, 0x61, 0xC1, 0xD0, 0x38, 0xC9, 0x19,
+ 0x60, 0xA8, 0xA6, 0xF8, 0x80, 0xC5, 0x03, 0x0F,
+ 0x22, 0x2D, 0x88, 0x32, 0x77, 0x70, 0xFE, 0x0C,
+ 0x31, 0x40, 0x5F, 0xED, 0xA5, 0x93, 0x43, 0xF0,
+ 0x8C, 0xE6, 0x34, 0x21, 0xD9, 0xC2, 0xD8, 0xC6,
+ 0x6A, 0xD6, 0xCB, 0xAC, 0x75, 0xB5, 0x78, 0x0A,
+ 0xA3, 0x69, 0x16, 0xBA, 0x50, 0x2A, 0x41, 0x83,
+ 0xF6, 0x64, 0x00, 0x65, 0x7E, 0xDD, 0x5B, 0xDA,
+ 0x14, 0xFD, 0x3E, 0x7F, 0xCA, 0x66, 0x4A, 0x1F,
+ 0xA2, 0xAD, 0xF2, 0x23, 0xBB, 0x72, 0xF3, 0x94,
+ 0x62, 0x1B, 0xDE, 0x91, 0x87, 0x97, 0x05, 0x2E };
+
+unsigned char table_125[32] = {
+ 0x1A, 0x18, 0x12, 0x15, 0x00, 0x1C, 0x01, 0x0B,
+ 0x19, 0x1B, 0x1F, 0x11, 0x07, 0x10, 0x1E, 0x06,
+ 0x17, 0x04, 0x0A, 0x0E, 0x0D, 0x0C, 0x16, 0x08,
+ 0x02, 0x03, 0x13, 0x14, 0x09, 0x1D, 0x05, 0x0F };
+
+unsigned char table_126[32] = {
+ 0x1C, 0x1D, 0x07, 0x12, 0x18, 0x1A, 0x19, 0x09,
+ 0x0F, 0x14, 0x1F, 0x0B, 0x13, 0x04, 0x0E, 0x1E,
+ 0x0C, 0x0D, 0x01, 0x17, 0x1B, 0x16, 0x0A, 0x05,
+ 0x15, 0x10, 0x11, 0x08, 0x00, 0x03, 0x06, 0x02 };
+
+unsigned char table_127[256] = {
+ 0xA0, 0x66, 0xD8, 0x08, 0xEA, 0x39, 0x78, 0xAB,
+ 0x61, 0x4E, 0xC7, 0xD1, 0xA3, 0x1C, 0x9F, 0xCB,
+ 0x19, 0x51, 0x15, 0x92, 0x23, 0xFD, 0x7D, 0x1D,
+ 0x95, 0xAE, 0x0E, 0x8B, 0xE6, 0x7F, 0x86, 0x6D,
+ 0x06, 0xBD, 0x20, 0x1F, 0x3A, 0xE4, 0x54, 0x91,
+ 0x69, 0xD3, 0xE3, 0x3D, 0x4D, 0x31, 0x49, 0xA4,
+ 0x41, 0xF3, 0xE0, 0x11, 0x14, 0x9B, 0x96, 0x5A,
+ 0xC4, 0x8E, 0x34, 0xDB, 0xBA, 0x83, 0xD9, 0x81,
+ 0xAF, 0x58, 0x8A, 0x79, 0x13, 0xBC, 0x85, 0x37,
+ 0x9E, 0x6C, 0x57, 0x71, 0x8D, 0x97, 0x5F, 0x6F,
+ 0x1E, 0x74, 0x27, 0xFC, 0x5C, 0x7A, 0x64, 0x87,
+ 0xF5, 0xC6, 0xF2, 0x4F, 0xDE, 0x80, 0xAA, 0x84,
+ 0x2E, 0xDC, 0xE7, 0x40, 0x75, 0xC5, 0xB3, 0xC8,
+ 0xCE, 0x21, 0x02, 0x67, 0xB7, 0x10, 0x47, 0x6A,
+ 0xEE, 0x53, 0x2C, 0x16, 0x05, 0xC0, 0x63, 0x4C,
+ 0x0D, 0xBB, 0xC3, 0x38, 0x46, 0x68, 0x7E, 0xF9,
+ 0xB8, 0xB4, 0x3E, 0x36, 0xD5, 0xEC, 0x0B, 0xF6,
+ 0x33, 0x0A, 0x0F, 0x5B, 0xFB, 0x45, 0xEB, 0xA9,
+ 0x6E, 0x6B, 0xCF, 0x55, 0x99, 0xAC, 0x22, 0xBE,
+ 0xB1, 0xA2, 0x3F, 0x25, 0x77, 0x8F, 0x7C, 0xF1,
+ 0xD4, 0x59, 0xA8, 0xE5, 0xD7, 0xCA, 0xA1, 0x93,
+ 0xE9, 0xAD, 0xF7, 0x94, 0xEF, 0xED, 0x3C, 0x2A,
+ 0x88, 0xB5, 0x35, 0x9D, 0x9C, 0x32, 0x5E, 0xB6,
+ 0x48, 0x9A, 0x7B, 0x26, 0x50, 0x90, 0x04, 0xA7,
+ 0xDD, 0x09, 0xB9, 0x98, 0xB2, 0xFE, 0xDF, 0x44,
+ 0x89, 0x29, 0x5D, 0xE2, 0x72, 0xC9, 0x28, 0x03,
+ 0x43, 0x8C, 0x52, 0x18, 0xC1, 0x56, 0x1B, 0x1A,
+ 0x01, 0x65, 0xDA, 0xBF, 0x07, 0xFF, 0x76, 0xE8,
+ 0x30, 0xA5, 0x4A, 0xA6, 0x12, 0x62, 0x24, 0x60,
+ 0x4B, 0x73, 0x0C, 0xF0, 0xFA, 0x42, 0xF4, 0x00,
+ 0xD2, 0xD0, 0xD6, 0x3B, 0xC2, 0x2F, 0xE1, 0x2B,
+ 0x70, 0xF8, 0x17, 0xCD, 0xB0, 0xCC, 0x82, 0x2D };
+
+unsigned char table_128[32] = {
+ 0x1A, 0x1C, 0x09, 0x17, 0x1B, 0x0B, 0x16, 0x1E,
+ 0x14, 0x0C, 0x12, 0x0E, 0x05, 0x03, 0x1F, 0x15,
+ 0x19, 0x0D, 0x10, 0x13, 0x0A, 0x01, 0x00, 0x11,
+ 0x02, 0x08, 0x0F, 0x18, 0x07, 0x04, 0x1D, 0x06 };
+
+unsigned char table_129[256] = {
+ 0x9D, 0x5F, 0xE8, 0x99, 0x57, 0x07, 0x16, 0xA6,
+ 0x9F, 0xB6, 0xDE, 0xED, 0x2D, 0xB3, 0xC0, 0x8E,
+ 0xCC, 0x49, 0xCE, 0xB0, 0x1B, 0xB1, 0x7A, 0xE0,
+ 0xEB, 0x28, 0xDB, 0x7D, 0x88, 0xC8, 0x06, 0x6C,
+ 0x02, 0xD0, 0x85, 0x7E, 0xDF, 0xF5, 0x78, 0xE5,
+ 0xA9, 0x71, 0xD9, 0xDD, 0xDC, 0xEE, 0x8C, 0x54,
+ 0xA0, 0x86, 0xFE, 0x0E, 0x55, 0xF7, 0x41, 0x47,
+ 0x1D, 0x15, 0xD6, 0xA4, 0xFF, 0x1F, 0x25, 0xF8,
+ 0x12, 0xE9, 0x74, 0x7B, 0x04, 0xE6, 0x4C, 0x31,
+ 0xA2, 0xBE, 0x0C, 0xB9, 0x17, 0xBD, 0x3D, 0xF0,
+ 0x9E, 0x4D, 0x4E, 0xB2, 0xE7, 0x40, 0xC9, 0x8A,
+ 0x67, 0x5E, 0x19, 0x0F, 0xB7, 0x22, 0x8D, 0xBA,
+ 0xFC, 0x93, 0x14, 0xEA, 0xFD, 0x0D, 0xD5, 0x38,
+ 0xA1, 0x84, 0x1C, 0x35, 0x60, 0x37, 0x43, 0x9C,
+ 0xCF, 0xEF, 0x3A, 0x72, 0xF2, 0x61, 0x75, 0x6A,
+ 0x42, 0xAC, 0xD3, 0x48, 0x77, 0xC5, 0x29, 0xF6,
+ 0x58, 0x79, 0xFA, 0x5D, 0xC7, 0x70, 0x53, 0x9A,
+ 0x6F, 0xC1, 0x0A, 0x90, 0x8F, 0x3E, 0x3B, 0x8B,
+ 0xEC, 0xBC, 0x20, 0x27, 0xC3, 0x66, 0x3F, 0x33,
+ 0xA5, 0x44, 0x2E, 0x32, 0x65, 0x18, 0xFB, 0x59,
+ 0x52, 0x50, 0xE2, 0x63, 0x2B, 0xCD, 0x64, 0xCB,
+ 0xD2, 0x68, 0x10, 0xA7, 0xAE, 0x11, 0xA8, 0x96,
+ 0x69, 0xAF, 0xC2, 0x34, 0x5C, 0x56, 0xE3, 0xF9,
+ 0xDA, 0x51, 0x81, 0x4A, 0x05, 0x00, 0xB8, 0x7C,
+ 0x30, 0x2F, 0x46, 0xB4, 0xC6, 0x87, 0x4B, 0x94,
+ 0x80, 0xF4, 0x7F, 0x3C, 0x26, 0xF1, 0x5B, 0xAB,
+ 0x91, 0x6E, 0x08, 0x76, 0x98, 0xD1, 0xE1, 0x36,
+ 0x21, 0xCA, 0xD8, 0x24, 0x9B, 0x39, 0xBB, 0xAD,
+ 0x13, 0x62, 0x97, 0x1A, 0x6D, 0x2C, 0x5A, 0xC4,
+ 0xD4, 0xA3, 0x03, 0xBF, 0x1E, 0xE4, 0xF3, 0x95,
+ 0x23, 0x73, 0x92, 0xB5, 0x01, 0x83, 0x82, 0xAA,
+ 0x09, 0x45, 0x6B, 0xD7, 0x0B, 0x89, 0x4F, 0x2A };
+
+unsigned char table_130[32] = {
+ 0x07, 0x03, 0x15, 0x0B, 0x02, 0x11, 0x17, 0x14,
+ 0x05, 0x10, 0x0A, 0x0F, 0x01, 0x1C, 0x1D, 0x0E,
+ 0x12, 0x06, 0x18, 0x16, 0x1A, 0x09, 0x13, 0x19,
+ 0x1B, 0x00, 0x08, 0x0D, 0x0C, 0x1E, 0x04, 0x1F };
+
+unsigned char table_131[32] = {
+ 0x1D, 0x13, 0x1B, 0x10, 0x07, 0x03, 0x0A, 0x02,
+ 0x00, 0x0C, 0x0E, 0x0B, 0x0D, 0x18, 0x12, 0x1F,
+ 0x1A, 0x04, 0x15, 0x11, 0x1E, 0x08, 0x1C, 0x14,
+ 0x19, 0x05, 0x0F, 0x17, 0x06, 0x01, 0x09, 0x16 };
+
+unsigned char table_132[256] = {
+ 0x33, 0x8D, 0x45, 0x6F, 0xFF, 0xF5, 0xB6, 0x53,
+ 0x3B, 0xF3, 0x07, 0xA4, 0x97, 0xEB, 0x6B, 0xA5,
+ 0xD3, 0xDC, 0x7B, 0x79, 0x93, 0xE7, 0xF7, 0x67,
+ 0x9C, 0x4F, 0x88, 0xF9, 0x3A, 0x2B, 0x27, 0x48,
+ 0x47, 0x18, 0xF4, 0xAD, 0xB4, 0x8F, 0x2A, 0x76,
+ 0x17, 0xE9, 0x1F, 0x40, 0x0C, 0x59, 0xD1, 0x4C,
+ 0x20, 0x31, 0x73, 0x54, 0xCD, 0x68, 0x08, 0x52,
+ 0x10, 0x62, 0x3D, 0xD2, 0x77, 0xF2, 0xD7, 0x30,
+ 0xCA, 0x16, 0x01, 0x50, 0x9F, 0x3F, 0x75, 0xED,
+ 0x90, 0x6A, 0x34, 0xCE, 0x05, 0x78, 0x5E, 0xD6,
+ 0x85, 0xCC, 0x29, 0xB8, 0xC1, 0x0D, 0xCB, 0x80,
+ 0x2E, 0x04, 0x00, 0x44, 0x32, 0x95, 0xBF, 0xFE,
+ 0x6E, 0x7C, 0xFD, 0xA7, 0x3C, 0x5C, 0xF0, 0xEC,
+ 0xAC, 0xF8, 0xB9, 0xC0, 0x1B, 0x3E, 0xE8, 0x66,
+ 0x5D, 0xDE, 0x49, 0x71, 0xAA, 0xAF, 0x21, 0x64,
+ 0x28, 0x8A, 0x4E, 0x98, 0x58, 0xA2, 0x23, 0xCF,
+ 0x9E, 0x63, 0x61, 0x91, 0x12, 0xC6, 0x8C, 0x19,
+ 0xA8, 0xD4, 0xC7, 0xDD, 0xFC, 0xBD, 0x38, 0xDF,
+ 0xEA, 0x2D, 0x7E, 0x7D, 0xE3, 0xE0, 0xC3, 0xD9,
+ 0x8B, 0x11, 0xF1, 0x4D, 0xC8, 0xB5, 0x55, 0xAE,
+ 0xE1, 0x89, 0xE5, 0xB3, 0xBC, 0x69, 0x9D, 0xA6,
+ 0x09, 0x9A, 0x74, 0x35, 0x1A, 0xFB, 0x24, 0xB7,
+ 0x13, 0x14, 0x94, 0x0A, 0x86, 0x0F, 0x60, 0x51,
+ 0xB0, 0x84, 0x22, 0x5B, 0x87, 0x43, 0x57, 0x0B,
+ 0x2F, 0x5F, 0x02, 0xD0, 0xBB, 0xA3, 0xC9, 0x7A,
+ 0xBE, 0xC2, 0x26, 0x46, 0xDB, 0x1E, 0x1D, 0x92,
+ 0xE2, 0xB2, 0x37, 0x6D, 0xD5, 0x4A, 0x0E, 0x4B,
+ 0x8E, 0xC5, 0x42, 0x99, 0xEE, 0xE4, 0xB1, 0x06,
+ 0xAB, 0x5A, 0x56, 0x41, 0x65, 0xBA, 0xFA, 0x83,
+ 0x15, 0xDA, 0x72, 0xA1, 0x81, 0x1C, 0xA9, 0x36,
+ 0x25, 0x96, 0x6C, 0x39, 0x82, 0xE6, 0x2C, 0x9B,
+ 0xC4, 0x7F, 0xA0, 0xD8, 0xEF, 0x03, 0x70, 0xF6 };
+
+unsigned char table_133[256] = {
+ 0x02, 0xF0, 0xED, 0xC4, 0xE4, 0x67, 0x60, 0x8B,
+ 0xF3, 0x77, 0x92, 0xE0, 0x85, 0x93, 0x1E, 0x8E,
+ 0x9A, 0x38, 0x61, 0x20, 0xB7, 0x68, 0xE1, 0x5E,
+ 0xD5, 0x63, 0xA9, 0xA5, 0xBE, 0x36, 0x12, 0x4D,
+ 0x86, 0x16, 0xD6, 0xB1, 0x23, 0x64, 0x4F, 0x62,
+ 0xFC, 0xA3, 0xD3, 0x04, 0x7D, 0x8C, 0xE2, 0xFF,
+ 0x5D, 0x30, 0xF5, 0x95, 0x1B, 0x5F, 0x73, 0xAA,
+ 0xE8, 0x07, 0x87, 0xDC, 0x54, 0x7C, 0xEE, 0x00,
+ 0xB8, 0xDE, 0x55, 0xBA, 0xD0, 0x50, 0xBB, 0x89,
+ 0x1C, 0xCC, 0x0E, 0xC0, 0x42, 0x11, 0xD8, 0xA2,
+ 0x2E, 0x33, 0xFE, 0x26, 0xD4, 0x10, 0xDA, 0xC5,
+ 0xFB, 0xAF, 0x98, 0x78, 0xB5, 0xBD, 0xC8, 0x8D,
+ 0x46, 0xA0, 0xD1, 0x7B, 0xBC, 0x75, 0xAB, 0x25,
+ 0xB2, 0x43, 0x57, 0xB6, 0xEC, 0xF4, 0x66, 0x05,
+ 0x9C, 0x08, 0x53, 0x80, 0xEA, 0x21, 0x2C, 0x6C,
+ 0x17, 0x71, 0xD2, 0x70, 0x76, 0x9E, 0x6B, 0x7A,
+ 0x58, 0xA7, 0xBF, 0x29, 0x03, 0x1F, 0x06, 0xC1,
+ 0xDD, 0x2F, 0x5C, 0x0B, 0x0D, 0x8A, 0x0A, 0xCB,
+ 0xCA, 0x6F, 0x19, 0x6A, 0xFA, 0xF7, 0xA8, 0xA1,
+ 0xEB, 0x88, 0x44, 0xAC, 0x01, 0x4E, 0x59, 0x94,
+ 0x72, 0x2B, 0xE9, 0x0F, 0x22, 0x9B, 0x27, 0x37,
+ 0x41, 0xF9, 0xF2, 0xE3, 0xEF, 0xB3, 0xD9, 0x2A,
+ 0x31, 0xC2, 0x0C, 0x15, 0x90, 0x14, 0xF6, 0x83,
+ 0xFD, 0x96, 0x9D, 0x7F, 0xA4, 0x39, 0xE7, 0x3F,
+ 0xE6, 0xC7, 0xCD, 0x1A, 0xCF, 0x48, 0x3C, 0x51,
+ 0x6D, 0x5B, 0x74, 0xC3, 0xC9, 0x09, 0x3D, 0x9F,
+ 0xDB, 0x32, 0x40, 0x18, 0xD7, 0xCE, 0x69, 0x49,
+ 0x3A, 0xF1, 0xB9, 0x56, 0x91, 0x99, 0x84, 0x24,
+ 0x7E, 0x34, 0x4B, 0xA6, 0x47, 0xB4, 0x6E, 0xDF,
+ 0x65, 0x3B, 0xAD, 0x45, 0x13, 0xC6, 0x81, 0xF8,
+ 0x4A, 0x2D, 0x8F, 0x4C, 0x97, 0x28, 0x3E, 0xE5,
+ 0x5A, 0x35, 0xB0, 0xAE, 0x82, 0x79, 0x1D, 0x52 };
+
+unsigned char table_134[32] = {
+ 0x09, 0x0F, 0x10, 0x0C, 0x03, 0x15, 0x07, 0x17,
+ 0x0E, 0x0B, 0x1D, 0x08, 0x19, 0x11, 0x00, 0x0A,
+ 0x01, 0x06, 0x18, 0x16, 0x0D, 0x13, 0x14, 0x12,
+ 0x02, 0x1B, 0x1A, 0x04, 0x05, 0x1F, 0x1C, 0x1E };
+
+unsigned char table_135[256] = {
+ 0x14, 0x34, 0xEA, 0x02, 0x2B, 0x5A, 0x10, 0x51,
+ 0xF3, 0x8F, 0x28, 0xB2, 0x50, 0x8B, 0x01, 0xCC,
+ 0x80, 0x15, 0x29, 0x42, 0xF4, 0x1D, 0xFB, 0xBB,
+ 0x1F, 0x43, 0x8C, 0x17, 0x1E, 0x81, 0x04, 0x98,
+ 0x46, 0xD8, 0xD5, 0x65, 0x4C, 0x1C, 0xDB, 0x40,
+ 0x5F, 0x1A, 0x31, 0x74, 0xF1, 0x64, 0x19, 0x05,
+ 0xFC, 0xF0, 0x73, 0xB6, 0x23, 0x77, 0x9C, 0xCE,
+ 0x70, 0xEF, 0xDA, 0xE0, 0xA2, 0x78, 0x84, 0xEB,
+ 0x9E, 0xC5, 0x95, 0xA3, 0xF6, 0xCA, 0xAD, 0x52,
+ 0xD0, 0x3F, 0x54, 0xA7, 0x33, 0xA9, 0x09, 0x6A,
+ 0x89, 0x7E, 0x75, 0xA8, 0xD6, 0x79, 0x9F, 0xAB,
+ 0x8E, 0x11, 0x0E, 0x3B, 0xAA, 0xE6, 0x85, 0x53,
+ 0x0A, 0x59, 0xEC, 0x94, 0xD7, 0x41, 0x86, 0x7D,
+ 0x2F, 0xC7, 0xDE, 0x06, 0xCB, 0x13, 0xBA, 0x58,
+ 0xC8, 0xC9, 0x07, 0x67, 0x7F, 0xA5, 0xB4, 0x2C,
+ 0x48, 0x6C, 0xB8, 0xD1, 0x30, 0xD3, 0x35, 0x4F,
+ 0x88, 0x26, 0x93, 0x32, 0x71, 0x3E, 0x3D, 0xF7,
+ 0x6D, 0x03, 0xED, 0x8A, 0x36, 0x55, 0x9B, 0x66,
+ 0x8D, 0x27, 0x7C, 0xF9, 0xA6, 0xC3, 0x20, 0x69,
+ 0x4A, 0xE3, 0x99, 0x5C, 0xBC, 0x45, 0x16, 0x6B,
+ 0xB9, 0x49, 0x82, 0xFF, 0xBD, 0xDD, 0xE9, 0x0C,
+ 0xD4, 0x44, 0xFD, 0x22, 0xE5, 0xAC, 0x61, 0xC4,
+ 0x90, 0x47, 0x37, 0x72, 0xA4, 0x7A, 0x24, 0x4D,
+ 0x5B, 0x12, 0x38, 0x92, 0x87, 0x1B, 0xE1, 0xA0,
+ 0x91, 0x3C, 0xEE, 0x6F, 0xC1, 0x0F, 0x56, 0xC2,
+ 0x9A, 0xF8, 0x18, 0xE8, 0xD2, 0xDC, 0x4B, 0xCF,
+ 0x39, 0xF5, 0xFE, 0x2A, 0x2D, 0x9D, 0xA1, 0xFA,
+ 0xE7, 0xBF, 0x6E, 0xE4, 0x2E, 0xB3, 0xCD, 0xE2,
+ 0xAF, 0x7B, 0xC0, 0x68, 0x97, 0xB5, 0x5D, 0xB7,
+ 0x21, 0x57, 0x83, 0x76, 0xB1, 0xAE, 0x5E, 0x0D,
+ 0x96, 0x4E, 0x08, 0xC6, 0x0B, 0xDF, 0x3A, 0xB0,
+ 0x00, 0x63, 0xD9, 0xBE, 0xF2, 0x60, 0x25, 0x62 };
+
+unsigned char table_136[256] = {
+ 0xD3, 0x1A, 0x00, 0xED, 0x59, 0x24, 0xA3, 0xF2,
+ 0xBA, 0x58, 0x4C, 0x5C, 0x75, 0x48, 0x98, 0xB0,
+ 0xCF, 0xC3, 0xF7, 0x88, 0x70, 0xB3, 0x3D, 0x3E,
+ 0x03, 0xF9, 0xC9, 0xFD, 0x80, 0x44, 0x7F, 0x3B,
+ 0x95, 0x5F, 0x31, 0x47, 0x15, 0x07, 0xB8, 0x08,
+ 0xCE, 0xDA, 0x71, 0x9F, 0x83, 0xB1, 0x55, 0x16,
+ 0xE6, 0xB2, 0xC7, 0xBE, 0x54, 0xE7, 0x2E, 0x8D,
+ 0x12, 0x21, 0x41, 0x69, 0xFE, 0x28, 0x11, 0x56,
+ 0x5A, 0xDD, 0xB6, 0x87, 0x78, 0x82, 0x4D, 0x7B,
+ 0x50, 0x9A, 0x9E, 0x62, 0xF8, 0x0A, 0x64, 0xF1,
+ 0x4E, 0x33, 0xAD, 0xBB, 0x79, 0x76, 0xD8, 0xCD,
+ 0x86, 0x34, 0x29, 0xD5, 0x7D, 0x72, 0xC5, 0xC1,
+ 0xDF, 0x09, 0x4A, 0xB4, 0xD2, 0x7A, 0xF0, 0xCC,
+ 0x0F, 0xA7, 0xD6, 0x2B, 0x20, 0x26, 0xEF, 0xAB,
+ 0x74, 0x1E, 0xE3, 0x77, 0xCB, 0x7C, 0x73, 0x5E,
+ 0x6B, 0x0D, 0x65, 0xA6, 0x30, 0xFB, 0xD0, 0xB7,
+ 0xAA, 0x94, 0x9D, 0x85, 0x13, 0x18, 0xA8, 0xF3,
+ 0xE0, 0xBC, 0x45, 0xCA, 0xC8, 0xDC, 0xE2, 0x3C,
+ 0x23, 0xE5, 0xB9, 0x90, 0x49, 0xA5, 0xE4, 0x36,
+ 0xFC, 0x53, 0xF6, 0xE8, 0xC6, 0x2C, 0x02, 0x25,
+ 0xC0, 0x8F, 0x61, 0xA4, 0x39, 0x8C, 0x5D, 0xAE,
+ 0x22, 0x1C, 0x2F, 0xD4, 0x6C, 0xD1, 0x51, 0xEA,
+ 0x4F, 0x7E, 0xA0, 0xF5, 0x6A, 0x32, 0xA2, 0x01,
+ 0xB5, 0x10, 0x2A, 0xAC, 0xA9, 0x06, 0xC4, 0x91,
+ 0x68, 0xE1, 0xBD, 0x14, 0x38, 0xFA, 0x6E, 0x3F,
+ 0x37, 0x66, 0xDB, 0x57, 0x43, 0x1B, 0x67, 0xAF,
+ 0x1F, 0x0B, 0x6D, 0x2D, 0x89, 0x04, 0x4B, 0x52,
+ 0xC2, 0xBF, 0xA1, 0x92, 0x99, 0x6F, 0x63, 0x81,
+ 0x27, 0x05, 0x96, 0x3A, 0xEC, 0x0E, 0x97, 0xD9,
+ 0xDE, 0x46, 0x35, 0x8B, 0x8E, 0x8A, 0xF4, 0xFF,
+ 0x60, 0xD7, 0xE9, 0x17, 0xEB, 0x9C, 0x84, 0x0C,
+ 0x93, 0x1D, 0x9B, 0x5B, 0x40, 0xEE, 0x42, 0x19 };
+
+unsigned char table_137[32] = {
+ 0x0F, 0x09, 0x02, 0x06, 0x18, 0x0B, 0x1E, 0x05,
+ 0x11, 0x1D, 0x16, 0x01, 0x13, 0x10, 0x0E, 0x1A,
+ 0x1B, 0x00, 0x0D, 0x08, 0x15, 0x14, 0x19, 0x17,
+ 0x03, 0x1F, 0x0A, 0x12, 0x0C, 0x07, 0x04, 0x1C };
+
+unsigned char table_138[32] = {
+ 0x0D, 0x1C, 0x1F, 0x15, 0x0F, 0x14, 0x1B, 0x12,
+ 0x09, 0x0B, 0x19, 0x07, 0x11, 0x16, 0x0C, 0x04,
+ 0x13, 0x05, 0x1D, 0x03, 0x0E, 0x0A, 0x08, 0x1E,
+ 0x01, 0x06, 0x18, 0x17, 0x10, 0x1A, 0x02, 0x00 };
+
+unsigned char table_139[32] = {
+ 0x05, 0x15, 0x1D, 0x02, 0x0F, 0x03, 0x17, 0x1A,
+ 0x0A, 0x00, 0x1F, 0x12, 0x0E, 0x11, 0x1B, 0x13,
+ 0x0B, 0x0D, 0x09, 0x18, 0x1E, 0x08, 0x14, 0x07,
+ 0x0C, 0x04, 0x16, 0x19, 0x1C, 0x06, 0x10, 0x01 };
+
+unsigned char table_140[32] = {
+ 0x06, 0x1E, 0x0C, 0x11, 0x13, 0x08, 0x15, 0x01,
+ 0x1D, 0x03, 0x0F, 0x19, 0x18, 0x04, 0x00, 0x14,
+ 0x12, 0x1A, 0x0B, 0x0E, 0x02, 0x1B, 0x07, 0x05,
+ 0x1F, 0x17, 0x09, 0x0A, 0x0D, 0x16, 0x10, 0x1C };
+
+unsigned char table_141[256] = {
+ 0xE1, 0x0A, 0x28, 0xCD, 0x8A, 0x1E, 0x26, 0x10,
+ 0xC0, 0x6F, 0x06, 0x2C, 0xF8, 0x51, 0x6C, 0x8F,
+ 0xA8, 0x8C, 0x41, 0xF4, 0xED, 0x36, 0xAC, 0x89,
+ 0xBD, 0x9D, 0x42, 0x50, 0x95, 0x07, 0x2A, 0x9B,
+ 0x7E, 0xA3, 0x6B, 0x30, 0x72, 0x4E, 0xBE, 0xD8,
+ 0x8B, 0x5B, 0x1A, 0x56, 0x05, 0xEF, 0xEE, 0x64,
+ 0xFF, 0xFD, 0x93, 0xB5, 0xD6, 0x04, 0x57, 0xAE,
+ 0x4D, 0x6D, 0x2F, 0xBA, 0x40, 0xE0, 0xDB, 0xF2,
+ 0xCC, 0x08, 0x35, 0x02, 0xC4, 0x65, 0x66, 0x76,
+ 0xA1, 0x97, 0x9F, 0x6A, 0x90, 0xA7, 0x34, 0x1B,
+ 0x18, 0xB9, 0xA2, 0xDE, 0x23, 0x1F, 0xCB, 0xE6,
+ 0xAB, 0xCF, 0xAD, 0x4A, 0xF7, 0x24, 0xD0, 0xE8,
+ 0x8D, 0x49, 0xEA, 0x0F, 0x94, 0x22, 0xD3, 0x74,
+ 0x71, 0x0D, 0x21, 0x14, 0x39, 0x4B, 0x16, 0x25,
+ 0x5A, 0xB7, 0x17, 0x67, 0x59, 0x47, 0x27, 0x4F,
+ 0x32, 0x3B, 0x63, 0x0C, 0xF0, 0xF3, 0x7B, 0xC7,
+ 0xCA, 0x3A, 0x9A, 0xE2, 0xD5, 0xFA, 0x91, 0xFC,
+ 0x86, 0x81, 0x99, 0xB4, 0xBC, 0x7C, 0xC5, 0xBF,
+ 0xC1, 0xF5, 0x77, 0xA4, 0x79, 0x11, 0x8E, 0x75,
+ 0x55, 0x3D, 0x78, 0x20, 0x37, 0x3E, 0x85, 0xE4,
+ 0x2E, 0x82, 0xA9, 0x7A, 0x31, 0xC9, 0xB3, 0xFE,
+ 0x4C, 0x7D, 0xC3, 0xA0, 0x0E, 0x96, 0x5C, 0xC6,
+ 0x1C, 0x5F, 0xD7, 0xDD, 0x83, 0xC8, 0x9E, 0xEC,
+ 0x3F, 0xAF, 0x38, 0x9C, 0xD9, 0xB6, 0xDA, 0xD4,
+ 0x61, 0x44, 0x43, 0xAA, 0xB1, 0xCE, 0xE7, 0x84,
+ 0x00, 0x0B, 0xFB, 0x68, 0xC2, 0x3C, 0x58, 0xB2,
+ 0x69, 0x7F, 0x33, 0x2B, 0x80, 0x03, 0xE9, 0x88,
+ 0x29, 0x12, 0x01, 0x6E, 0x62, 0xF1, 0xA6, 0xF9,
+ 0x5D, 0xD2, 0xE3, 0x53, 0x09, 0x2D, 0xBB, 0x15,
+ 0xEB, 0x13, 0xA5, 0xF6, 0x73, 0x19, 0x60, 0xB0,
+ 0xD1, 0x48, 0x92, 0x1D, 0x52, 0x5E, 0x45, 0x70,
+ 0x98, 0x54, 0xB8, 0xDC, 0x46, 0xDF, 0x87, 0xE5 };
+
+unsigned char table_142[256] = {
+ 0x90, 0x94, 0xBE, 0x14, 0x99, 0xEB, 0x45, 0x0F,
+ 0x34, 0x4A, 0xE3, 0x79, 0xD2, 0x64, 0x4D, 0x69,
+ 0x91, 0xDE, 0xB9, 0x1C, 0x59, 0x20, 0x6C, 0x0B,
+ 0x16, 0xC7, 0x1D, 0x18, 0x02, 0x7D, 0x13, 0xB2,
+ 0x7B, 0x81, 0xCF, 0x61, 0xA3, 0x33, 0x00, 0x73,
+ 0x5A, 0x8A, 0xA1, 0xA8, 0x31, 0xAC, 0xF0, 0x67,
+ 0xAE, 0xA5, 0x2A, 0x96, 0x58, 0xF4, 0xB7, 0x0E,
+ 0xE1, 0x54, 0x27, 0x83, 0x09, 0x85, 0xF8, 0x84,
+ 0xEA, 0xAD, 0x06, 0xED, 0x43, 0xFF, 0xA2, 0x6E,
+ 0x68, 0x46, 0x74, 0x47, 0x3C, 0xAA, 0xBC, 0x55,
+ 0xA7, 0xC3, 0x82, 0xDC, 0xBF, 0x38, 0x80, 0x15,
+ 0xF6, 0xB3, 0x92, 0x7C, 0x93, 0x3F, 0xE9, 0x4C,
+ 0x35, 0x30, 0x32, 0xF3, 0x88, 0xC0, 0x49, 0x6D,
+ 0xCE, 0x42, 0xDF, 0xFD, 0x78, 0x6A, 0x24, 0xCA,
+ 0xB8, 0xFC, 0xA6, 0x5F, 0x29, 0xFE, 0x0C, 0x5C,
+ 0x0D, 0x23, 0x8B, 0x9D, 0xD4, 0x03, 0x2C, 0x9C,
+ 0x77, 0xD8, 0x39, 0x8C, 0x57, 0xD5, 0xE0, 0x8F,
+ 0xC6, 0xB0, 0xCD, 0x48, 0xC9, 0xA0, 0xDA, 0xC8,
+ 0xD1, 0x5B, 0xAB, 0x37, 0x5D, 0x63, 0xAF, 0xF9,
+ 0x17, 0x1B, 0xE5, 0xF1, 0x36, 0xC1, 0x04, 0x26,
+ 0x6F, 0x9E, 0xD9, 0x2F, 0x7F, 0xB5, 0x3A, 0xD6,
+ 0xE6, 0x40, 0x07, 0xCB, 0x7E, 0x3E, 0xC5, 0x22,
+ 0xEC, 0xE2, 0xD3, 0x4E, 0x65, 0x2D, 0x70, 0xE7,
+ 0x10, 0x19, 0xD0, 0xEF, 0xBD, 0xC2, 0x44, 0xB4,
+ 0xF7, 0xA4, 0x53, 0x9F, 0x86, 0xFA, 0xE8, 0x4B,
+ 0x28, 0x3D, 0x9B, 0x56, 0x89, 0x6B, 0x25, 0x71,
+ 0x60, 0x11, 0x9A, 0x5E, 0x1A, 0x52, 0x08, 0x4F,
+ 0xB1, 0xDD, 0xBB, 0x98, 0xFB, 0x12, 0x3B, 0x0A,
+ 0x2E, 0xDB, 0x62, 0x8D, 0xC4, 0x75, 0xA9, 0x2B,
+ 0xE4, 0x97, 0x72, 0xF5, 0xEE, 0xF2, 0xB6, 0x21,
+ 0xBA, 0x7A, 0x76, 0x41, 0x50, 0x66, 0x05, 0x8E,
+ 0xCC, 0x1E, 0x87, 0xD7, 0x01, 0x1F, 0x51, 0x95 };
+
+unsigned char table_143[32] = {
+ 0x0E, 0x16, 0x18, 0x11, 0x0C, 0x01, 0x12, 0x1F,
+ 0x08, 0x15, 0x0A, 0x06, 0x1C, 0x1E, 0x02, 0x1A,
+ 0x17, 0x03, 0x07, 0x13, 0x05, 0x19, 0x10, 0x0F,
+ 0x0D, 0x14, 0x09, 0x0B, 0x1B, 0x00, 0x1D, 0x04 };
+
+unsigned char table_144[32] = {
+ 0x00, 0x1B, 0x17, 0x19, 0x1D, 0x11, 0x0D, 0x1A,
+ 0x13, 0x03, 0x1E, 0x09, 0x10, 0x0E, 0x15, 0x05,
+ 0x0B, 0x1C, 0x1F, 0x08, 0x0A, 0x06, 0x01, 0x0F,
+ 0x16, 0x14, 0x02, 0x04, 0x07, 0x18, 0x12, 0x0C };
+
+unsigned char table_145[256] = {
+ 0xF9, 0x2C, 0x38, 0x74, 0xDA, 0x65, 0x85, 0x0E,
+ 0xBA, 0x64, 0xDB, 0xE3, 0xB6, 0x8B, 0x0B, 0x5E,
+ 0x01, 0x0F, 0x12, 0x8C, 0xD4, 0xCC, 0xB1, 0x7B,
+ 0xE7, 0xBC, 0x2E, 0x87, 0x84, 0x3B, 0xF8, 0x4C,
+ 0x8E, 0x59, 0x2D, 0xAA, 0xCE, 0x28, 0x1B, 0xEE,
+ 0x7F, 0x5C, 0xFB, 0x62, 0x05, 0xD9, 0xDD, 0x9D,
+ 0x49, 0x66, 0x82, 0x71, 0xD2, 0xC7, 0xEB, 0xCF,
+ 0x5B, 0x41, 0x25, 0xC8, 0x6C, 0xFF, 0x78, 0x97,
+ 0x0C, 0xA2, 0x50, 0x7A, 0xAF, 0x2F, 0xB0, 0x7E,
+ 0xBB, 0x73, 0xA0, 0x9B, 0x09, 0xDE, 0x35, 0xE9,
+ 0x5A, 0x70, 0x56, 0xC5, 0x81, 0x19, 0x55, 0xAB,
+ 0xC1, 0xB4, 0x2A, 0x30, 0x54, 0x6F, 0x3E, 0x46,
+ 0x5D, 0x37, 0xF5, 0x57, 0x6B, 0x7C, 0x43, 0xE1,
+ 0x4A, 0x3F, 0xB2, 0x4B, 0x77, 0xB5, 0x44, 0xD6,
+ 0x91, 0x11, 0x72, 0xE8, 0xBE, 0xA5, 0xA8, 0xD3,
+ 0x9A, 0x17, 0x86, 0x88, 0x16, 0x3C, 0x36, 0xD8,
+ 0x6E, 0x07, 0x8D, 0x5F, 0xFA, 0xF1, 0x24, 0x7D,
+ 0x20, 0x60, 0x0D, 0x89, 0xC9, 0x29, 0xA7, 0x2B,
+ 0x4E, 0x10, 0x9F, 0xE5, 0x61, 0x32, 0x3A, 0xBF,
+ 0x93, 0xE6, 0xF3, 0x52, 0x80, 0xC4, 0x02, 0x22,
+ 0xA4, 0xBD, 0xF0, 0x48, 0x51, 0xF2, 0xD7, 0x33,
+ 0x00, 0x53, 0x98, 0xEC, 0x47, 0x39, 0xB9, 0x90,
+ 0x76, 0x4F, 0x68, 0x3D, 0x9C, 0x92, 0xD5, 0xB8,
+ 0xAE, 0xD0, 0xF4, 0x67, 0x58, 0xC0, 0x06, 0x08,
+ 0x14, 0x31, 0xDC, 0xA1, 0x15, 0xDF, 0xCA, 0xE2,
+ 0x23, 0xFE, 0xE4, 0x8F, 0x0A, 0xFC, 0x8A, 0xA3,
+ 0xC6, 0xCD, 0x6A, 0x75, 0xFD, 0x42, 0xB7, 0x79,
+ 0x96, 0x1D, 0x63, 0x18, 0xA9, 0x1C, 0x83, 0x6D,
+ 0xE0, 0x34, 0x04, 0xA6, 0x13, 0xAC, 0xD1, 0xF7,
+ 0x26, 0xC3, 0x1F, 0x27, 0x45, 0x95, 0xCB, 0x21,
+ 0xED, 0x1A, 0x9E, 0x99, 0xEA, 0x40, 0x94, 0x4D,
+ 0x69, 0xF6, 0xEF, 0xC2, 0xAD, 0x03, 0xB3, 0x1E };
+
+unsigned char table_146[256] = {
+ 0x1C, 0xF5, 0x16, 0xD2, 0xCC, 0xDC, 0x1E, 0x29,
+ 0xE3, 0x17, 0x3B, 0x66, 0x6A, 0xF7, 0x03, 0xB2,
+ 0x92, 0x45, 0x4D, 0xD6, 0x0C, 0x5E, 0xE6, 0x01,
+ 0xDE, 0xCE, 0x83, 0xFA, 0x35, 0x02, 0x85, 0xC4,
+ 0x2E, 0x89, 0x8D, 0xE7, 0x30, 0x93, 0xDD, 0x70,
+ 0x80, 0xD9, 0x6D, 0x81, 0x07, 0x8E, 0xA9, 0xA6,
+ 0x5F, 0xC9, 0xF3, 0x9D, 0x65, 0xE8, 0x88, 0x0B,
+ 0x49, 0xAA, 0xB7, 0x6C, 0x11, 0xFC, 0x6F, 0xA3,
+ 0xF8, 0x52, 0x0E, 0xD4, 0x08, 0x25, 0x27, 0x33,
+ 0x2F, 0xF0, 0x2B, 0x47, 0xDA, 0x4C, 0x39, 0x54,
+ 0xB9, 0xC1, 0xEA, 0x7C, 0x44, 0xEB, 0x06, 0xE1,
+ 0x8C, 0x9B, 0x74, 0x42, 0x4F, 0x0A, 0x69, 0x2A,
+ 0x2D, 0xA1, 0x19, 0xD5, 0xC3, 0x87, 0x68, 0xFF,
+ 0xEC, 0xE4, 0x86, 0xCF, 0xF6, 0x79, 0x34, 0xA8,
+ 0x72, 0xF4, 0x8B, 0xAF, 0xA5, 0x00, 0xBA, 0x5C,
+ 0x23, 0xB8, 0xC8, 0x59, 0xBF, 0x6E, 0xCB, 0x20,
+ 0x1F, 0x53, 0x97, 0x4B, 0xD0, 0x55, 0x5B, 0xDF,
+ 0x8A, 0xED, 0x9A, 0x62, 0xC5, 0xD7, 0x18, 0x82,
+ 0xC7, 0x12, 0x15, 0x1B, 0xC0, 0x38, 0xCA, 0x26,
+ 0xDB, 0xAE, 0xF9, 0x90, 0x1A, 0xF2, 0x56, 0x32,
+ 0x21, 0x3C, 0x43, 0xEE, 0xA4, 0x13, 0x94, 0xA2,
+ 0x46, 0x77, 0xBC, 0xB6, 0x9C, 0x0D, 0xCD, 0x37,
+ 0x63, 0x60, 0x6B, 0x3A, 0x3E, 0xA7, 0xD8, 0xFE,
+ 0xFB, 0xEF, 0x67, 0xFD, 0xAD, 0xF1, 0x09, 0x1D,
+ 0xE9, 0x51, 0xB4, 0x95, 0x75, 0x0F, 0xB3, 0xD3,
+ 0xAB, 0x22, 0xBB, 0x61, 0x7F, 0x5A, 0x58, 0x7B,
+ 0x73, 0xC2, 0x05, 0xE0, 0x14, 0xE2, 0xAC, 0x91,
+ 0xBE, 0x4E, 0xC6, 0x7A, 0x84, 0x50, 0x28, 0x3F,
+ 0xB0, 0x04, 0x7E, 0xD1, 0x40, 0xBD, 0xE5, 0x71,
+ 0xB1, 0x78, 0x41, 0x9E, 0x57, 0x64, 0x8F, 0x24,
+ 0x4A, 0x9F, 0x3D, 0x31, 0x36, 0x5D, 0xA0, 0x2C,
+ 0x7D, 0x96, 0x76, 0x99, 0xB5, 0x48, 0x98, 0x10 };
+
+unsigned char table_147[32] = {
+ 0x17, 0x07, 0x0D, 0x16, 0x00, 0x1B, 0x1F, 0x09,
+ 0x10, 0x11, 0x14, 0x0A, 0x02, 0x06, 0x13, 0x0C,
+ 0x08, 0x1E, 0x0F, 0x12, 0x05, 0x15, 0x19, 0x01,
+ 0x1C, 0x1A, 0x03, 0x18, 0x04, 0x0B, 0x1D, 0x0E };
+
+unsigned char table_148[256] = {
+ 0xFB, 0x23, 0xBC, 0x5A, 0x8C, 0x02, 0x42, 0x3B,
+ 0x95, 0x0C, 0x21, 0x0E, 0x14, 0xDF, 0x11, 0xC0,
+ 0xDB, 0x5E, 0xD3, 0xEA, 0xCE, 0xB4, 0x32, 0x12,
+ 0x70, 0x68, 0xA3, 0x25, 0x5B, 0x4B, 0x47, 0xA5,
+ 0x84, 0x9B, 0xFA, 0xD1, 0xE1, 0x3C, 0x20, 0x93,
+ 0x41, 0x26, 0x81, 0x39, 0x17, 0xA4, 0xCF, 0xB9,
+ 0xC5, 0x5F, 0x1C, 0xB3, 0x88, 0xC2, 0x92, 0x30,
+ 0x0A, 0xB8, 0xA0, 0xE2, 0x50, 0x2B, 0x48, 0x1E,
+ 0xD5, 0x13, 0xC7, 0x46, 0x9E, 0x2A, 0xF7, 0x7E,
+ 0xE8, 0x82, 0x60, 0x7A, 0x36, 0x97, 0x0F, 0x8F,
+ 0x8B, 0x80, 0xE0, 0xEB, 0xB1, 0xC6, 0x6E, 0xAE,
+ 0x90, 0x76, 0xA7, 0x31, 0xBE, 0x9C, 0x18, 0x6D,
+ 0xAB, 0x6C, 0x7B, 0xFE, 0x62, 0x05, 0xE9, 0x66,
+ 0x2E, 0x38, 0xB5, 0xB2, 0xFD, 0xFC, 0x7F, 0xE3,
+ 0xA1, 0xF1, 0x99, 0x4D, 0x79, 0x22, 0xD2, 0x37,
+ 0x29, 0x01, 0x54, 0x00, 0xBD, 0x51, 0x1B, 0x07,
+ 0x0B, 0x4A, 0xEE, 0x57, 0xDA, 0x1A, 0x06, 0xCA,
+ 0xCB, 0x9A, 0xC9, 0x7D, 0xE4, 0xDC, 0xE5, 0x8D,
+ 0x75, 0x4F, 0xF6, 0xA2, 0x65, 0x7C, 0xD9, 0x9D,
+ 0x03, 0x27, 0x2D, 0x4C, 0x49, 0xD4, 0x5D, 0x3E,
+ 0xBA, 0x1D, 0xD8, 0x91, 0x74, 0x10, 0xF8, 0xDE,
+ 0xEF, 0xF0, 0x6A, 0x04, 0x72, 0x08, 0x78, 0x3A,
+ 0x53, 0xC4, 0x34, 0xF2, 0x64, 0xAF, 0x86, 0xC3,
+ 0xF3, 0x73, 0x67, 0xCC, 0x58, 0xF4, 0x96, 0xAC,
+ 0x3D, 0xE7, 0x15, 0x8E, 0x19, 0x61, 0xF9, 0xB6,
+ 0xCD, 0x87, 0xAA, 0xB0, 0x1F, 0x6F, 0xAD, 0x28,
+ 0xC8, 0x69, 0x56, 0xC1, 0x71, 0xED, 0xE6, 0x98,
+ 0x6B, 0x59, 0xB7, 0xF5, 0x2C, 0xEC, 0xA8, 0x94,
+ 0x89, 0xBB, 0xA9, 0xD7, 0x2F, 0x8A, 0x4E, 0xD6,
+ 0x33, 0x16, 0x0D, 0x83, 0x5C, 0x52, 0x85, 0xA6,
+ 0x40, 0x45, 0x9F, 0x44, 0x63, 0x35, 0x77, 0xFF,
+ 0x09, 0x43, 0xBF, 0xD0, 0x55, 0xDD, 0x3F, 0x24 };
+
+unsigned char table_149[32] = {
+ 0x1B, 0x0B, 0x0C, 0x06, 0x1F, 0x17, 0x04, 0x1A,
+ 0x1E, 0x02, 0x0F, 0x16, 0x0E, 0x09, 0x10, 0x01,
+ 0x13, 0x19, 0x11, 0x00, 0x0A, 0x05, 0x03, 0x1C,
+ 0x18, 0x1D, 0x14, 0x0D, 0x07, 0x08, 0x15, 0x12 };
+
+unsigned char table_150[256] = {
+ 0x57, 0xBC, 0x9D, 0x46, 0x14, 0xD0, 0x94, 0x95,
+ 0x1B, 0x12, 0xB8, 0xD4, 0x53, 0x73, 0x83, 0xE6,
+ 0x75, 0xE1, 0xD1, 0x0D, 0xDF, 0x23, 0x13, 0x40,
+ 0xF1, 0x0C, 0xA0, 0xC1, 0x22, 0xDA, 0xE8, 0xFB,
+ 0xE5, 0xC4, 0x16, 0x9C, 0x3F, 0xC3, 0x78, 0x3A,
+ 0x06, 0xC7, 0xA8, 0x79, 0xA4, 0xB3, 0x55, 0x88,
+ 0xA9, 0x82, 0xE3, 0x68, 0xFC, 0x3B, 0x26, 0x81,
+ 0xB4, 0x0A, 0x7D, 0x96, 0xDB, 0x2C, 0xE2, 0xCD,
+ 0x92, 0x5C, 0xED, 0x0E, 0x42, 0x98, 0xBE, 0xB7,
+ 0x63, 0x25, 0x7B, 0xD9, 0xEF, 0x11, 0xB9, 0xA3,
+ 0xFA, 0x00, 0x2A, 0x91, 0x71, 0xBF, 0xB2, 0x3D,
+ 0x20, 0x4C, 0xB0, 0x8C, 0x3C, 0x27, 0xAF, 0x09,
+ 0x10, 0x5D, 0x2B, 0x1D, 0xBD, 0x4B, 0x54, 0xD3,
+ 0xAB, 0x1A, 0xE7, 0xF8, 0x56, 0x65, 0xA5, 0xAD,
+ 0xEC, 0x17, 0x45, 0x28, 0xCA, 0xEA, 0x01, 0xF5,
+ 0x34, 0x84, 0x43, 0x8B, 0x03, 0x02, 0x90, 0x6B,
+ 0x60, 0xCE, 0x19, 0x86, 0x4F, 0x08, 0x35, 0x9A,
+ 0xAE, 0x07, 0xE0, 0xB6, 0xD6, 0x2D, 0xD2, 0x89,
+ 0x5F, 0xA6, 0x72, 0x05, 0x36, 0xB5, 0xC0, 0x5A,
+ 0x4D, 0xD7, 0x30, 0x37, 0x87, 0x50, 0xA2, 0x48,
+ 0x29, 0xAC, 0xDE, 0x93, 0x24, 0x6E, 0x1E, 0xF7,
+ 0x52, 0x5E, 0x41, 0xC8, 0xEB, 0x31, 0x7E, 0xE9,
+ 0x67, 0x7A, 0x47, 0x85, 0x8D, 0x74, 0x9E, 0x64,
+ 0x38, 0x9B, 0xBA, 0xCC, 0x9F, 0x8E, 0xEE, 0x0F,
+ 0xB1, 0x7C, 0x6A, 0xBB, 0x2E, 0x58, 0x70, 0x7F,
+ 0x4E, 0x4A, 0x1C, 0x5B, 0xF0, 0xA1, 0x61, 0xF6,
+ 0x15, 0x33, 0xE4, 0xF9, 0x2F, 0x62, 0x1F, 0x76,
+ 0x32, 0xCB, 0x49, 0xFE, 0x8F, 0xD5, 0xDC, 0x66,
+ 0x0B, 0x3E, 0xC5, 0x21, 0xC6, 0x6C, 0x18, 0xC2,
+ 0x6D, 0xFF, 0x51, 0x99, 0xCF, 0xFD, 0x59, 0xA7,
+ 0xAA, 0x8A, 0xF2, 0x69, 0x39, 0x6F, 0x77, 0xDD,
+ 0x97, 0xC9, 0xF3, 0x04, 0xD8, 0xF4, 0x80, 0x44 };
+
+unsigned char table_151[256] = {
+ 0x78, 0x6C, 0xC5, 0x0C, 0x2D, 0xA7, 0x97, 0x9C,
+ 0x22, 0x76, 0x3E, 0x81, 0x51, 0x47, 0x59, 0x71,
+ 0xB1, 0xA2, 0x4A, 0x3C, 0xB5, 0x16, 0x06, 0x95,
+ 0xB9, 0x01, 0xE6, 0x91, 0x96, 0x1C, 0x1B, 0xAD,
+ 0x61, 0x64, 0xB2, 0xE7, 0x29, 0x19, 0x52, 0x3B,
+ 0xFA, 0xAF, 0x30, 0xDB, 0xD4, 0x0B, 0xFE, 0x75,
+ 0x1F, 0xBE, 0xCB, 0xF6, 0xEA, 0x31, 0xF8, 0xD8,
+ 0xA3, 0x82, 0x73, 0x1D, 0x99, 0xF0, 0xCC, 0xB6,
+ 0x46, 0x26, 0xAA, 0x8C, 0x87, 0x90, 0x24, 0x8F,
+ 0x7A, 0x13, 0xEE, 0xD1, 0xA9, 0x05, 0xB3, 0xF7,
+ 0x02, 0x7C, 0x4C, 0x1E, 0xFF, 0xE5, 0x77, 0xAB,
+ 0xD6, 0x98, 0x20, 0x4D, 0xC4, 0x23, 0xF4, 0xA4,
+ 0x85, 0x9A, 0x8E, 0x1A, 0x0E, 0xF5, 0x15, 0x60,
+ 0x38, 0x72, 0xE9, 0xF1, 0xC3, 0x68, 0xF2, 0x93,
+ 0xD3, 0x2A, 0x48, 0x74, 0xC2, 0x57, 0xA1, 0x7D,
+ 0x94, 0x37, 0x92, 0x5C, 0xE1, 0x41, 0x83, 0xD5,
+ 0x65, 0x14, 0xA6, 0xDC, 0x44, 0x27, 0xEF, 0xD7,
+ 0x25, 0x10, 0x2C, 0x7F, 0x40, 0xA5, 0x55, 0xBD,
+ 0x2B, 0x0D, 0xD0, 0xFC, 0xDF, 0xA0, 0x04, 0x00,
+ 0x62, 0xB4, 0x5A, 0xEB, 0x6B, 0x84, 0x7E, 0x6A,
+ 0xDE, 0xED, 0x66, 0x03, 0xFB, 0x2E, 0x4F, 0x4E,
+ 0xBB, 0x36, 0x5B, 0x18, 0xE3, 0x69, 0x3F, 0xEC,
+ 0xE4, 0xD2, 0x0A, 0x34, 0x63, 0xCF, 0xA8, 0xF9,
+ 0x9B, 0x7B, 0x6F, 0xE8, 0x49, 0xC1, 0x09, 0x54,
+ 0xF3, 0x50, 0x67, 0x79, 0xC0, 0x9F, 0x8D, 0x5F,
+ 0x17, 0x70, 0x11, 0xC8, 0xBC, 0xC6, 0xE0, 0x35,
+ 0x39, 0xC7, 0x6E, 0x21, 0xBF, 0xDA, 0x6D, 0x28,
+ 0x0F, 0xDD, 0x33, 0xAC, 0x8A, 0x12, 0xC9, 0xCD,
+ 0xB8, 0x45, 0xAE, 0x32, 0xCE, 0xE2, 0x56, 0xFD,
+ 0x42, 0x89, 0x86, 0xCA, 0x4B, 0x3D, 0x5E, 0xBA,
+ 0x8B, 0x5D, 0xB0, 0xB7, 0xD9, 0x58, 0x2F, 0x08,
+ 0x43, 0x3A, 0x53, 0x9E, 0x80, 0x88, 0x07, 0x9D };
+
+unsigned char table_152[32] = {
+ 0x02, 0x1A, 0x17, 0x1D, 0x01, 0x03, 0x13, 0x1E,
+ 0x05, 0x18, 0x06, 0x0A, 0x0C, 0x04, 0x1B, 0x00,
+ 0x1C, 0x09, 0x1F, 0x16, 0x07, 0x0F, 0x0B, 0x0E,
+ 0x14, 0x12, 0x0D, 0x10, 0x19, 0x11, 0x08, 0x15 };
+
+unsigned char table_153[32] = {
+ 0x0E, 0x14, 0x12, 0x1E, 0x1C, 0x02, 0x06, 0x16,
+ 0x18, 0x0D, 0x17, 0x0C, 0x1D, 0x11, 0x08, 0x19,
+ 0x07, 0x0F, 0x13, 0x04, 0x03, 0x1B, 0x0B, 0x1F,
+ 0x1A, 0x0A, 0x05, 0x10, 0x00, 0x01, 0x15, 0x09 };
+
+unsigned char table_154[256] = {
+ 0x27, 0x5A, 0x08, 0x5B, 0xF4, 0x39, 0x13, 0x6F,
+ 0x67, 0xEA, 0x22, 0xCA, 0x5C, 0xCF, 0x18, 0x7C,
+ 0x05, 0x87, 0x60, 0xCC, 0x40, 0xC6, 0xE8, 0x6D,
+ 0xF5, 0x2A, 0x2D, 0xA2, 0x8C, 0x82, 0xE9, 0xDC,
+ 0xD6, 0x65, 0x74, 0x8E, 0x42, 0x4F, 0x3E, 0x55,
+ 0xFF, 0xC7, 0x9D, 0x0F, 0x81, 0xE2, 0x4C, 0xE6,
+ 0xEB, 0x4D, 0x70, 0xD1, 0x49, 0x43, 0x3D, 0x69,
+ 0x0C, 0x45, 0x28, 0x00, 0x99, 0xAE, 0xEC, 0xB8,
+ 0xC3, 0x17, 0x93, 0x8D, 0x36, 0x3C, 0x46, 0x2B,
+ 0x29, 0xC5, 0xB4, 0xB1, 0xD0, 0x0D, 0xAD, 0xFE,
+ 0xE5, 0xA8, 0x3B, 0x1A, 0x2C, 0xDF, 0x07, 0x86,
+ 0xB0, 0xD3, 0x7A, 0x59, 0x79, 0x8B, 0xC1, 0x9A,
+ 0x30, 0xDB, 0x24, 0xF3, 0xD8, 0x04, 0x25, 0xC2,
+ 0xA3, 0x98, 0x96, 0x7B, 0x71, 0x4E, 0x5E, 0x58,
+ 0xA5, 0x51, 0x88, 0xDA, 0xF8, 0xC0, 0x7D, 0xF6,
+ 0x31, 0x5F, 0x09, 0x16, 0x21, 0x62, 0x01, 0x64,
+ 0x9B, 0x3A, 0x2F, 0x61, 0x19, 0xA1, 0xB7, 0xE0,
+ 0xB9, 0x12, 0xA0, 0xBA, 0x6E, 0x8A, 0xFB, 0xD9,
+ 0x38, 0x1B, 0xD5, 0xB3, 0x10, 0xED, 0xE4, 0x6A,
+ 0x32, 0xBD, 0x75, 0xD4, 0x1C, 0xFD, 0x73, 0x77,
+ 0x54, 0xC8, 0x97, 0x47, 0x35, 0x94, 0xE3, 0xCD,
+ 0x6B, 0xBB, 0xF9, 0xAC, 0x11, 0x14, 0xAF, 0x78,
+ 0x3F, 0xCE, 0x26, 0x44, 0xEE, 0xFC, 0x15, 0x66,
+ 0x4B, 0xA6, 0x20, 0x23, 0xBE, 0x84, 0x1D, 0x7E,
+ 0x0B, 0x56, 0x92, 0x0A, 0xFA, 0xF7, 0x48, 0x33,
+ 0x9E, 0x8F, 0xAB, 0x5D, 0x41, 0x50, 0xA4, 0x7F,
+ 0x80, 0x4A, 0x68, 0x06, 0x2E, 0x6C, 0xC4, 0x02,
+ 0x0E, 0x63, 0xF0, 0xC9, 0x91, 0xB2, 0xD2, 0x03,
+ 0x37, 0xEF, 0x9C, 0x90, 0x83, 0x76, 0x1E, 0xA9,
+ 0x85, 0xB6, 0x57, 0xD7, 0xF2, 0xF1, 0xE7, 0xDE,
+ 0xCB, 0xAA, 0xBF, 0x89, 0x1F, 0xA7, 0xBC, 0x9F,
+ 0x53, 0xE1, 0xDD, 0x72, 0x95, 0x52, 0x34, 0xB5 };
+
+unsigned char table_155[256] = {
+ 0x75, 0x58, 0xC5, 0xA5, 0x83, 0x16, 0xF3, 0x7F,
+ 0x94, 0xDE, 0xA0, 0xF6, 0xFD, 0x89, 0xA8, 0x06,
+ 0x98, 0x01, 0xD9, 0x69, 0xB7, 0x0F, 0xEA, 0x73,
+ 0x32, 0xF0, 0x49, 0xBF, 0x02, 0xE7, 0x22, 0x3F,
+ 0xDB, 0x30, 0x5F, 0x20, 0x6A, 0x93, 0x07, 0xBC,
+ 0x09, 0x0D, 0x37, 0x24, 0x90, 0x15, 0x80, 0xAF,
+ 0x8F, 0x59, 0x28, 0xFF, 0x6D, 0x1E, 0x52, 0x62,
+ 0xE2, 0xDD, 0x85, 0x48, 0xB5, 0xAB, 0x68, 0xAC,
+ 0x7E, 0x26, 0x2C, 0xF9, 0x2A, 0xBE, 0x5B, 0xCE,
+ 0x87, 0x1D, 0x96, 0xBD, 0xEF, 0x29, 0xA9, 0xC3,
+ 0x9D, 0x57, 0x79, 0x6B, 0x7A, 0x82, 0x78, 0x0A,
+ 0x91, 0xF2, 0x7C, 0xC2, 0x25, 0x88, 0xE3, 0x47,
+ 0x64, 0x46, 0x8D, 0x19, 0xF4, 0xE6, 0xF1, 0x53,
+ 0x9C, 0x54, 0x23, 0xAD, 0xA3, 0x86, 0x3A, 0x04,
+ 0x67, 0x1C, 0xF5, 0x43, 0x05, 0x42, 0xD6, 0x4B,
+ 0xFB, 0xD4, 0x2B, 0x08, 0x45, 0xD8, 0xCD, 0xEB,
+ 0x31, 0x4A, 0x5A, 0x34, 0x9B, 0xEC, 0x4D, 0xB4,
+ 0xC6, 0xFE, 0xD5, 0x5E, 0xC1, 0x39, 0x81, 0xCF,
+ 0x03, 0x6E, 0x95, 0x50, 0xA1, 0x3B, 0xB3, 0xE5,
+ 0x3D, 0xB1, 0xB2, 0x41, 0x17, 0x2F, 0x2E, 0xE4,
+ 0x1F, 0xDC, 0xB0, 0xB6, 0x18, 0x6F, 0x44, 0x12,
+ 0x0B, 0xCC, 0x4E, 0xC0, 0x51, 0x14, 0x76, 0x3C,
+ 0xB9, 0x9F, 0xA4, 0xD3, 0xA7, 0xE8, 0x13, 0x55,
+ 0xC8, 0x8C, 0xD2, 0xEE, 0x65, 0xB8, 0xAA, 0x6C,
+ 0x2D, 0x4F, 0x56, 0xFA, 0x61, 0x4C, 0xE0, 0x5C,
+ 0xA6, 0x1A, 0xD1, 0x38, 0xD7, 0x72, 0x60, 0x74,
+ 0xE1, 0xBA, 0x84, 0x3E, 0x40, 0xF8, 0xC7, 0x36,
+ 0x27, 0x0C, 0x70, 0x97, 0x9A, 0x7D, 0x35, 0x71,
+ 0xCA, 0x1B, 0x99, 0x8E, 0xAE, 0x66, 0x63, 0xE9,
+ 0xC9, 0x11, 0x8A, 0x21, 0x92, 0x5D, 0x77, 0x10,
+ 0xD0, 0xC4, 0xF7, 0x7B, 0x9E, 0xCB, 0xED, 0x0E,
+ 0x8B, 0x33, 0xFC, 0xBB, 0x00, 0xA2, 0xDF, 0xDA };
+
+unsigned char table_156[256] = {
+ 0x31, 0x25, 0xB1, 0xD3, 0xAF, 0xAE, 0x84, 0x2C,
+ 0x71, 0x5E, 0xD8, 0x80, 0x6F, 0x3E, 0x48, 0x86,
+ 0xED, 0x54, 0x6A, 0xC3, 0xBC, 0xBF, 0x0E, 0xEA,
+ 0x10, 0xA2, 0x9D, 0x91, 0x32, 0xE2, 0x7E, 0x1B,
+ 0x49, 0x27, 0xFF, 0xDD, 0x8A, 0x2F, 0x8D, 0x38,
+ 0xFA, 0x3C, 0x03, 0x14, 0x0F, 0x89, 0xCC, 0x07,
+ 0x1A, 0xA0, 0x97, 0x37, 0xA6, 0xD6, 0x63, 0x87,
+ 0xA1, 0xC2, 0x4B, 0x39, 0xCB, 0xCF, 0x69, 0x4E,
+ 0xC9, 0x28, 0x1C, 0xBB, 0x42, 0x2B, 0xA9, 0x78,
+ 0x5B, 0xF6, 0xE0, 0xD0, 0x5F, 0x46, 0x98, 0xCE,
+ 0x1F, 0x7A, 0x34, 0x8B, 0xFD, 0x9B, 0xEF, 0x74,
+ 0x05, 0xF2, 0x02, 0xC6, 0xDF, 0x73, 0x5C, 0x8E,
+ 0xDE, 0x88, 0x57, 0x3B, 0x85, 0xBD, 0xC0, 0x3A,
+ 0x45, 0x4D, 0x2D, 0x72, 0x0C, 0x60, 0xCA, 0x5D,
+ 0x06, 0x04, 0x3D, 0x51, 0x15, 0xAD, 0xE8, 0x67,
+ 0xBA, 0x43, 0x7D, 0xF8, 0xB2, 0xE6, 0xAB, 0xF4,
+ 0x23, 0x6E, 0xF0, 0x6B, 0x0B, 0x2E, 0xC8, 0xC4,
+ 0x4F, 0xA8, 0x6D, 0x26, 0xE9, 0x9C, 0x22, 0xB7,
+ 0x00, 0xB3, 0x0A, 0x7C, 0x44, 0x55, 0x75, 0xD5,
+ 0xAA, 0x66, 0x56, 0x24, 0x83, 0x90, 0xA4, 0xF5,
+ 0xCD, 0xEC, 0x18, 0xDC, 0xFE, 0x96, 0xA3, 0xF7,
+ 0xD2, 0xFB, 0xD1, 0x65, 0xC5, 0x08, 0x7B, 0x70,
+ 0x16, 0x9A, 0x20, 0x09, 0x29, 0xDA, 0x52, 0x5A,
+ 0x59, 0xB4, 0x77, 0x62, 0x9E, 0x19, 0x7F, 0x82,
+ 0x4C, 0xB6, 0x0D, 0x58, 0xEE, 0x1D, 0xB9, 0x93,
+ 0x50, 0xD9, 0x30, 0xE4, 0x13, 0x01, 0x36, 0x8F,
+ 0x53, 0x3F, 0x64, 0xA5, 0xB5, 0xD7, 0x81, 0x41,
+ 0x17, 0xE5, 0x94, 0xE3, 0xF9, 0x61, 0x76, 0xE1,
+ 0x9F, 0xFC, 0x1E, 0x12, 0xDB, 0x21, 0x79, 0x2A,
+ 0xAC, 0xF3, 0x6C, 0xC1, 0x95, 0x92, 0xEB, 0xA7,
+ 0x11, 0xC7, 0xB8, 0x4A, 0x33, 0xB0, 0x99, 0xE7,
+ 0xF1, 0x68, 0xBE, 0x35, 0x40, 0x8C, 0xD4, 0x47 };
+
+unsigned char table_157[32] = {
+ 0x00, 0x0D, 0x03, 0x02, 0x11, 0x04, 0x18, 0x0B,
+ 0x14, 0x1D, 0x1C, 0x13, 0x1B, 0x17, 0x10, 0x15,
+ 0x01, 0x19, 0x07, 0x09, 0x1A, 0x16, 0x12, 0x1E,
+ 0x08, 0x06, 0x0C, 0x0E, 0x1F, 0x0F, 0x0A, 0x05 };
+
+unsigned char table_158[256] = {
+ 0x68, 0x26, 0x80, 0x0B, 0xB8, 0xD5, 0x8C, 0xB7,
+ 0x65, 0xEF, 0xBC, 0x94, 0x28, 0xB9, 0xB2, 0xD2,
+ 0x92, 0xA4, 0x55, 0x27, 0xE0, 0x40, 0x6C, 0x41,
+ 0x25, 0xBD, 0xAF, 0xEA, 0xB1, 0x19, 0xA5, 0xC9,
+ 0x0E, 0xED, 0xB4, 0xF9, 0x8B, 0x6A, 0xAE, 0xD8,
+ 0x64, 0x83, 0xC1, 0xD3, 0x04, 0xF4, 0xFA, 0xC3,
+ 0x46, 0x2C, 0xA8, 0xBB, 0x3A, 0x47, 0x33, 0x8F,
+ 0x52, 0x86, 0x08, 0x9D, 0x1D, 0x59, 0x8E, 0x91,
+ 0x32, 0xCF, 0x6B, 0x75, 0xB0, 0x7F, 0xC7, 0x24,
+ 0x05, 0x6F, 0x00, 0x1C, 0x2D, 0xAC, 0xDA, 0x45,
+ 0x73, 0xB3, 0x3E, 0xD6, 0x54, 0x61, 0x03, 0x77,
+ 0xF8, 0xD9, 0xE2, 0x4B, 0xFF, 0xF2, 0x0C, 0x4F,
+ 0x93, 0x71, 0xA7, 0x3D, 0x66, 0x88, 0x98, 0xF1,
+ 0xB6, 0x7A, 0x2B, 0xCD, 0x44, 0x3C, 0x37, 0x5A,
+ 0x96, 0x23, 0x9F, 0xBF, 0x7D, 0x5E, 0x2A, 0x35,
+ 0x72, 0x79, 0xE1, 0xA3, 0x84, 0x99, 0x38, 0x49,
+ 0xC8, 0xDB, 0x30, 0xDC, 0xAD, 0x3F, 0xF6, 0x09,
+ 0x69, 0x95, 0xE5, 0x67, 0xA1, 0xFD, 0xF7, 0x1B,
+ 0xEC, 0x17, 0xD4, 0xEB, 0x29, 0x36, 0x3B, 0x15,
+ 0xDE, 0x2E, 0xC5, 0x70, 0x6D, 0x53, 0x56, 0xAB,
+ 0xC0, 0x43, 0xC2, 0xE7, 0x31, 0xE6, 0xA6, 0x78,
+ 0x5C, 0x7C, 0x48, 0x10, 0x87, 0xCC, 0x9E, 0x7E,
+ 0x5F, 0xE9, 0x07, 0x5B, 0xF5, 0xEE, 0xB5, 0xCA,
+ 0x62, 0x18, 0xBE, 0x20, 0x16, 0xDF, 0x13, 0x4E,
+ 0x7B, 0x02, 0x11, 0x4C, 0x51, 0x85, 0x0D, 0x22,
+ 0xF3, 0x14, 0x63, 0x76, 0xD0, 0x0F, 0xE4, 0xCB,
+ 0xCE, 0xA0, 0x82, 0xE3, 0x01, 0xAA, 0x5D, 0x4A,
+ 0x4D, 0xFB, 0x39, 0x8A, 0x2F, 0xDD, 0xE8, 0x06,
+ 0x1A, 0x90, 0x81, 0x50, 0x8D, 0x89, 0x97, 0x1E,
+ 0xFC, 0x60, 0x12, 0x42, 0x9C, 0xF0, 0x34, 0xD7,
+ 0xD1, 0x1F, 0x0A, 0x21, 0xA9, 0x6E, 0xC4, 0xBA,
+ 0x9A, 0x57, 0xA2, 0x74, 0xC6, 0xFE, 0x9B, 0x58 };
+
+unsigned char table_159[256] = {
+ 0xE5, 0xBF, 0x84, 0x56, 0xD6, 0x43, 0x3E, 0xA5,
+ 0x64, 0x87, 0x44, 0x63, 0x4A, 0x4C, 0x8D, 0x24,
+ 0x1C, 0xDA, 0x89, 0x52, 0x80, 0x4F, 0xE4, 0xBC,
+ 0xC5, 0xF4, 0x27, 0x75, 0x9C, 0xF0, 0xE1, 0x06,
+ 0x99, 0x48, 0xF2, 0x57, 0x34, 0x9A, 0xA8, 0x62,
+ 0xC9, 0xD5, 0x16, 0x6D, 0x55, 0xFA, 0x37, 0x5A,
+ 0x2A, 0xC6, 0x45, 0xDD, 0x1B, 0x76, 0x50, 0xE2,
+ 0x69, 0x41, 0x6C, 0xC4, 0x3C, 0x47, 0xA9, 0x92,
+ 0x00, 0x3D, 0x6F, 0xE7, 0x7A, 0x3A, 0x33, 0x53,
+ 0xF7, 0x03, 0xA7, 0xB1, 0x15, 0x78, 0x0B, 0x67,
+ 0x2E, 0x21, 0xF1, 0xD4, 0xB3, 0x98, 0x60, 0x58,
+ 0xBB, 0x82, 0x1E, 0x70, 0x0A, 0xA2, 0x02, 0x17,
+ 0xFF, 0x9F, 0xD2, 0xAF, 0xC7, 0xDC, 0x68, 0x83,
+ 0x42, 0xCA, 0x08, 0x39, 0x20, 0xEC, 0x77, 0x96,
+ 0x5B, 0xAD, 0x09, 0x6B, 0x40, 0xC2, 0x91, 0x51,
+ 0x10, 0xD9, 0xF9, 0xC1, 0xB5, 0xDF, 0xDB, 0xC0,
+ 0x7D, 0xAB, 0xAE, 0x54, 0x35, 0xF3, 0xA1, 0xE6,
+ 0xEA, 0x14, 0xBA, 0xFC, 0xE8, 0xEB, 0xF6, 0xBD,
+ 0x8C, 0x72, 0x1F, 0xE9, 0xFB, 0x7C, 0xCF, 0x49,
+ 0xE3, 0xA3, 0x22, 0x9D, 0x46, 0x71, 0x94, 0x31,
+ 0x2D, 0x65, 0x2B, 0x32, 0x18, 0xB6, 0x90, 0xF8,
+ 0x11, 0x5F, 0xA0, 0xEF, 0xED, 0x1A, 0x25, 0x2C,
+ 0x3B, 0xFD, 0x2F, 0x73, 0xB9, 0x7E, 0xDE, 0xB4,
+ 0x97, 0x0F, 0x7F, 0x86, 0x93, 0x07, 0x19, 0xCE,
+ 0xE0, 0xB7, 0xEE, 0x26, 0xD1, 0x01, 0x59, 0x5C,
+ 0xC3, 0x79, 0x8B, 0xD3, 0x4B, 0x04, 0xD0, 0x29,
+ 0x0D, 0x3F, 0xB2, 0x30, 0xCC, 0x36, 0xFE, 0xB0,
+ 0xF5, 0x8E, 0xA6, 0x8A, 0xC8, 0xD8, 0x05, 0xB8,
+ 0x12, 0xBE, 0x81, 0x4D, 0x38, 0xAC, 0x1D, 0x9E,
+ 0x66, 0x5E, 0x7B, 0x6E, 0x0C, 0xCD, 0x6A, 0x88,
+ 0xAA, 0x0E, 0x61, 0x5D, 0x95, 0x4E, 0xD7, 0x74,
+ 0xCB, 0x9B, 0x13, 0x8F, 0xA4, 0x28, 0x23, 0x85 };
+
+unsigned char table_160[256] = {
+ 0x35, 0x44, 0x0E, 0x92, 0x75, 0x83, 0x9D, 0x53,
+ 0xA5, 0x90, 0xF8, 0xF7, 0x54, 0x74, 0xDF, 0x3D,
+ 0x5A, 0xAA, 0xC6, 0x26, 0x7A, 0xFC, 0x79, 0x6C,
+ 0x56, 0xB3, 0x32, 0xE3, 0x1C, 0xF9, 0xDC, 0xE6,
+ 0xA2, 0x93, 0x71, 0xFF, 0x1D, 0xEB, 0xB2, 0x04,
+ 0x96, 0x46, 0x0C, 0x2B, 0x17, 0xEE, 0x28, 0x25,
+ 0xD9, 0xAE, 0x11, 0xA7, 0x40, 0x45, 0xFB, 0x80,
+ 0x18, 0xF1, 0xCB, 0x2E, 0x24, 0xF3, 0xEC, 0x4F,
+ 0xAB, 0xD7, 0xD4, 0xC4, 0xFD, 0x4B, 0xAD, 0xC9,
+ 0x4C, 0x08, 0xAC, 0xF4, 0xCD, 0xB7, 0xF2, 0x15,
+ 0x02, 0x2F, 0x16, 0x34, 0x65, 0x8A, 0x87, 0xCC,
+ 0x50, 0x0F, 0x9B, 0xC2, 0xC8, 0x7B, 0xEA, 0x8E,
+ 0xE4, 0xD6, 0x97, 0x30, 0xA8, 0xA0, 0x94, 0xC5,
+ 0xE8, 0x12, 0x27, 0xCE, 0x84, 0xDD, 0xB1, 0x47,
+ 0x7E, 0xE7, 0xE1, 0x3A, 0x37, 0x21, 0x2D, 0x3B,
+ 0x20, 0x60, 0x1E, 0x1B, 0x82, 0xBE, 0xA3, 0x70,
+ 0x98, 0xBF, 0xA6, 0x4D, 0x76, 0x86, 0x42, 0x9F,
+ 0xCF, 0xE0, 0x14, 0x4A, 0x0B, 0xB4, 0x36, 0xF5,
+ 0x85, 0xB8, 0xC0, 0x6A, 0xE9, 0x7D, 0xBD, 0x4E,
+ 0x8F, 0x51, 0x0D, 0x5B, 0x6B, 0x58, 0x5F, 0x03,
+ 0x6F, 0xBC, 0x5D, 0x1F, 0x7F, 0xDB, 0x00, 0xC1,
+ 0x13, 0xF0, 0xD1, 0xFA, 0xDA, 0x05, 0x39, 0xD3,
+ 0x38, 0xD2, 0x89, 0xE2, 0x88, 0x5E, 0x5C, 0x6D,
+ 0xCA, 0xB0, 0x01, 0x63, 0x8B, 0x59, 0xA4, 0xD0,
+ 0x78, 0x19, 0xB5, 0x62, 0x1A, 0x69, 0x8D, 0x9C,
+ 0x22, 0x3F, 0x9E, 0x33, 0x72, 0x2A, 0x41, 0x29,
+ 0xFE, 0xF6, 0x64, 0x7C, 0x66, 0xB6, 0xAF, 0x23,
+ 0x8C, 0x68, 0x6E, 0x49, 0x07, 0x99, 0x77, 0x3E,
+ 0x9A, 0x73, 0xD8, 0x55, 0x0A, 0x3C, 0xBA, 0xA9,
+ 0x52, 0xED, 0x91, 0x09, 0x95, 0xC7, 0x43, 0xD5,
+ 0x57, 0x61, 0x81, 0xEF, 0x06, 0xDE, 0x48, 0x31,
+ 0xBB, 0x2C, 0xE5, 0xC3, 0x67, 0xA1, 0x10, 0xB9 };
+
+unsigned char table_161[256] = {
+ 0x8F, 0x1A, 0x81, 0xA2, 0x2C, 0x56, 0x6D, 0xCD,
+ 0x4A, 0x33, 0x50, 0xE9, 0xE0, 0x12, 0x5A, 0x43,
+ 0x2D, 0x4F, 0xEA, 0x95, 0xFD, 0x49, 0xAB, 0xA3,
+ 0x79, 0x42, 0x0B, 0xB8, 0x89, 0x40, 0x71, 0x14,
+ 0x80, 0x55, 0xAF, 0xCF, 0x3E, 0x64, 0x8B, 0x74,
+ 0xBF, 0x9C, 0x24, 0x97, 0xD1, 0xBA, 0x48, 0xD2,
+ 0x08, 0x1F, 0xDD, 0xA7, 0xDC, 0x92, 0x30, 0x75,
+ 0x31, 0x37, 0x67, 0x06, 0x68, 0x72, 0x6F, 0x05,
+ 0x8A, 0x7C, 0x4C, 0x3C, 0x19, 0x28, 0x86, 0x3D,
+ 0x93, 0xDA, 0xF4, 0xC7, 0x17, 0x85, 0xAC, 0x02,
+ 0x78, 0x04, 0xAD, 0x03, 0x8D, 0x11, 0xC5, 0x9D,
+ 0x3A, 0x73, 0x82, 0x59, 0x51, 0x9F, 0x27, 0x47,
+ 0xE7, 0xED, 0x1E, 0xFF, 0x34, 0x01, 0x5B, 0x4B,
+ 0xCA, 0x6C, 0x69, 0xBB, 0x3B, 0xC4, 0x5F, 0xDF,
+ 0x09, 0x6B, 0x7D, 0xC9, 0x88, 0x45, 0x57, 0xD3,
+ 0x2A, 0x4E, 0xF1, 0xC2, 0xA9, 0xB6, 0x18, 0xD4,
+ 0xA0, 0x1C, 0x4D, 0x0E, 0xE5, 0xE1, 0xD7, 0xB2,
+ 0x0C, 0x3F, 0x00, 0x61, 0x16, 0x0D, 0x32, 0x62,
+ 0x58, 0x63, 0xEE, 0xEF, 0x2F, 0x5D, 0xB0, 0x20,
+ 0x7A, 0x10, 0xE6, 0xA1, 0xF9, 0xD8, 0x6E, 0xCB,
+ 0xF0, 0x9B, 0x84, 0x8E, 0xF2, 0xFE, 0xC8, 0x7F,
+ 0xBD, 0xF8, 0x07, 0xC6, 0x39, 0xBC, 0xCC, 0x22,
+ 0x54, 0x15, 0x9A, 0xA4, 0xC1, 0x2B, 0x1B, 0x25,
+ 0xDE, 0x6A, 0xDB, 0x90, 0xEB, 0xB7, 0xD0, 0x44,
+ 0xA6, 0xB9, 0xB1, 0x23, 0x9E, 0x65, 0x83, 0xFA,
+ 0x96, 0xB5, 0x0F, 0xF6, 0xD6, 0xE8, 0x53, 0x13,
+ 0x76, 0xD5, 0x35, 0x87, 0xE3, 0x38, 0xF5, 0xAE,
+ 0xB3, 0xCE, 0xE2, 0x70, 0xD9, 0x66, 0x5C, 0x26,
+ 0xC3, 0xFC, 0xF7, 0x94, 0xF3, 0xEC, 0xFB, 0x99,
+ 0x91, 0x77, 0xB4, 0x46, 0xA5, 0x98, 0x7B, 0x1D,
+ 0x52, 0x2E, 0xA8, 0x60, 0x5E, 0x29, 0x21, 0x7E,
+ 0xBE, 0x0A, 0x36, 0x41, 0xC0, 0x8C, 0xE4, 0xAA };
+
+unsigned char table_162[256] = {
+ 0xF7, 0x1B, 0xC0, 0x31, 0x5A, 0x23, 0xEA, 0xE9,
+ 0xFB, 0x14, 0x6A, 0xE8, 0x04, 0x65, 0x5B, 0x2C,
+ 0x41, 0xD9, 0xEB, 0xE4, 0x8D, 0x1D, 0xCA, 0x8F,
+ 0x5E, 0x43, 0xAF, 0x46, 0x0A, 0x01, 0x0C, 0xB4,
+ 0x95, 0x52, 0x92, 0xE0, 0x10, 0x57, 0x0F, 0x71,
+ 0xB1, 0x26, 0xD8, 0x05, 0x69, 0x3C, 0x54, 0xDF,
+ 0xFF, 0x9D, 0x51, 0xA0, 0xA1, 0x0B, 0xC1, 0x20,
+ 0x6D, 0xFA, 0x47, 0x15, 0x09, 0xD3, 0xE1, 0xA9,
+ 0x66, 0x12, 0x5C, 0x49, 0x1E, 0x3B, 0xD0, 0x8B,
+ 0x62, 0xBD, 0x06, 0xE5, 0x00, 0x98, 0x4E, 0x32,
+ 0xB0, 0x2D, 0x2A, 0x7F, 0x03, 0xD5, 0x99, 0x7E,
+ 0xAB, 0x22, 0xC6, 0xC3, 0x2F, 0x4C, 0x33, 0x45,
+ 0xE3, 0x3F, 0xF9, 0xB2, 0xFE, 0x36, 0xE7, 0xF8,
+ 0x55, 0x0D, 0x56, 0x1F, 0x4B, 0xE6, 0x50, 0x81,
+ 0xCE, 0x80, 0xCD, 0x67, 0x6B, 0xCF, 0x2E, 0x9B,
+ 0xBC, 0xBE, 0x11, 0x75, 0x4D, 0xAC, 0x59, 0x40,
+ 0x85, 0x0E, 0xC9, 0x17, 0xA3, 0x60, 0xED, 0x16,
+ 0xA4, 0xDD, 0xEE, 0x96, 0x77, 0x83, 0x34, 0xD2,
+ 0xCB, 0xFC, 0x6C, 0x08, 0xEC, 0x35, 0xF2, 0x6F,
+ 0x3A, 0x7B, 0x21, 0x4A, 0x70, 0xEF, 0xAD, 0xDE,
+ 0x90, 0x9E, 0x7D, 0x64, 0x2B, 0x79, 0xF5, 0xF3,
+ 0x13, 0x1C, 0x7A, 0x07, 0x4F, 0x78, 0x89, 0xB6,
+ 0x97, 0xF1, 0xD7, 0x7C, 0x48, 0xAE, 0x39, 0xA8,
+ 0xA6, 0x86, 0x3E, 0x27, 0x87, 0x73, 0x82, 0x24,
+ 0x30, 0x74, 0x5F, 0xD1, 0x9F, 0x9C, 0x1A, 0x8C,
+ 0x42, 0x6E, 0x28, 0xB9, 0xF0, 0xC4, 0x68, 0x25,
+ 0xC5, 0xDC, 0xB8, 0x29, 0xD6, 0x84, 0x3D, 0xBB,
+ 0x88, 0x76, 0xFD, 0x61, 0x94, 0x91, 0xDA, 0xB7,
+ 0x72, 0xBA, 0xC2, 0xDB, 0xB5, 0xA5, 0xE2, 0x18,
+ 0xF6, 0xAA, 0x8A, 0x19, 0x63, 0x9A, 0xA7, 0xC8,
+ 0xD4, 0x02, 0x8E, 0x37, 0xF4, 0xB3, 0xA2, 0x53,
+ 0x38, 0xCC, 0x58, 0x44, 0xBF, 0x93, 0x5D, 0xC7 };
+
+unsigned char table_163[32] = {
+ 0x1B, 0x14, 0x12, 0x15, 0x11, 0x1D, 0x17, 0x19,
+ 0x10, 0x09, 0x08, 0x06, 0x1A, 0x16, 0x07, 0x13,
+ 0x1F, 0x0B, 0x1C, 0x05, 0x0E, 0x00, 0x18, 0x0A,
+ 0x04, 0x01, 0x03, 0x0C, 0x0D, 0x1E, 0x02, 0x0F };
+
+unsigned char table_164[32] = {
+ 0x15, 0x00, 0x10, 0x0B, 0x1D, 0x0A, 0x06, 0x1C,
+ 0x0D, 0x1F, 0x17, 0x0F, 0x03, 0x14, 0x13, 0x12,
+ 0x1B, 0x18, 0x08, 0x1E, 0x16, 0x09, 0x1A, 0x04,
+ 0x02, 0x0C, 0x0E, 0x01, 0x07, 0x19, 0x11, 0x05 };
+
+unsigned char table_165[256] = {
+ 0x98, 0xF5, 0x1D, 0xFB, 0x13, 0x20, 0x41, 0xA3,
+ 0xE3, 0x76, 0x49, 0x7E, 0x60, 0xD8, 0x68, 0x30,
+ 0x88, 0x45, 0xD5, 0x77, 0x00, 0xC3, 0x09, 0x31,
+ 0x44, 0x18, 0xD4, 0x14, 0xC8, 0x1B, 0x8B, 0x38,
+ 0x08, 0x52, 0xD1, 0xF3, 0x69, 0x9F, 0xDA, 0x61,
+ 0x16, 0x1C, 0xE4, 0x7D, 0xEE, 0xD9, 0x5E, 0x4C,
+ 0xA7, 0xAA, 0xA6, 0xF6, 0xCF, 0xA0, 0xBA, 0x10,
+ 0xE2, 0xDE, 0x0F, 0xEA, 0xBC, 0x32, 0x63, 0xC0,
+ 0x54, 0xC5, 0xBE, 0x71, 0x80, 0x56, 0x5C, 0xA4,
+ 0xAD, 0x15, 0x9D, 0x11, 0x43, 0x67, 0x95, 0xAE,
+ 0xC6, 0xC4, 0x91, 0x9C, 0xE5, 0x37, 0xE1, 0x7A,
+ 0xDB, 0xEF, 0x03, 0x65, 0x86, 0x66, 0x2A, 0xB5,
+ 0xBF, 0xB4, 0x0D, 0xB3, 0xD7, 0x2D, 0x01, 0xEB,
+ 0x8C, 0xF2, 0x5A, 0x2E, 0x64, 0x25, 0x02, 0xCB,
+ 0x4A, 0xB0, 0xCE, 0x35, 0xA8, 0x47, 0x85, 0x33,
+ 0x34, 0x24, 0x23, 0x7B, 0xB6, 0x48, 0x83, 0x40,
+ 0x87, 0x57, 0x3C, 0xD6, 0xCD, 0x2C, 0x6D, 0xE7,
+ 0xBB, 0xED, 0x81, 0x5D, 0x55, 0x46, 0xDD, 0xD3,
+ 0x70, 0xBD, 0xB8, 0x75, 0x53, 0x6E, 0xD0, 0x99,
+ 0xCA, 0x58, 0xC7, 0x4B, 0x3D, 0xA5, 0x50, 0x7C,
+ 0x93, 0x51, 0xB7, 0xFD, 0x05, 0x3A, 0xE8, 0x8F,
+ 0x28, 0x74, 0x39, 0xF0, 0x7F, 0x4F, 0x06, 0x36,
+ 0xB2, 0x19, 0x2F, 0x1F, 0x8D, 0x0C, 0xB9, 0xFC,
+ 0x89, 0x21, 0x12, 0xF7, 0x3F, 0x94, 0x6F, 0xDC,
+ 0x3E, 0x4E, 0x3B, 0xC9, 0x07, 0x9B, 0x17, 0x9A,
+ 0x73, 0x6A, 0x5B, 0xA1, 0x1E, 0x8A, 0x04, 0x72,
+ 0x6C, 0xA2, 0xEC, 0x96, 0xFE, 0xF8, 0x84, 0xC1,
+ 0x79, 0x0E, 0x62, 0x90, 0x8E, 0xF4, 0x42, 0x29,
+ 0x92, 0x9E, 0xAC, 0x82, 0x4D, 0xAF, 0x2B, 0x6B,
+ 0xA9, 0xFF, 0x0A, 0xAB, 0x22, 0x5F, 0xDF, 0xD2,
+ 0x0B, 0x78, 0xF1, 0xE6, 0x59, 0x27, 0xC2, 0xE0,
+ 0x1A, 0x26, 0xCC, 0xB1, 0xF9, 0xFA, 0x97, 0xE9 };
+
+unsigned char table_166[256] = {
+ 0xCB, 0xEA, 0x2A, 0x36, 0x6D, 0x93, 0x4E, 0xD5,
+ 0xBC, 0x6A, 0xD4, 0x68, 0xF7, 0x18, 0xAB, 0x8B,
+ 0x66, 0x95, 0x94, 0x64, 0xB7, 0x00, 0x4D, 0x97,
+ 0x38, 0xB3, 0xFC, 0xE1, 0xBB, 0x63, 0xF3, 0x1F,
+ 0x6B, 0x2C, 0x2F, 0x5E, 0xA4, 0x7E, 0xFB, 0xF4,
+ 0xA8, 0x8A, 0x65, 0x53, 0x90, 0x58, 0x40, 0x60,
+ 0x28, 0x8E, 0x35, 0x49, 0xED, 0xBD, 0x1B, 0x0B,
+ 0xBA, 0xB8, 0x61, 0x50, 0xE9, 0x39, 0xEF, 0xC3,
+ 0x74, 0xB6, 0x46, 0x8D, 0xD9, 0x32, 0x92, 0x9A,
+ 0x30, 0x01, 0xF2, 0x41, 0xB9, 0xE7, 0x3A, 0xB0,
+ 0x80, 0x15, 0xDE, 0x7D, 0x7F, 0x09, 0xC2, 0x76,
+ 0xF8, 0x12, 0x59, 0xDD, 0x1D, 0xE6, 0x75, 0xBE,
+ 0xA3, 0x04, 0xCA, 0x78, 0x7B, 0xAC, 0xD8, 0x70,
+ 0xD3, 0xC1, 0x25, 0x6F, 0x03, 0x6C, 0x14, 0x45,
+ 0xE5, 0x2B, 0x87, 0x83, 0xAA, 0x77, 0x5F, 0x4A,
+ 0x9C, 0x27, 0x0C, 0x10, 0xAE, 0x56, 0x85, 0x0D,
+ 0xE3, 0xFA, 0x71, 0xEE, 0x9F, 0x21, 0xC0, 0xCD,
+ 0xFD, 0xDC, 0x5B, 0x11, 0x02, 0x0F, 0x96, 0x3D,
+ 0x3C, 0x26, 0xEB, 0x08, 0x7A, 0x82, 0xA7, 0x19,
+ 0xD7, 0xC5, 0xF6, 0x52, 0x57, 0x88, 0xFF, 0x47,
+ 0x8F, 0xC6, 0x33, 0xB5, 0x2E, 0x8C, 0x81, 0x91,
+ 0x44, 0xA6, 0x17, 0xF0, 0x4B, 0x9D, 0x34, 0x73,
+ 0x72, 0x67, 0xD2, 0x0E, 0xA0, 0x99, 0xA5, 0xAF,
+ 0xFE, 0x9E, 0x6E, 0xDA, 0x3B, 0xE2, 0x23, 0xD6,
+ 0xD0, 0x13, 0x89, 0x5A, 0x42, 0x98, 0x5C, 0xD1,
+ 0x86, 0x24, 0xDF, 0x37, 0xF9, 0xCC, 0xF5, 0xA9,
+ 0x2D, 0xBF, 0x5D, 0xF1, 0x69, 0xE8, 0xA2, 0x06,
+ 0x48, 0xC7, 0xDB, 0x29, 0xE4, 0xAD, 0x3E, 0xA1,
+ 0xC9, 0x4C, 0x1A, 0xCE, 0x62, 0x4F, 0x7C, 0xC8,
+ 0x05, 0xC4, 0xB1, 0x1E, 0x79, 0x55, 0x84, 0xB2,
+ 0x20, 0x31, 0x9B, 0xEC, 0xB4, 0xCF, 0x54, 0x22,
+ 0x1C, 0xE0, 0x51, 0x16, 0x43, 0x07, 0x0A, 0x3F };
+
+unsigned char table_167[256] = {
+ 0x91, 0xEA, 0x4F, 0x6A, 0x6E, 0x2D, 0x27, 0x22,
+ 0x44, 0xA5, 0x6D, 0xE3, 0x45, 0x06, 0xE2, 0x87,
+ 0x9A, 0xC9, 0x2C, 0x4A, 0x93, 0x6F, 0x00, 0xEB,
+ 0x7C, 0x7F, 0xA2, 0xFE, 0x40, 0x3C, 0x3F, 0xC0,
+ 0xC7, 0xFB, 0x8B, 0xDF, 0xA3, 0x28, 0x78, 0x48,
+ 0x46, 0xD5, 0x70, 0x5C, 0x35, 0x4E, 0xD7, 0x3A,
+ 0x42, 0x47, 0x5B, 0x26, 0x8E, 0xE0, 0x21, 0xB1,
+ 0x77, 0x1E, 0x53, 0x4B, 0xCC, 0xE5, 0x65, 0xF6,
+ 0x66, 0x2A, 0xA0, 0x5E, 0x3E, 0xAD, 0xA8, 0x95,
+ 0x1B, 0x0D, 0x8A, 0x05, 0x68, 0x59, 0x0C, 0x38,
+ 0x18, 0xC3, 0x81, 0xA4, 0xFD, 0x13, 0x50, 0xCA,
+ 0xE8, 0xDD, 0xD9, 0x76, 0x8C, 0xC5, 0xF4, 0x17,
+ 0xB4, 0x3D, 0xEC, 0x0B, 0x67, 0xC6, 0x8D, 0xE1,
+ 0xBB, 0x7E, 0xCB, 0x10, 0x99, 0xE9, 0x39, 0xF3,
+ 0x75, 0xFA, 0xAC, 0x16, 0x54, 0x51, 0xBC, 0x24,
+ 0x58, 0x08, 0xA7, 0x0F, 0x5D, 0xBF, 0xBA, 0xE7,
+ 0x9D, 0x2B, 0xB5, 0x29, 0xE4, 0xCD, 0x37, 0x30,
+ 0x55, 0xAE, 0x1D, 0x4D, 0x94, 0x34, 0x92, 0x1C,
+ 0x6B, 0xBE, 0x52, 0x7B, 0x33, 0xB0, 0x0A, 0x5A,
+ 0x03, 0x23, 0x41, 0x49, 0x61, 0x64, 0x73, 0x97,
+ 0xC2, 0x9F, 0x5F, 0x07, 0x04, 0xF8, 0xC1, 0xFC,
+ 0x74, 0x02, 0x0E, 0x60, 0x9E, 0xD4, 0x85, 0x88,
+ 0xC4, 0xF5, 0x90, 0x31, 0xF7, 0xEE, 0x9B, 0xB9,
+ 0x20, 0xE6, 0xA6, 0x63, 0x79, 0x56, 0x62, 0xF0,
+ 0x2F, 0xD8, 0x4C, 0x83, 0xF9, 0x36, 0x3B, 0x84,
+ 0xDE, 0x57, 0xB8, 0xB7, 0x11, 0xF2, 0xC8, 0xD3,
+ 0xD1, 0x96, 0x19, 0x2E, 0x72, 0x9C, 0xDB, 0xB3,
+ 0xA1, 0xAA, 0xCE, 0x09, 0x98, 0xED, 0xA9, 0xDA,
+ 0xAF, 0x86, 0xD0, 0x12, 0xFF, 0xDC, 0x1F, 0xD6,
+ 0x01, 0xF1, 0xD2, 0x80, 0x43, 0x7A, 0x71, 0x82,
+ 0xB6, 0xAB, 0x89, 0xBD, 0x8F, 0xEF, 0x7D, 0xB2,
+ 0x14, 0x15, 0x25, 0x32, 0x6C, 0x69, 0x1A, 0xCF };
+
+unsigned char table_168[256] = {
+ 0x28, 0xEE, 0xB1, 0xFD, 0xB3, 0xEF, 0x36, 0x8E,
+ 0x85, 0x5D, 0x1C, 0x53, 0x1E, 0xDA, 0xBA, 0x3C,
+ 0xA8, 0x90, 0x99, 0x49, 0x45, 0xE0, 0x27, 0x8D,
+ 0x22, 0xE4, 0x51, 0x3E, 0xAB, 0xE8, 0x70, 0xF5,
+ 0x81, 0xE6, 0x34, 0x29, 0xF3, 0x11, 0x46, 0x5F,
+ 0x5C, 0xA0, 0xD1, 0xE3, 0x15, 0x68, 0x3A, 0x01,
+ 0xE9, 0xD7, 0x24, 0x5A, 0x18, 0x16, 0x88, 0x3B,
+ 0x64, 0xA1, 0xDB, 0xBF, 0xAA, 0x43, 0xEA, 0x19,
+ 0xA2, 0xD5, 0x7B, 0xBD, 0x2A, 0x0E, 0x4F, 0xB5,
+ 0x4B, 0xB7, 0x5B, 0x73, 0xC9, 0xAC, 0x1B, 0x67,
+ 0xC7, 0xB4, 0x69, 0x00, 0xBC, 0x6D, 0xC1, 0x04,
+ 0xF4, 0x74, 0xD6, 0xD0, 0x60, 0xAE, 0x17, 0xFE,
+ 0x63, 0xB6, 0x89, 0x41, 0x7C, 0x44, 0x8B, 0xDC,
+ 0x50, 0xE5, 0x79, 0x77, 0x47, 0x9F, 0xA6, 0x3D,
+ 0x09, 0x8A, 0x2F, 0xC0, 0x0F, 0xCD, 0x2B, 0x4D,
+ 0x0D, 0xC2, 0x5E, 0xB0, 0x57, 0x62, 0xAF, 0x1A,
+ 0x21, 0x82, 0x48, 0x9E, 0x38, 0xB9, 0xB8, 0xF2,
+ 0x37, 0x07, 0xCA, 0xC5, 0x84, 0xDF, 0xF9, 0xEC,
+ 0x42, 0x6B, 0x8F, 0x6C, 0x3F, 0xC4, 0x94, 0xED,
+ 0x7A, 0x2D, 0xA3, 0x83, 0xD9, 0x55, 0x02, 0x9A,
+ 0xA9, 0x75, 0x10, 0x2C, 0xCB, 0x95, 0xBB, 0x6E,
+ 0x23, 0x65, 0x35, 0x97, 0x56, 0xAD, 0xCE, 0xF8,
+ 0xF0, 0x0C, 0xE2, 0x52, 0x05, 0x91, 0xCC, 0xC8,
+ 0x78, 0x06, 0x96, 0x4E, 0x03, 0xD3, 0x98, 0xA7,
+ 0x13, 0x58, 0x93, 0xD4, 0xDD, 0xC6, 0xFC, 0x25,
+ 0x9C, 0x86, 0x1F, 0xCF, 0x76, 0xA4, 0x6A, 0xFA,
+ 0x0B, 0x4A, 0x54, 0x40, 0x59, 0xD8, 0x61, 0xFF,
+ 0x7F, 0x80, 0x6F, 0x7D, 0xF1, 0x8C, 0x92, 0xDE,
+ 0x9D, 0xC3, 0xB2, 0xE7, 0xFB, 0x20, 0x31, 0x72,
+ 0x12, 0xBE, 0x1D, 0xF6, 0x9B, 0x14, 0x26, 0x0A,
+ 0xEB, 0xF7, 0x71, 0x39, 0x30, 0xA5, 0x87, 0xD2,
+ 0x66, 0x2E, 0x08, 0x32, 0x4C, 0x33, 0x7E, 0xE1 };
+
+unsigned char table_169[256] = {
+ 0xA4, 0x31, 0xA9, 0x3F, 0x13, 0x4D, 0x1B, 0x29,
+ 0x73, 0x43, 0xF1, 0xE7, 0x9C, 0xC2, 0xF6, 0xCD,
+ 0xA1, 0x94, 0x0D, 0x27, 0xFE, 0x7B, 0x9B, 0x0B,
+ 0x89, 0xBA, 0x23, 0xEC, 0x76, 0xC3, 0x6C, 0xD8,
+ 0x8D, 0xF8, 0xF9, 0x7D, 0x68, 0x5B, 0x61, 0x87,
+ 0x28, 0x14, 0x55, 0x0C, 0xFC, 0xD9, 0x07, 0xE8,
+ 0x36, 0x88, 0x67, 0x4C, 0xEA, 0xBD, 0xF5, 0x9D,
+ 0xB6, 0xC6, 0x24, 0x32, 0x93, 0x03, 0x79, 0x8C,
+ 0x12, 0x84, 0xFF, 0x7E, 0x42, 0xE4, 0x3C, 0xF2,
+ 0x50, 0xEB, 0x1F, 0x47, 0xB0, 0xA5, 0xB1, 0x71,
+ 0x30, 0x5F, 0x5C, 0x53, 0xF7, 0x10, 0xC5, 0x6E,
+ 0xE0, 0xDE, 0xC8, 0x58, 0xB7, 0x90, 0xA6, 0x95,
+ 0x70, 0x8F, 0xFD, 0xC1, 0x48, 0xB5, 0x19, 0x92,
+ 0xBC, 0x15, 0x4E, 0xE6, 0x11, 0xDD, 0x81, 0x0E,
+ 0xBB, 0x75, 0x5D, 0x4A, 0xAB, 0x2D, 0x02, 0x54,
+ 0x4B, 0x66, 0xD6, 0x2B, 0x2A, 0xE5, 0x26, 0xE1,
+ 0xEE, 0xE9, 0x8B, 0x6A, 0x7A, 0xF4, 0x51, 0x39,
+ 0x1C, 0xC9, 0xCF, 0x77, 0x00, 0xF3, 0x25, 0xCC,
+ 0x08, 0xFB, 0x0F, 0x3E, 0xCE, 0xED, 0x3D, 0x56,
+ 0xEF, 0x1D, 0x85, 0x96, 0x52, 0xA8, 0xD3, 0xCB,
+ 0xE3, 0x33, 0x06, 0x7C, 0xAE, 0x72, 0x09, 0x04,
+ 0x91, 0xC4, 0x5A, 0x69, 0x98, 0xB4, 0x40, 0xDF,
+ 0x7F, 0x9F, 0xAA, 0x83, 0xE2, 0x78, 0x74, 0x20,
+ 0xAD, 0x6D, 0xDC, 0xD4, 0xCA, 0x60, 0xF0, 0x35,
+ 0x37, 0xD0, 0x18, 0x1A, 0x64, 0x3A, 0x99, 0xDB,
+ 0x62, 0x44, 0x2C, 0x82, 0x8E, 0xD7, 0xD1, 0xFA,
+ 0x16, 0xD5, 0x46, 0xBF, 0xA7, 0xC0, 0x2E, 0x3B,
+ 0x01, 0x63, 0xB2, 0x1E, 0x05, 0x21, 0xB8, 0x17,
+ 0x22, 0x97, 0xAF, 0x4F, 0x86, 0x34, 0xDA, 0xC7,
+ 0xA3, 0xA0, 0xB3, 0x2F, 0xAC, 0x49, 0xD2, 0x57,
+ 0x6F, 0x9A, 0x65, 0xB9, 0x41, 0xBE, 0x8A, 0xA2,
+ 0x6B, 0x0A, 0x59, 0x9E, 0x5E, 0x38, 0x45, 0x80 };
+
+unsigned char table_170[256] = {
+ 0xE3, 0x00, 0x99, 0x03, 0xF6, 0xDD, 0xD1, 0x41,
+ 0x58, 0x7E, 0xD9, 0x46, 0x04, 0xAF, 0x5C, 0x43,
+ 0xDE, 0x5E, 0xFC, 0x97, 0x3D, 0x68, 0xC8, 0x37,
+ 0x3C, 0xFB, 0x0F, 0x5A, 0xBE, 0xFA, 0x4C, 0x82,
+ 0x0C, 0xA0, 0x0A, 0xD4, 0x9D, 0xCE, 0x78, 0xA8,
+ 0x55, 0x56, 0x60, 0xAA, 0xC9, 0x96, 0x62, 0xEA,
+ 0x0D, 0xB8, 0xE2, 0x84, 0x17, 0xAE, 0x2B, 0x2C,
+ 0x91, 0x57, 0x38, 0x01, 0xA9, 0xCD, 0x34, 0xBA,
+ 0x8D, 0xC0, 0xD6, 0xFF, 0xF2, 0xD3, 0x5F, 0x26,
+ 0xCA, 0x9B, 0x21, 0x75, 0x4E, 0x49, 0x20, 0x59,
+ 0x39, 0xBF, 0x90, 0x6C, 0xFE, 0x8F, 0x2F, 0x18,
+ 0x36, 0xD7, 0xB4, 0xAC, 0xBD, 0xF3, 0x1D, 0x4F,
+ 0xA3, 0x74, 0x5B, 0x44, 0x05, 0x9C, 0x6D, 0x6B,
+ 0x1E, 0xE8, 0x25, 0x16, 0x80, 0xCC, 0x29, 0xC7,
+ 0x94, 0x4A, 0xF5, 0xF4, 0x27, 0x85, 0xBB, 0x24,
+ 0xDA, 0xB5, 0x76, 0x69, 0xA5, 0x54, 0x23, 0x31,
+ 0x11, 0xA4, 0x09, 0xE4, 0x64, 0x10, 0xC5, 0xC1,
+ 0x7D, 0xE7, 0x92, 0xF8, 0x9E, 0x6A, 0x15, 0x8B,
+ 0x98, 0x42, 0x52, 0x66, 0x0B, 0xA1, 0x35, 0x1A,
+ 0x14, 0x7C, 0xE1, 0x9F, 0x28, 0xF1, 0x1B, 0xA6,
+ 0x71, 0x73, 0x81, 0xAB, 0xE6, 0x95, 0x06, 0x1F,
+ 0xC6, 0xB0, 0x51, 0x0E, 0xEE, 0x77, 0xF0, 0xD8,
+ 0xC2, 0x89, 0x7B, 0x07, 0xA2, 0xB7, 0x19, 0x67,
+ 0x2E, 0x8E, 0x47, 0xA7, 0xEF, 0x32, 0xD2, 0x93,
+ 0xDC, 0x9A, 0xB2, 0xED, 0x45, 0xC4, 0x50, 0x3F,
+ 0xE5, 0xCF, 0x88, 0x1C, 0x7A, 0x79, 0xEB, 0x70,
+ 0x2A, 0x7F, 0xBC, 0xDB, 0xD0, 0xB1, 0xCB, 0x08,
+ 0x86, 0x5D, 0x53, 0x72, 0xB6, 0x4B, 0xB3, 0x22,
+ 0xC3, 0x6F, 0xB9, 0xD5, 0x3B, 0x13, 0x2D, 0xAD,
+ 0x33, 0xFD, 0x02, 0x40, 0x8A, 0x3A, 0xF7, 0xE0,
+ 0x8C, 0x3E, 0x61, 0x6E, 0xE9, 0x63, 0xF9, 0xEC,
+ 0x48, 0x30, 0x87, 0x83, 0x12, 0x4D, 0x65, 0xDF };
+
+unsigned char table_171[32] = {
+ 0x07, 0x06, 0x11, 0x08, 0x0C, 0x1F, 0x19, 0x02,
+ 0x14, 0x04, 0x0D, 0x18, 0x1A, 0x05, 0x17, 0x13,
+ 0x1C, 0x1B, 0x15, 0x03, 0x01, 0x0F, 0x16, 0x1E,
+ 0x1D, 0x10, 0x00, 0x12, 0x0B, 0x0E, 0x09, 0x0A };
+
+unsigned char table_172[32] = {
+ 0x11, 0x01, 0x1F, 0x06, 0x1A, 0x04, 0x02, 0x09,
+ 0x05, 0x0D, 0x0B, 0x18, 0x0E, 0x12, 0x1B, 0x17,
+ 0x07, 0x08, 0x1D, 0x1E, 0x14, 0x19, 0x16, 0x15,
+ 0x03, 0x0C, 0x00, 0x10, 0x0A, 0x1C, 0x0F, 0x13 };
+
+unsigned char table_173[32] = {
+ 0x1F, 0x0B, 0x13, 0x00, 0x16, 0x15, 0x14, 0x0A,
+ 0x1D, 0x05, 0x1E, 0x1A, 0x0F, 0x04, 0x0E, 0x01,
+ 0x19, 0x07, 0x02, 0x12, 0x0C, 0x17, 0x08, 0x09,
+ 0x03, 0x11, 0x18, 0x10, 0x1C, 0x1B, 0x06, 0x0D };
+
+unsigned char table_174[32] = {
+ 0x02, 0x1B, 0x0C, 0x17, 0x1F, 0x05, 0x15, 0x1E,
+ 0x16, 0x09, 0x1A, 0x12, 0x0F, 0x1C, 0x18, 0x0A,
+ 0x19, 0x10, 0x0D, 0x13, 0x04, 0x11, 0x08, 0x14,
+ 0x1D, 0x0E, 0x06, 0x00, 0x01, 0x07, 0x0B, 0x03 };
+
+unsigned char table_175[32] = {
+ 0x00, 0x06, 0x0B, 0x08, 0x0C, 0x04, 0x1A, 0x1C,
+ 0x05, 0x1E, 0x14, 0x03, 0x0A, 0x18, 0x12, 0x1D,
+ 0x16, 0x1F, 0x07, 0x09, 0x0F, 0x0E, 0x17, 0x13,
+ 0x11, 0x19, 0x10, 0x0D, 0x1B, 0x02, 0x01, 0x15 };
+
+unsigned char table_176[32] = {
+ 0x12, 0x03, 0x1A, 0x15, 0x04, 0x19, 0x0B, 0x1B,
+ 0x17, 0x1E, 0x0D, 0x05, 0x11, 0x14, 0x1C, 0x00,
+ 0x18, 0x10, 0x0A, 0x06, 0x0E, 0x08, 0x02, 0x07,
+ 0x13, 0x09, 0x16, 0x1D, 0x0F, 0x0C, 0x01, 0x1F };
+
+unsigned char table_177[256] = {
+ 0x5E, 0x4D, 0x76, 0xFE, 0xB5, 0x50, 0x83, 0x23,
+ 0x72, 0xDD, 0x93, 0x08, 0x69, 0xAD, 0xEC, 0x3B,
+ 0x0B, 0x9A, 0x36, 0xC9, 0xCA, 0xBE, 0xF7, 0x30,
+ 0x19, 0x39, 0x2C, 0xAB, 0xE3, 0x7B, 0xBC, 0x32,
+ 0xA0, 0xE4, 0xA6, 0xB6, 0xCB, 0xC8, 0x37, 0x07,
+ 0xD2, 0xA1, 0xD9, 0xF6, 0xBF, 0xF5, 0x88, 0x01,
+ 0x95, 0x0F, 0x03, 0xFD, 0xE6, 0x68, 0x90, 0x61,
+ 0x21, 0x6D, 0x3C, 0x62, 0x34, 0x2B, 0x71, 0x4B,
+ 0x44, 0x64, 0x75, 0xA2, 0x6A, 0xFF, 0x29, 0xBD,
+ 0x35, 0x15, 0xF9, 0xC1, 0x09, 0x45, 0xB2, 0xF2,
+ 0x3F, 0xCE, 0xB0, 0xC0, 0xB8, 0x00, 0x05, 0xD7,
+ 0x11, 0xC6, 0x78, 0x53, 0x9E, 0xB3, 0xED, 0x56,
+ 0x22, 0x5C, 0x9D, 0x6C, 0x99, 0x43, 0x2F, 0xAE,
+ 0xEB, 0x40, 0x8C, 0x1F, 0xC2, 0xDF, 0x92, 0x65,
+ 0x6F, 0x79, 0x5D, 0x5B, 0xAA, 0xDB, 0xF1, 0x96,
+ 0xD4, 0xF4, 0x8B, 0x51, 0xD5, 0xE2, 0xBB, 0x80,
+ 0x17, 0x7C, 0x2A, 0x6E, 0xDE, 0xEA, 0x94, 0x31,
+ 0xA4, 0x2D, 0xC3, 0x8D, 0x55, 0x14, 0x9B, 0x0E,
+ 0x7D, 0xC4, 0x06, 0x33, 0x73, 0xE9, 0x7A, 0x38,
+ 0x5F, 0x89, 0x84, 0xD6, 0xA8, 0x13, 0xE8, 0xCF,
+ 0x46, 0xD0, 0x7F, 0x24, 0x8F, 0xF8, 0x87, 0x1B,
+ 0x47, 0x02, 0x0C, 0x97, 0x52, 0xFB, 0x8E, 0x20,
+ 0x70, 0x3E, 0x7E, 0xD1, 0xE5, 0xEE, 0xCC, 0x91,
+ 0x74, 0xCD, 0x42, 0x04, 0x8A, 0xEF, 0xE1, 0x10,
+ 0x4F, 0x1C, 0x28, 0x9F, 0xD8, 0x0A, 0x18, 0x49,
+ 0x9C, 0x16, 0xF3, 0x82, 0x57, 0x1D, 0x26, 0x66,
+ 0x27, 0x86, 0xE7, 0x59, 0xFA, 0x25, 0x54, 0x0D,
+ 0x98, 0xDC, 0xF0, 0x3D, 0x63, 0x1E, 0x77, 0x3A,
+ 0xDA, 0xB7, 0x6B, 0x2E, 0x48, 0x4C, 0xBA, 0xC7,
+ 0x60, 0xAC, 0x1A, 0xB9, 0xFC, 0xA3, 0xA7, 0xA5,
+ 0xB4, 0x67, 0xA9, 0x81, 0xB1, 0x12, 0xD3, 0x85,
+ 0x5A, 0xC5, 0xE0, 0x58, 0x41, 0x4E, 0x4A, 0xAF };
+
+unsigned char table_178[256] = {
+ 0x33, 0xBA, 0x98, 0xDA, 0x07, 0x2C, 0x22, 0x9B,
+ 0xE0, 0xED, 0xB7, 0xA1, 0x93, 0xEB, 0xDC, 0x49,
+ 0xDF, 0xE1, 0x6C, 0xC2, 0x64, 0x52, 0xD0, 0x8F,
+ 0xA2, 0x48, 0x26, 0x21, 0x6E, 0x5E, 0x0B, 0x7C,
+ 0x0D, 0x90, 0xA4, 0xCE, 0xF5, 0x5F, 0xF9, 0x1D,
+ 0x55, 0x83, 0x8D, 0xFB, 0x38, 0xB3, 0xF2, 0x67,
+ 0xDE, 0x0A, 0xBE, 0xEC, 0x5B, 0x35, 0x08, 0x50,
+ 0xE7, 0x56, 0x4A, 0x02, 0xBC, 0x5A, 0xBD, 0x43,
+ 0x6F, 0x79, 0xB2, 0xF7, 0x60, 0xE9, 0xA0, 0x1B,
+ 0xC8, 0xDD, 0x9D, 0xA3, 0x5C, 0x61, 0x77, 0x72,
+ 0x9C, 0x31, 0x0E, 0x05, 0x1E, 0x12, 0xF1, 0xC9,
+ 0x78, 0x4E, 0x15, 0x7D, 0x54, 0xCB, 0x73, 0xEA,
+ 0xC5, 0x2B, 0x0F, 0x7E, 0x42, 0x96, 0xC6, 0x74,
+ 0x09, 0x65, 0x34, 0xE6, 0x63, 0xA6, 0x70, 0xD3,
+ 0x27, 0x87, 0x3A, 0x16, 0x7B, 0x13, 0x06, 0x40,
+ 0x46, 0x69, 0xAD, 0x88, 0x81, 0xC0, 0x37, 0x58,
+ 0xD1, 0x8A, 0x8E, 0x9A, 0x5D, 0x6D, 0xC7, 0xC3,
+ 0xD2, 0xF4, 0x3F, 0x57, 0x3C, 0x4F, 0xA9, 0x6A,
+ 0x92, 0xA5, 0x97, 0x0C, 0x2A, 0x36, 0x47, 0xDB,
+ 0x8C, 0xEE, 0x03, 0x89, 0x7F, 0x91, 0x24, 0x80,
+ 0x2F, 0x62, 0xE4, 0xAF, 0x17, 0x99, 0xD6, 0xCD,
+ 0xFE, 0x76, 0x1C, 0xD4, 0x3E, 0xFF, 0xD8, 0xC4,
+ 0x39, 0x32, 0xCF, 0xE2, 0xE3, 0x53, 0xD7, 0xCC,
+ 0xD9, 0x11, 0xAA, 0x1F, 0x01, 0x3B, 0x51, 0xB5,
+ 0x94, 0x4B, 0x28, 0xF0, 0xAC, 0x44, 0x14, 0x4C,
+ 0xB9, 0xA7, 0xB8, 0x1A, 0xD5, 0xCA, 0xE8, 0x82,
+ 0x9F, 0x2D, 0xAB, 0x2E, 0x29, 0xFD, 0x68, 0xB1,
+ 0x66, 0xC1, 0x7A, 0xFA, 0x71, 0x04, 0xA8, 0xB0,
+ 0x59, 0x18, 0xAE, 0x25, 0x3D, 0xE5, 0xF6, 0x41,
+ 0x86, 0x75, 0x6B, 0xBB, 0xFC, 0x84, 0x8B, 0x85,
+ 0x10, 0x23, 0xB6, 0xF3, 0x19, 0x30, 0x20, 0x4D,
+ 0x95, 0x9E, 0xBF, 0xEF, 0xF8, 0x45, 0x00, 0xB4 };
+
+unsigned char table_179[256] = {
+ 0x50, 0x3D, 0x41, 0x42, 0x06, 0x5B, 0xD6, 0x34,
+ 0x9D, 0x3C, 0x7B, 0x14, 0xE2, 0x9B, 0x80, 0x15,
+ 0x51, 0x01, 0x6A, 0x30, 0xD7, 0xFC, 0x61, 0x4B,
+ 0x8A, 0xEC, 0x38, 0x71, 0x70, 0x2E, 0x1C, 0x72,
+ 0x79, 0x26, 0x4C, 0x48, 0xED, 0xAD, 0x25, 0x53,
+ 0x03, 0xD9, 0xB5, 0x0D, 0x8E, 0x19, 0xCC, 0xBE,
+ 0xE1, 0x91, 0x64, 0xA6, 0x21, 0xCE, 0x76, 0xAB,
+ 0x9F, 0xD1, 0xB6, 0x23, 0x6D, 0xB0, 0x90, 0xBD,
+ 0x09, 0x3A, 0x5E, 0xD0, 0x73, 0x10, 0x44, 0x08,
+ 0xFF, 0xB8, 0x24, 0x58, 0xDB, 0x65, 0x95, 0xAA,
+ 0xE9, 0xC4, 0x32, 0x2B, 0x84, 0xC9, 0xC7, 0xB1,
+ 0x4F, 0x0C, 0xCB, 0x11, 0x4E, 0x22, 0x4A, 0x16,
+ 0xDE, 0xBC, 0xEE, 0x68, 0x13, 0xFA, 0xC3, 0x98,
+ 0xEB, 0x29, 0x43, 0x9A, 0xA1, 0xE0, 0xF0, 0x3F,
+ 0x2F, 0x1B, 0xC2, 0x66, 0x35, 0xF5, 0xC8, 0xD8,
+ 0x5A, 0xE5, 0x87, 0x47, 0xD3, 0x7A, 0xE6, 0x39,
+ 0x77, 0x81, 0xF2, 0x0E, 0x83, 0x7E, 0x17, 0x6C,
+ 0xB3, 0x5C, 0xE8, 0xD2, 0xC0, 0xA4, 0xF9, 0x86,
+ 0xCD, 0xFB, 0x54, 0x7C, 0xBF, 0x2D, 0x82, 0xDA,
+ 0x96, 0x74, 0x97, 0xC5, 0x7D, 0x27, 0x57, 0x56,
+ 0xDC, 0xBA, 0x69, 0x8C, 0x9C, 0x88, 0xB4, 0x8D,
+ 0x37, 0xEA, 0x3B, 0x33, 0x2C, 0xB2, 0x45, 0xF7,
+ 0xC1, 0x1E, 0x46, 0x02, 0x6B, 0x3E, 0xA7, 0xD5,
+ 0x05, 0x0A, 0xA9, 0x1D, 0xA3, 0x4D, 0xAE, 0x6F,
+ 0x49, 0xDD, 0x8F, 0xEF, 0xBB, 0x67, 0x0B, 0x40,
+ 0x9E, 0xF1, 0x78, 0x28, 0xDF, 0x52, 0xF4, 0x92,
+ 0x94, 0x0F, 0xB9, 0x93, 0xF6, 0x1F, 0xAF, 0xA8,
+ 0xCA, 0xE4, 0x59, 0x7F, 0x85, 0x75, 0xC6, 0xFD,
+ 0x00, 0xB7, 0x55, 0xFE, 0x8B, 0x62, 0x5F, 0x12,
+ 0xF8, 0xD4, 0x89, 0xA0, 0x20, 0xE7, 0xCF, 0x60,
+ 0x5D, 0xAC, 0x1A, 0x36, 0x63, 0x99, 0x31, 0xF3,
+ 0x2A, 0x04, 0x18, 0xA5, 0xA2, 0x6E, 0x07, 0xE3 };
+
+unsigned char table_180[256] = {
+ 0xDA, 0xCC, 0x72, 0xA6, 0xE7, 0x07, 0xFD, 0x25,
+ 0x92, 0x39, 0x49, 0x02, 0xD6, 0x09, 0xA8, 0x65,
+ 0x2E, 0x6C, 0xA1, 0x19, 0xBF, 0x21, 0x11, 0xC7,
+ 0x3F, 0x9F, 0xF4, 0x51, 0xAF, 0x8C, 0xFE, 0xCD,
+ 0x7A, 0xEB, 0x5A, 0xF7, 0x18, 0x69, 0xB9, 0xED,
+ 0x37, 0x45, 0x13, 0xB4, 0xAA, 0x75, 0x47, 0x42,
+ 0xA3, 0x81, 0x88, 0x70, 0xC1, 0x36, 0x73, 0x1D,
+ 0x3B, 0x22, 0xB6, 0x35, 0xE9, 0x31, 0x56, 0x23,
+ 0xE1, 0xF5, 0xAD, 0x46, 0x99, 0x32, 0xE4, 0x40,
+ 0x00, 0x0F, 0x05, 0xC6, 0x33, 0x84, 0x7B, 0x4D,
+ 0x4B, 0x7D, 0x91, 0x3D, 0xCE, 0x64, 0x77, 0x55,
+ 0xD7, 0x2B, 0x2F, 0x2C, 0xB8, 0xD3, 0x85, 0xD1,
+ 0xB5, 0x6A, 0xF9, 0x41, 0x08, 0xBB, 0x87, 0xEC,
+ 0x78, 0xE0, 0xEE, 0x8D, 0x01, 0x58, 0x15, 0x8F,
+ 0x06, 0xF0, 0x8B, 0x27, 0x0D, 0x0B, 0x6D, 0xBD,
+ 0xCA, 0x2A, 0xA2, 0xE6, 0xDD, 0xBC, 0x4E, 0x5D,
+ 0x74, 0x04, 0x3A, 0x96, 0x66, 0x12, 0x1E, 0xF2,
+ 0xF6, 0xC4, 0xAE, 0x3C, 0x0C, 0x90, 0x68, 0xD8,
+ 0x24, 0x5E, 0x79, 0x10, 0xAC, 0xDF, 0x9B, 0xC5,
+ 0x44, 0xC3, 0x50, 0x5C, 0xA5, 0x89, 0x60, 0x5F,
+ 0x48, 0x17, 0x34, 0xA7, 0xE2, 0xF3, 0xD9, 0x3E,
+ 0x9C, 0xB7, 0x7C, 0x1F, 0xA9, 0xD4, 0xA4, 0x0E,
+ 0x8E, 0x4C, 0xDC, 0xF8, 0xF1, 0x98, 0xDE, 0x2D,
+ 0x61, 0xCB, 0xD5, 0x43, 0x86, 0x26, 0xB0, 0x7F,
+ 0x7E, 0xFF, 0xAB, 0x83, 0x14, 0x9A, 0x80, 0x16,
+ 0x30, 0xA0, 0x53, 0x97, 0x52, 0x9E, 0xB1, 0x1B,
+ 0xD0, 0x1A, 0xC8, 0x57, 0xBA, 0x6E, 0xFA, 0x94,
+ 0xE8, 0x63, 0x5B, 0x29, 0xEF, 0x71, 0x8A, 0x03,
+ 0xB3, 0x76, 0xC9, 0xD2, 0xBE, 0xE5, 0x82, 0x1C,
+ 0x95, 0x9D, 0x4A, 0x28, 0xEA, 0x0A, 0xC0, 0xE3,
+ 0x6F, 0x20, 0x54, 0xFB, 0x93, 0xFC, 0x6B, 0x38,
+ 0x62, 0x4F, 0xCF, 0xB2, 0xC2, 0x59, 0xDB, 0x67 };
+
+unsigned char table_181[256] = {
+ 0x2B, 0xED, 0x14, 0x05, 0x80, 0xCC, 0x5A, 0xF8,
+ 0x43, 0xB7, 0x86, 0xC6, 0xEE, 0xA6, 0xD7, 0xD6,
+ 0xA0, 0xC4, 0x21, 0x34, 0xB1, 0x8C, 0xF9, 0xF4,
+ 0x7C, 0x53, 0x06, 0xD4, 0x6B, 0x3F, 0xE1, 0x12,
+ 0x6A, 0xCE, 0xCF, 0xBF, 0x74, 0x3E, 0xD5, 0xCB,
+ 0x97, 0x01, 0xA2, 0x2D, 0xAE, 0xF7, 0x17, 0x29,
+ 0x47, 0x03, 0x0E, 0xE9, 0x82, 0x46, 0x94, 0xAF,
+ 0x2A, 0x90, 0xFE, 0x4A, 0x7E, 0x0C, 0x71, 0xB6,
+ 0xA5, 0xF2, 0x67, 0x41, 0xBA, 0xC2, 0x8A, 0x9D,
+ 0x36, 0xFF, 0x50, 0x2E, 0xC3, 0x91, 0x9C, 0x37,
+ 0x66, 0xAD, 0xB2, 0x1F, 0xE4, 0xE3, 0x9F, 0xDD,
+ 0x87, 0xC0, 0xE6, 0xEF, 0x13, 0x70, 0x5B, 0xDE,
+ 0x5C, 0x75, 0x7F, 0x4F, 0x44, 0xCA, 0x55, 0x57,
+ 0xF0, 0x26, 0xA7, 0xC7, 0x10, 0x51, 0x00, 0xB3,
+ 0x5D, 0x99, 0x81, 0x3B, 0xB9, 0x1C, 0x64, 0x7B,
+ 0xFB, 0xD9, 0x8D, 0x4E, 0xAC, 0x25, 0xBB, 0x69,
+ 0xDF, 0x02, 0x9E, 0x2C, 0xAB, 0xF3, 0x65, 0x09,
+ 0xA3, 0x6C, 0xC1, 0x76, 0x52, 0x30, 0xD8, 0x3A,
+ 0x40, 0x18, 0x59, 0xD0, 0xE5, 0xB4, 0x5F, 0x33,
+ 0x68, 0x92, 0x2F, 0xB8, 0x93, 0xD1, 0xEB, 0xA4,
+ 0xFC, 0x77, 0x19, 0x62, 0xC9, 0x49, 0x84, 0x1A,
+ 0x9A, 0xE7, 0x31, 0xE8, 0xE2, 0x58, 0xF1, 0x4B,
+ 0x1E, 0x0B, 0x39, 0xFD, 0x42, 0x7A, 0x89, 0x38,
+ 0x11, 0x98, 0x63, 0x08, 0xE0, 0xEA, 0xBE, 0xB0,
+ 0x45, 0x1B, 0x4C, 0x54, 0xC8, 0x27, 0x3D, 0x73,
+ 0x04, 0x8F, 0x79, 0xBC, 0x6F, 0x0D, 0x0F, 0xA1,
+ 0x60, 0xDC, 0xC5, 0xFA, 0x8E, 0xDA, 0x15, 0x96,
+ 0xD3, 0x07, 0xF5, 0x3C, 0x88, 0x72, 0x1D, 0x4D,
+ 0x8B, 0x61, 0x0A, 0xDB, 0xAA, 0x20, 0x23, 0xEC,
+ 0x6E, 0x22, 0x48, 0x28, 0xBD, 0xA9, 0x56, 0x5E,
+ 0x85, 0xA8, 0x95, 0x6D, 0x16, 0x78, 0xB5, 0xF6,
+ 0x32, 0x24, 0x7D, 0x9B, 0xD2, 0x83, 0x35, 0xCD };
+
+unsigned char table_182[256] = {
+ 0x06, 0x7F, 0x66, 0xB5, 0xBA, 0x1E, 0xFD, 0x51,
+ 0x81, 0x8D, 0x28, 0xA3, 0x15, 0x37, 0xDC, 0x58,
+ 0xE6, 0x3D, 0xB4, 0xB9, 0x2E, 0xA0, 0x2F, 0xC4,
+ 0xCB, 0xB1, 0x25, 0xBF, 0xC1, 0x4E, 0x5A, 0xE4,
+ 0x0F, 0x10, 0x7C, 0x52, 0xA7, 0x29, 0x76, 0x55,
+ 0xAA, 0x70, 0x62, 0x54, 0x43, 0x93, 0x3A, 0x7D,
+ 0x5B, 0x56, 0x33, 0x64, 0x74, 0x2A, 0xD9, 0x9B,
+ 0x88, 0xC0, 0x3C, 0x63, 0xDE, 0xF4, 0x73, 0xDF,
+ 0x9E, 0xB2, 0xA8, 0x4F, 0x04, 0x57, 0x47, 0x87,
+ 0x14, 0xFC, 0x27, 0x53, 0x83, 0xDB, 0xD7, 0x20,
+ 0x96, 0x31, 0xD0, 0xCF, 0x30, 0x19, 0x69, 0x1A,
+ 0xAE, 0x3B, 0x11, 0x0C, 0xA6, 0x95, 0x8A, 0xF2,
+ 0x1B, 0xCC, 0x78, 0xEF, 0xB3, 0x71, 0x84, 0xA2,
+ 0xF1, 0x7A, 0x92, 0x61, 0xCA, 0x90, 0x94, 0x89,
+ 0x68, 0xEE, 0x97, 0x38, 0x0D, 0xF9, 0x1F, 0x8E,
+ 0xE9, 0x26, 0xBD, 0xC9, 0xFF, 0x4C, 0x44, 0x1D,
+ 0x98, 0xE5, 0x86, 0xF3, 0x18, 0xB6, 0x09, 0xD2,
+ 0x7E, 0xC5, 0xE7, 0x2B, 0x8C, 0x8B, 0x60, 0x3F,
+ 0x2C, 0x6A, 0x08, 0x0E, 0x50, 0x32, 0x9F, 0xF0,
+ 0x9A, 0xC2, 0x39, 0xBE, 0xEA, 0x12, 0x16, 0xBB,
+ 0x5E, 0x67, 0xE3, 0xB8, 0x79, 0x46, 0xDA, 0x00,
+ 0xD3, 0xBC, 0xCE, 0x1C, 0x80, 0xFA, 0xAB, 0x65,
+ 0x4A, 0xF8, 0xAC, 0x72, 0x01, 0xC6, 0x35, 0x85,
+ 0x3E, 0x5C, 0xA1, 0x05, 0xA5, 0xA9, 0xE1, 0x40,
+ 0xEB, 0xE8, 0x5F, 0xF5, 0xC3, 0xD1, 0x34, 0xFB,
+ 0xEC, 0xF7, 0x9C, 0xC7, 0xDD, 0x6C, 0x36, 0x9D,
+ 0x42, 0x59, 0x99, 0x5D, 0xD8, 0x82, 0x07, 0x24,
+ 0x6D, 0xAD, 0x13, 0x48, 0x6B, 0x6E, 0x75, 0x4D,
+ 0xD5, 0x02, 0xED, 0xFE, 0x91, 0xCD, 0x77, 0xB0,
+ 0xF6, 0xC8, 0x6F, 0x23, 0xAF, 0xB7, 0x2D, 0xD6,
+ 0xA4, 0xE2, 0x45, 0x8F, 0x21, 0xE0, 0x49, 0x22,
+ 0x7B, 0x17, 0x0B, 0x0A, 0x41, 0x03, 0xD4, 0x4B };
+
+unsigned char table_183[32] = {
+ 0x1E, 0x1B, 0x11, 0x07, 0x08, 0x06, 0x18, 0x17,
+ 0x0D, 0x0F, 0x12, 0x03, 0x1D, 0x04, 0x0A, 0x1A,
+ 0x0C, 0x13, 0x14, 0x1F, 0x0B, 0x19, 0x10, 0x01,
+ 0x16, 0x05, 0x1C, 0x0E, 0x02, 0x00, 0x09, 0x15 };
+
+unsigned char table_184[32] = {
+ 0x0F, 0x1D, 0x17, 0x16, 0x0D, 0x05, 0x13, 0x1F,
+ 0x1B, 0x09, 0x1C, 0x1E, 0x15, 0x01, 0x06, 0x08,
+ 0x0C, 0x10, 0x0B, 0x02, 0x04, 0x0A, 0x07, 0x1A,
+ 0x18, 0x0E, 0x03, 0x11, 0x12, 0x14, 0x19, 0x00 };
+
+unsigned char table_185[256] = {
+ 0xA5, 0xEE, 0x2E, 0x28, 0xA7, 0xAC, 0xD9, 0xB2,
+ 0x6E, 0x04, 0xB4, 0x03, 0xE8, 0x92, 0x5F, 0x4D,
+ 0x73, 0x20, 0x71, 0xE0, 0x43, 0x53, 0x3F, 0xF8,
+ 0x96, 0xA1, 0x24, 0x97, 0xAD, 0x7B, 0xE5, 0xE6,
+ 0xF2, 0xCE, 0xE3, 0x76, 0x2F, 0xA2, 0x48, 0x0E,
+ 0x4B, 0x4A, 0x8B, 0x5A, 0x81, 0x2C, 0xBF, 0xD7,
+ 0xFB, 0x7D, 0x4C, 0x16, 0xF4, 0x00, 0xF5, 0x40,
+ 0x64, 0x74, 0xA9, 0x37, 0x86, 0xD3, 0x1B, 0xCD,
+ 0xF1, 0x1A, 0x90, 0x9F, 0x54, 0x79, 0x29, 0xC3,
+ 0x77, 0x85, 0x02, 0xB1, 0x70, 0xFE, 0x5B, 0xDA,
+ 0x6B, 0x01, 0x0C, 0x07, 0xB8, 0x58, 0x47, 0x42,
+ 0x09, 0xE4, 0x27, 0xDD, 0xF3, 0x1E, 0x10, 0x9E,
+ 0x49, 0x30, 0x05, 0xBE, 0x59, 0xEB, 0xD2, 0xAA,
+ 0xC8, 0x9D, 0x8C, 0x5E, 0x14, 0x56, 0x8E, 0xF7,
+ 0x38, 0x55, 0x87, 0xA3, 0x5D, 0x41, 0x4F, 0x1F,
+ 0xF6, 0x0F, 0x57, 0x91, 0xAE, 0xBA, 0xB3, 0x95,
+ 0x9B, 0x69, 0xC1, 0x11, 0xD0, 0x25, 0x7F, 0x3B,
+ 0x62, 0xCF, 0xC0, 0xA0, 0xFC, 0xB6, 0x12, 0x6C,
+ 0xF0, 0x13, 0x93, 0xAB, 0xC6, 0x78, 0x6D, 0x88,
+ 0x22, 0x08, 0x2A, 0xE2, 0xB7, 0x65, 0x31, 0x3A,
+ 0xA6, 0x7C, 0xF9, 0xDC, 0xE7, 0xA4, 0xC9, 0x63,
+ 0xA8, 0x0B, 0xED, 0x50, 0x36, 0xD8, 0x3E, 0xB0,
+ 0x6A, 0x5C, 0x45, 0x4E, 0x23, 0x84, 0x34, 0x9A,
+ 0xCC, 0x3D, 0xB5, 0xEA, 0xDE, 0x75, 0xD6, 0xFF,
+ 0x6F, 0xC2, 0xDB, 0x8D, 0x7A, 0x1C, 0xE9, 0x61,
+ 0x0A, 0x1D, 0x32, 0x52, 0x3C, 0x19, 0xFA, 0xD1,
+ 0xD4, 0x68, 0xC7, 0x0D, 0x99, 0x83, 0xEF, 0x80,
+ 0x82, 0xBD, 0xD5, 0x7E, 0x39, 0x72, 0x51, 0xAF,
+ 0x8A, 0x2D, 0xB9, 0x89, 0xC4, 0x67, 0x35, 0xE1,
+ 0x44, 0x06, 0xEC, 0xCB, 0x8F, 0x17, 0xDF, 0x94,
+ 0x60, 0xCA, 0x26, 0xFD, 0x33, 0x46, 0x21, 0xBB,
+ 0x2B, 0xC5, 0x98, 0x18, 0x66, 0x15, 0x9C, 0xBC };
+
+unsigned char table_186[256] = {
+ 0xB7, 0xFA, 0x03, 0x7C, 0x76, 0x43, 0xA7, 0x15,
+ 0x4B, 0x4F, 0x04, 0xAA, 0x4E, 0xD2, 0x52, 0xC8,
+ 0x79, 0x16, 0xF6, 0x61, 0x01, 0x5D, 0xD6, 0x47,
+ 0xDE, 0xC5, 0x4D, 0x2F, 0xF5, 0x29, 0x21, 0xE6,
+ 0x97, 0x35, 0xDC, 0x0E, 0x8B, 0xF4, 0x0F, 0xBE,
+ 0x30, 0x07, 0x1D, 0x46, 0x75, 0xCE, 0x56, 0x42,
+ 0x28, 0x93, 0x84, 0x20, 0xA5, 0xC2, 0x87, 0x45,
+ 0x1C, 0x6B, 0x55, 0x06, 0xEB, 0xB0, 0xF9, 0x14,
+ 0x23, 0xF1, 0xFC, 0xD7, 0x98, 0xD1, 0xA4, 0xED,
+ 0x5B, 0xB1, 0x12, 0x7A, 0xD5, 0x5F, 0x53, 0x88,
+ 0x95, 0x71, 0xE7, 0x5C, 0xF8, 0x83, 0xC7, 0x49,
+ 0xDD, 0xDA, 0x0B, 0xC1, 0x70, 0xEC, 0x67, 0xE2,
+ 0xEA, 0x72, 0x4C, 0x92, 0xA6, 0xE5, 0x59, 0xA9,
+ 0x3C, 0xFE, 0x0A, 0x65, 0x6E, 0xF3, 0xA3, 0x22,
+ 0x24, 0x81, 0xF2, 0xCC, 0xD3, 0xA0, 0xDF, 0xDB,
+ 0xAB, 0x09, 0x13, 0x96, 0x36, 0x9C, 0xEE, 0xD4,
+ 0x33, 0x5E, 0x26, 0xAE, 0x48, 0x38, 0xFF, 0x08,
+ 0x1F, 0x6D, 0x02, 0xEF, 0x7E, 0x57, 0x2A, 0x8A,
+ 0xBA, 0x90, 0xAF, 0xA8, 0x37, 0x8E, 0x9B, 0xC0,
+ 0x69, 0x32, 0x86, 0xBD, 0x73, 0x6C, 0xB9, 0x31,
+ 0x66, 0xBF, 0x1B, 0x44, 0x9E, 0xB2, 0xD0, 0xE0,
+ 0xF0, 0x2C, 0x3F, 0xE1, 0x91, 0x18, 0x19, 0x50,
+ 0xCA, 0x8F, 0x54, 0xB5, 0x8D, 0x0C, 0x17, 0x39,
+ 0x8C, 0x00, 0x7F, 0x41, 0xE3, 0x2E, 0x1A, 0x9D,
+ 0x27, 0xA1, 0x10, 0x34, 0x1E, 0x3A, 0x60, 0x77,
+ 0xBB, 0xB6, 0x0D, 0x4A, 0x3E, 0x6A, 0xB4, 0xA2,
+ 0xB3, 0xFD, 0xCD, 0x80, 0x51, 0xAD, 0xCF, 0xBC,
+ 0x40, 0x74, 0x6F, 0x68, 0x2B, 0xC3, 0xF7, 0x63,
+ 0xB8, 0x25, 0xC4, 0x62, 0xE9, 0xFB, 0x58, 0x85,
+ 0x78, 0xCB, 0x9A, 0x3D, 0xE4, 0xC9, 0x89, 0x2D,
+ 0x64, 0x82, 0xC6, 0x05, 0xD8, 0xAC, 0x99, 0x9F,
+ 0x11, 0x3B, 0x94, 0xE8, 0x7D, 0x7B, 0xD9, 0x5A };
+
+unsigned char table_187[32] = {
+ 0x0F, 0x04, 0x1D, 0x1B, 0x15, 0x10, 0x01, 0x0B,
+ 0x00, 0x17, 0x13, 0x07, 0x1E, 0x1F, 0x08, 0x0A,
+ 0x19, 0x09, 0x05, 0x06, 0x0C, 0x1A, 0x14, 0x16,
+ 0x0E, 0x18, 0x03, 0x1C, 0x12, 0x11, 0x0D, 0x02 };
+
+struct yahoo_fn yahoo_fntable[5][96] =
+ {{{ IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 }},
+ {{ MULADD, 0x36056CD7, 0x4387 },
+ { LOOKUP, (long)table_0, 0 },
+ { LOOKUP, (long)table_1, 0 },
+ { BITFLD, (long)table_2, 0 },
+ { LOOKUP, (long)table_3, 0 },
+ { BITFLD, (long)table_4, 0 },
+ { MULADD, 0x4ABB534D, 0x3769 },
+ { XOR, 0x1D242DA5, 0 },
+ { MULADD, 0x3C23132D, 0x339B },
+ { XOR, 0x0191265C, 0 },
+ { XOR, 0x3DB979DB, 0 },
+ { LOOKUP, (long)table_5, 0 },
+ { XOR, 0x1A550E1E, 0 },
+ { XOR, 0x2F140A2D, 0 },
+ { MULADD, 0x7C466A4B, 0x29BF },
+ { XOR, 0x2D3F30D3, 0 },
+ { MULADD, 0x7E823B21, 0x6BB3 },
+ { BITFLD, (long)table_6, 0 },
+ { LOOKUP, (long)table_7, 0 },
+ { BITFLD, (long)table_8, 0 },
+ { LOOKUP, (long)table_9, 0 },
+ { BITFLD, (long)table_10, 0 },
+ { LOOKUP, (long)table_11, 0 },
+ { BITFLD, (long)table_12, 0 },
+ { LOOKUP, (long)table_13, 0 },
+ { BITFLD, (long)table_14, 0 },
+ { MULADD, 0x5B756AB9, 0x7E9B },
+ { LOOKUP, (long)table_15, 0 },
+ { XOR, 0x1D1C4911, 0 },
+ { LOOKUP, (long)table_16, 0 },
+ { LOOKUP, (long)table_17, 0 },
+ { XOR, 0x46BD7771, 0 },
+ { XOR, 0x51AE2B42, 0 },
+ { MULADD, 0x2417591B, 0x177B },
+ { MULADD, 0x57F27C5F, 0x2433 },
+ { LOOKUP, (long)table_18, 0 },
+ { LOOKUP, (long)table_19, 0 },
+ { XOR, 0x71422261, 0 },
+ { BITFLD, (long)table_20, 0 },
+ { MULADD, 0x58E937F9, 0x1075 },
+ { LOOKUP, (long)table_21, 0 },
+ { BITFLD, (long)table_22, 0 },
+ { LOOKUP, (long)table_23, 0 },
+ { LOOKUP, (long)table_24, 0 },
+ { MULADD, 0x0B4C3D13, 0x1597 },
+ { BITFLD, (long)table_25, 0 },
+ { XOR, 0x0FE07D38, 0 },
+ { MULADD, 0x689B4017, 0x3CFB },
+ { BITFLD, (long)table_26, 0 },
+ { LOOKUP, (long)table_27, 0 },
+ { XOR, 0x35413DF3, 0 },
+ { MULADD, 0x05B611AB, 0x570B },
+ { MULADD, 0x0DA5334F, 0x3AC7 },
+ { XOR, 0x47706008, 0 },
+ { BITFLD, (long)table_28, 0 },
+ { LOOKUP, (long)table_29, 0 },
+ { BITFLD, (long)table_30, 0 },
+ { XOR, 0x57611B36, 0 },
+ { MULADD, 0x314C2CD1, 0x2B5B },
+ { XOR, 0x1EF33946, 0 },
+ { MULADD, 0x28EA041F, 0x638F },
+ { LOOKUP, (long)table_31, 0 },
+ { LOOKUP, (long)table_32, 0 },
+ { LOOKUP, (long)table_33, 0 },
+ { MULADD, 0x511537CB, 0x7135 },
+ { MULADD, 0x1CF71007, 0x5E17 },
+ { XOR, 0x583D4BCF, 0 },
+ { LOOKUP, (long)table_34, 0 },
+ { XOR, 0x373E6856, 0 },
+ { MULADD, 0x4D595519, 0x1A7D },
+ { LOOKUP, (long)table_35, 0 },
+ { LOOKUP, (long)table_36, 0 },
+ { XOR, 0x0E2A36A7, 0 },
+ { LOOKUP, (long)table_37, 0 },
+ { LOOKUP, (long)table_38, 0 },
+ { BITFLD, (long)table_39, 0 },
+ { BITFLD, (long)table_40, 0 },
+ { XOR, 0x53F3604F, 0 },
+ { BITFLD, (long)table_41, 0 },
+ { BITFLD, (long)table_42, 0 },
+ { MULADD, 0x1EDC0BA3, 0x7531 },
+ { LOOKUP, (long)table_43, 0 },
+ { XOR, 0x10DF1038, 0 },
+ { BITFLD, (long)table_44, 0 },
+ { LOOKUP, (long)table_45, 0 },
+ { XOR, 0x4EDE0CAC, 0 },
+ { MULADD, 0x2F076EEB, 0x5BCF },
+ { XOR, 0x6D86030F, 0 },
+ { XOR, 0x3F331713, 0 },
+ { LOOKUP, (long)table_46, 0 },
+ { MULADD, 0x41CD726F, 0x3F79 },
+ { BITFLD, (long)table_47, 0 },
+ { XOR, 0x0ECE0054, 0 },
+ { MULADD, 0x19B32B03, 0x4AD1 },
+ { BITFLD, (long)table_48, 0 },
+ { BITFLD, (long)table_49, 0 }},
+ {{ MULADD, 0x39731111, 0x419B },
+ { XOR, 0x54F7757A, 0 },
+ { BITFLD, (long)table_50, 0 },
+ { BITFLD, (long)table_51, 0 },
+ { LOOKUP, (long)table_52, 0 },
+ { LOOKUP, (long)table_53, 0 },
+ { MULADD, 0x3CC0256B, 0x7CE7 },
+ { XOR, 0x79991847, 0 },
+ { MULADD, 0x228F7FB5, 0x472D },
+ { MULADD, 0x32DA290B, 0x7745 },
+ { XOR, 0x7A28180D, 0 },
+ { BITFLD, (long)table_54, 0 },
+ { BITFLD, (long)table_55, 0 },
+ { MULADD, 0x5C814F8B, 0x227F },
+ { LOOKUP, (long)table_56, 0 },
+ { MULADD, 0x0B496F6D, 0x412D },
+ { XOR, 0x6F4B62DA, 0 },
+ { LOOKUP, (long)table_57, 0 },
+ { XOR, 0x64973977, 0 },
+ { LOOKUP, (long)table_58, 0 },
+ { LOOKUP, (long)table_59, 0 },
+ { BITFLD, (long)table_60, 0 },
+ { LOOKUP, (long)table_61, 0 },
+ { LOOKUP, (long)table_62, 0 },
+ { XOR, 0x6DD14C92, 0 },
+ { LOOKUP, (long)table_63, 0 },
+ { BITFLD, (long)table_64, 0 },
+ { BITFLD, (long)table_65, 0 },
+ { BITFLD, (long)table_66, 0 },
+ { LOOKUP, (long)table_67, 0 },
+ { XOR, 0x5E6324D8, 0 },
+ { LOOKUP, (long)table_68, 0 },
+ { LOOKUP, (long)table_69, 0 },
+ { LOOKUP, (long)table_70, 0 },
+ { BITFLD, (long)table_71, 0 },
+ { XOR, 0x62745ED0, 0 },
+ { MULADD, 0x102C215B, 0x0581 },
+ { LOOKUP, (long)table_72, 0 },
+ { LOOKUP, (long)table_73, 0 },
+ { LOOKUP, (long)table_74, 0 },
+ { MULADD, 0x19511111, 0x12C1 },
+ { LOOKUP, (long)table_75, 0 },
+ { MULADD, 0x2A6E2953, 0x6977 },
+ { LOOKUP, (long)table_76, 0 },
+ { XOR, 0x55CD5445, 0 },
+ { BITFLD, (long)table_77, 0 },
+ { BITFLD, (long)table_78, 0 },
+ { MULADD, 0x646C21EB, 0x43E5 },
+ { XOR, 0x71DC4898, 0 },
+ { XOR, 0x167519CB, 0 },
+ { XOR, 0x6D3158F8, 0 },
+ { XOR, 0x7EA95BEA, 0 },
+ { BITFLD, (long)table_79, 0 },
+ { XOR, 0x47377587, 0 },
+ { XOR, 0x2D8B6E8F, 0 },
+ { MULADD, 0x5E6105DB, 0x1605 },
+ { XOR, 0x65B543C8, 0 },
+ { LOOKUP, (long)table_80, 0 },
+ { BITFLD, (long)table_81, 0 },
+ { MULADD, 0x48AF73CB, 0x0A67 },
+ { XOR, 0x4FB96154, 0 },
+ { LOOKUP, (long)table_82, 0 },
+ { BITFLD, (long)table_83, 0 },
+ { XOR, 0x622C4954, 0 },
+ { BITFLD, (long)table_84, 0 },
+ { XOR, 0x20D220F3, 0 },
+ { XOR, 0x361D4F0D, 0 },
+ { XOR, 0x2B2000D1, 0 },
+ { XOR, 0x6FB8593E, 0 },
+ { LOOKUP, (long)table_85, 0 },
+ { BITFLD, (long)table_86, 0 },
+ { XOR, 0x2B7F7DFC, 0 },
+ { MULADD, 0x5FC41A57, 0x0693 },
+ { MULADD, 0x17154387, 0x2489 },
+ { BITFLD, (long)table_87, 0 },
+ { BITFLD, (long)table_88, 0 },
+ { BITFLD, (long)table_89, 0 },
+ { LOOKUP, (long)table_90, 0 },
+ { XOR, 0x7E221470, 0 },
+ { XOR, 0x7A600061, 0 },
+ { BITFLD, (long)table_91, 0 },
+ { BITFLD, (long)table_92, 0 },
+ { LOOKUP, (long)table_93, 0 },
+ { BITFLD, (long)table_94, 0 },
+ { MULADD, 0x00E813A5, 0x2CE5 },
+ { MULADD, 0x3D707E25, 0x3827 },
+ { MULADD, 0x77A53E07, 0x6A5F },
+ { BITFLD, (long)table_95, 0 },
+ { LOOKUP, (long)table_96, 0 },
+ { LOOKUP, (long)table_97, 0 },
+ { XOR, 0x43A73788, 0 },
+ { LOOKUP, (long)table_98, 0 },
+ { BITFLD, (long)table_99, 0 },
+ { LOOKUP, (long)table_100, 0 },
+ { XOR, 0x55F4606B, 0 },
+ { BITFLD, (long)table_101, 0 }},
+ {{ BITFLD, (long)table_102, 0 },
+ { MULADD, 0x32CA58E3, 0x04F9 },
+ { XOR, 0x11756B30, 0 },
+ { MULADD, 0x218B2569, 0x5DB1 },
+ { XOR, 0x77D64B90, 0 },
+ { BITFLD, (long)table_103, 0 },
+ { LOOKUP, (long)table_104, 0 },
+ { MULADD, 0x7D1428CB, 0x3D },
+ { XOR, 0x6F872C49, 0 },
+ { XOR, 0x2E484655, 0 },
+ { MULADD, 0x1E3349F7, 0x41F5 },
+ { LOOKUP, (long)table_105, 0 },
+ { BITFLD, (long)table_106, 0 },
+ { XOR, 0x61640311, 0 },
+ { BITFLD, (long)table_107, 0 },
+ { LOOKUP, (long)table_108, 0 },
+ { LOOKUP, (long)table_109, 0 },
+ { LOOKUP, (long)table_110, 0 },
+ { XOR, 0x007044D3, 0 },
+ { BITFLD, (long)table_111, 0 },
+ { MULADD, 0x5C221625, 0x576F },
+ { LOOKUP, (long)table_112, 0 },
+ { LOOKUP, (long)table_113, 0 },
+ { XOR, 0x2D406BB1, 0 },
+ { MULADD, 0x680B1F17, 0x12CD },
+ { BITFLD, (long)table_114, 0 },
+ { MULADD, 0x12564D55, 0x32B9 },
+ { MULADD, 0x21A67897, 0x6BAB },
+ { LOOKUP, (long)table_115, 0 },
+ { MULADD, 0x06405119, 0x7143 },
+ { XOR, 0x351D01ED, 0 },
+ { MULADD, 0x46356F6B, 0x0A49 },
+ { MULADD, 0x32C77969, 0x72F3 },
+ { BITFLD, (long)table_116, 0 },
+ { LOOKUP, (long)table_117, 0 },
+ { LOOKUP, (long)table_118, 0 },
+ { BITFLD, (long)table_119, 0 },
+ { LOOKUP, (long)table_120, 0 },
+ { BITFLD, (long)table_121, 0 },
+ { MULADD, 0x74D52C55, 0x5F43 },
+ { XOR, 0x26201CA8, 0 },
+ { XOR, 0x7AEB3255, 0 },
+ { LOOKUP, (long)table_122, 0 },
+ { MULADD, 0x578F1047, 0x640B },
+ { LOOKUP, (long)table_123, 0 },
+ { LOOKUP, (long)table_124, 0 },
+ { BITFLD, (long)table_125, 0 },
+ { BITFLD, (long)table_126, 0 },
+ { XOR, 0x4A1352CF, 0 },
+ { MULADD, 0x4BFB6EF3, 0x704F },
+ { MULADD, 0x1B4C7FE7, 0x5637 },
+ { MULADD, 0x04091A3B, 0x4917 },
+ { XOR, 0x270C2F52, 0 },
+ { LOOKUP, (long)table_127, 0 },
+ { BITFLD, (long)table_128, 0 },
+ { LOOKUP, (long)table_129, 0 },
+ { BITFLD, (long)table_130, 0 },
+ { MULADD, 0x127549D5, 0x579B },
+ { MULADD, 0x0AB54121, 0x7A47 },
+ { BITFLD, (long)table_131, 0 },
+ { XOR, 0x751E6E49, 0 },
+ { LOOKUP, (long)table_132, 0 },
+ { LOOKUP, (long)table_133, 0 },
+ { XOR, 0x670C3F74, 0 },
+ { MULADD, 0x6B080851, 0x7E8B },
+ { XOR, 0x71CD789E, 0 },
+ { XOR, 0x3EB20B7B, 0 },
+ { BITFLD, (long)table_134, 0 },
+ { LOOKUP, (long)table_135, 0 },
+ { MULADD, 0x58A67753, 0x272B },
+ { MULADD, 0x1AB54AD7, 0x4D33 },
+ { MULADD, 0x07D30A45, 0x0569 },
+ { MULADD, 0x737616BF, 0x70C7 },
+ { LOOKUP, (long)table_136, 0 },
+ { MULADD, 0x45C4485D, 0x2063 },
+ { BITFLD, (long)table_137, 0 },
+ { XOR, 0x2598043D, 0 },
+ { MULADD, 0x223A4FE3, 0x49A7 },
+ { XOR, 0x1EED619F, 0 },
+ { BITFLD, (long)table_138, 0 },
+ { XOR, 0x6F477561, 0 },
+ { BITFLD, (long)table_139, 0 },
+ { BITFLD, (long)table_140, 0 },
+ { LOOKUP, (long)table_141, 0 },
+ { MULADD, 0x4BC13C4F, 0x45C1 },
+ { XOR, 0x3B547BFB, 0 },
+ { LOOKUP, (long)table_142, 0 },
+ { MULADD, 0x71406AB3, 0x7A5F },
+ { XOR, 0x2F1467E9, 0 },
+ { MULADD, 0x009366D1, 0x22D1 },
+ { MULADD, 0x587D1B75, 0x2CA5 },
+ { MULADD, 0x213A4BE7, 0x4499 },
+ { MULADD, 0x62653E89, 0x2D5D },
+ { BITFLD, (long)table_143, 0 },
+ { MULADD, 0x4F5F3257, 0x444F },
+ { MULADD, 0x4C0E2B2B, 0x19D3 }},
+ {{ MULADD, 0x3F867B35, 0x7B3B },
+ { MULADD, 0x32D25CB1, 0x3D6D },
+ { BITFLD, (long)table_144, 0 },
+ { MULADD, 0x50FA1C51, 0x5F4F },
+ { LOOKUP, (long)table_145, 0 },
+ { XOR, 0x05FE7AF1, 0 },
+ { MULADD, 0x14067C29, 0x10C5 },
+ { LOOKUP, (long)table_146, 0 },
+ { MULADD, 0x4A5558C5, 0x271F },
+ { XOR, 0x3C0861B1, 0 },
+ { BITFLD, (long)table_147, 0 },
+ { LOOKUP, (long)table_148, 0 },
+ { MULADD, 0x18837C9D, 0x6335 },
+ { BITFLD, (long)table_149, 0 },
+ { XOR, 0x7DAB5033, 0 },
+ { LOOKUP, (long)table_150, 0 },
+ { MULADD, 0x03B87321, 0x7225 },
+ { XOR, 0x7F906745, 0 },
+ { LOOKUP, (long)table_151, 0 },
+ { BITFLD, (long)table_152, 0 },
+ { XOR, 0x21C46C2C, 0 },
+ { MULADD, 0x2B36757D, 0x028D },
+ { BITFLD, (long)table_153, 0 },
+ { LOOKUP, (long)table_154, 0 },
+ { XOR, 0x106B4A85, 0 },
+ { XOR, 0x17640F11, 0 },
+ { LOOKUP, (long)table_155, 0 },
+ { XOR, 0x69E60486, 0 },
+ { LOOKUP, (long)table_156, 0 },
+ { MULADD, 0x3782017D, 0x05BF },
+ { BITFLD, (long)table_157, 0 },
+ { LOOKUP, (long)table_158, 0 },
+ { XOR, 0x6BCA53B0, 0 },
+ { LOOKUP, (long)table_159, 0 },
+ { LOOKUP, (long)table_160, 0 },
+ { LOOKUP, (long)table_161, 0 },
+ { LOOKUP, (long)table_162, 0 },
+ { XOR, 0x0B8236E3, 0 },
+ { BITFLD, (long)table_163, 0 },
+ { MULADD, 0x5EE51C43, 0x4553 },
+ { BITFLD, (long)table_164, 0 },
+ { LOOKUP, (long)table_165, 0 },
+ { LOOKUP, (long)table_166, 0 },
+ { LOOKUP, (long)table_167, 0 },
+ { MULADD, 0x42B14C6F, 0x5531 },
+ { XOR, 0x4A2548E8, 0 },
+ { MULADD, 0x5C071D85, 0x2437 },
+ { LOOKUP, (long)table_168, 0 },
+ { MULADD, 0x29195861, 0x108B },
+ { XOR, 0x24012258, 0 },
+ { LOOKUP, (long)table_169, 0 },
+ { XOR, 0x63CC2377, 0 },
+ { XOR, 0x08D04B59, 0 },
+ { MULADD, 0x3FD30CF5, 0x7027 },
+ { XOR, 0x7C3E0478, 0 },
+ { MULADD, 0x457776B7, 0x24B3 },
+ { XOR, 0x086652BC, 0 },
+ { MULADD, 0x302F5B13, 0x371D },
+ { LOOKUP, (long)table_170, 0 },
+ { MULADD, 0x58692D47, 0x0671 },
+ { XOR, 0x6601178E, 0 },
+ { MULADD, 0x0F195B9B, 0x1369 },
+ { XOR, 0x07BA21D8, 0 },
+ { BITFLD, (long)table_171, 0 },
+ { BITFLD, (long)table_172, 0 },
+ { XOR, 0x13AC3D21, 0 },
+ { MULADD, 0x5BCF3275, 0x6E1B },
+ { MULADD, 0x62725C5B, 0x16B9 },
+ { MULADD, 0x5B950FDF, 0x2D35 },
+ { BITFLD, (long)table_173, 0 },
+ { BITFLD, (long)table_174, 0 },
+ { MULADD, 0x73BA5335, 0x1C13 },
+ { BITFLD, (long)table_175, 0 },
+ { BITFLD, (long)table_176, 0 },
+ { XOR, 0x3E144154, 0 },
+ { MULADD, 0x4EED7B27, 0x38AB },
+ { LOOKUP, (long)table_177, 0 },
+ { MULADD, 0x627C7E0F, 0x7F01 },
+ { MULADD, 0x5D7E1F73, 0x2C0F },
+ { LOOKUP, (long)table_178, 0 },
+ { MULADD, 0x55C9525F, 0x4659 },
+ { XOR, 0x3765334C, 0 },
+ { MULADD, 0x5DF66DDF, 0x7C25 },
+ { LOOKUP, (long)table_179, 0 },
+ { LOOKUP, (long)table_180, 0 },
+ { XOR, 0x16AE5776, 0 },
+ { LOOKUP, (long)table_181, 0 },
+ { LOOKUP, (long)table_182, 0 },
+ { BITFLD, (long)table_183, 0 },
+ { BITFLD, (long)table_184, 0 },
+ { LOOKUP, (long)table_185, 0 },
+ { MULADD, 0x4392327B, 0x7E0D },
+ { LOOKUP, (long)table_186, 0 },
+ { MULADD, 0x3D8B0CB5, 0x640D },
+ { MULADD, 0x32865601, 0x4D43 },
+ { BITFLD, (long)table_187, 0 }}};
+
+#define A( x ) (( x ) & 0xFF )
+#define B( x ) (( x ) >> 8 & 0xFF )
+#define C( x ) (( x ) >> 16 & 0xFF )
+#define D( x ) (( x ) >> 24 & 0xFF )
+
+int yahoo_xfrm( int table, int depth, int seed )
+{
+ struct yahoo_fn *xfrm;
+ int i, j, z;
+ unsigned int n = seed;
+ unsigned char *arg;
+
+ for( i = 0; i < depth; i++ )
+ {
+ xfrm = &yahoo_fntable[table][n % 96];
+ switch( xfrm->type )
+ {
+ case IDENT:
+ return seed;
+ case XOR:
+ seed ^= xfrm->arg1;
+ break;
+ case MULADD:
+ seed = seed * xfrm->arg1 + xfrm->arg2;
+ break;
+ case LOOKUP:
+ arg = (unsigned char *)xfrm->arg1;
+ seed = arg[A( seed )] | arg[B( seed )] << 8 | arg[C( seed )] << 16
+ | arg[D( seed )] << 24;
+ break;
+ case BITFLD:
+ arg = (unsigned char *)xfrm->arg1;
+ for( j = 0, z = 0; j < 32; j++ )
+ z = ((( seed >> j ) & 1 ) << arg[j] ) | ( ~( 1 << arg[j] ) & z );
+ seed = z;
+ break;
+ }
+ if( depth - i == 1 )
+ return seed;
+ z = (((((( A( seed ) * 0x9E3779B1 ) ^ B( seed )) * 0x9E3779B1 )
+ ^ C( seed )) * 0x9E3779B1 ) ^ D( seed )) * 0x9E3779B1;
+ n = (((( z ^ ( z >> 8 )) >> 16 ) ^ z ) ^ ( z >> 8 )) & 0xFF;
+ seed *= 0x00010DCD;
+ }
+ return seed;
+}
diff --git a/protocols/yahoo/yahoo_fn.h b/protocols/yahoo/yahoo_fn.h
new file mode 100644
index 00000000..3f79f524
--- /dev/null
+++ b/protocols/yahoo/yahoo_fn.h
@@ -0,0 +1,33 @@
+/*
+ * libyahoo2 - originally from gaim patches by Amatus
+ *
+ * Copyright (C) 2003-2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define IDENT 1 /* identify function */
+#define XOR 2 /* xor with arg1 */
+#define MULADD 3 /* multipy by arg1 then add arg2 */
+#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */
+#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */
+
+struct yahoo_fn
+{
+ int type;
+ long arg1, arg2;
+};
+
+int yahoo_xfrm( int table, int depth, int seed );
diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c
new file mode 100644
index 00000000..41e31a23
--- /dev/null
+++ b/protocols/yahoo/yahoo_httplib.c
@@ -0,0 +1,437 @@
+/*
+ * libyahoo2: yahoo_httplib.c
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+
+#include <errno.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include "yahoo2.h"
+#include "yahoo2_callbacks.h"
+#include "yahoo_httplib.h"
+#include "yahoo_util.h"
+
+#include "yahoo_debug.h"
+#ifdef __MINGW32__
+# include <winsock2.h>
+# define write(a,b,c) send(a,b,c,0)
+# define read(a,b,c) recv(a,b,c,0)
+# define snprintf _snprintf
+#endif
+
+#ifdef USE_STRUCT_CALLBACKS
+extern struct yahoo_callbacks *yc;
+#define YAHOO_CALLBACK(x) yc->x
+#else
+#define YAHOO_CALLBACK(x) x
+#endif
+
+extern enum yahoo_log_level log_level;
+
+int yahoo_tcp_readline(char *ptr, int maxlen, int fd)
+{
+ int n, rc;
+ char c;
+
+ for (n = 1; n < maxlen; n++) {
+
+ do {
+ rc = read(fd, &c, 1);
+ } while(rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */
+
+ if (rc == 1) {
+ if(c == '\r') /* get rid of \r */
+ continue;
+ *ptr = c;
+ if (c == '\n')
+ break;
+ ptr++;
+ } else if (rc == 0) {
+ if (n == 1)
+ return (0); /* EOF, no data */
+ else
+ break; /* EOF, w/ data */
+ } else {
+ return -1;
+ }
+ }
+
+ *ptr = 0;
+ return (n);
+}
+
+static int url_to_host_port_path(const char *url,
+ char *host, int *port, char *path)
+{
+ char *urlcopy=NULL;
+ char *slash=NULL;
+ char *colon=NULL;
+
+ /*
+ * http://hostname
+ * http://hostname/
+ * http://hostname/path
+ * http://hostname/path:foo
+ * http://hostname:port
+ * http://hostname:port/
+ * http://hostname:port/path
+ * http://hostname:port/path:foo
+ */
+
+ if(strstr(url, "http://") == url) {
+ urlcopy = strdup(url+7);
+ } else {
+ WARNING(("Weird url - unknown protocol: %s", url));
+ return 0;
+ }
+
+ slash = strchr(urlcopy, '/');
+ colon = strchr(urlcopy, ':');
+
+ if(!colon || (slash && slash < colon)) {
+ *port = 80;
+ } else {
+ *colon = 0;
+ *port = atoi(colon+1);
+ }
+
+ if(!slash) {
+ strcpy(path, "/");
+ } else {
+ strcpy(path, slash);
+ *slash = 0;
+ }
+
+ strcpy(host, urlcopy);
+
+ FREE(urlcopy);
+
+ return 1;
+}
+
+static int isurlchar(unsigned char c)
+{
+ return (isalnum(c) || '-' == c || '_' == c);
+}
+
+char *yahoo_urlencode(const char *instr)
+{
+ int ipos=0, bpos=0;
+ char *str = NULL;
+ int len = strlen(instr);
+
+ if(!(str = y_new(char, 3*len + 1) ))
+ return "";
+
+ while(instr[ipos]) {
+ while(isurlchar(instr[ipos]))
+ str[bpos++] = instr[ipos++];
+ if(!instr[ipos])
+ break;
+
+ snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]);
+ bpos+=3;
+ ipos++;
+ }
+ str[bpos]='\0';
+
+ /* free extra alloc'ed mem. */
+ len = strlen(str);
+ str = y_renew(char, str, len+1);
+
+ return (str);
+}
+
+char *yahoo_urldecode(const char *instr)
+{
+ int ipos=0, bpos=0;
+ char *str = NULL;
+ char entity[3]={0,0,0};
+ unsigned dec;
+ int len = strlen(instr);
+
+ if(!(str = y_new(char, len+1) ))
+ return "";
+
+ while(instr[ipos]) {
+ while(instr[ipos] && instr[ipos]!='%')
+ if(instr[ipos]=='+') {
+ str[bpos++]=' ';
+ ipos++;
+ } else
+ str[bpos++] = instr[ipos++];
+ if(!instr[ipos])
+ break;
+
+ if(instr[ipos+1] && instr[ipos+2]) {
+ ipos++;
+ entity[0]=instr[ipos++];
+ entity[1]=instr[ipos++];
+ sscanf(entity, "%2x", &dec);
+ str[bpos++] = (char)dec;
+ } else {
+ str[bpos++] = instr[ipos++];
+ }
+ }
+ str[bpos]='\0';
+
+ /* free extra alloc'ed mem. */
+ len = strlen(str);
+ str = y_renew(char, str, len+1);
+
+ return (str);
+}
+
+char *yahoo_xmldecode(const char *instr)
+{
+ int ipos=0, bpos=0, epos=0;
+ char *str = NULL;
+ char entity[4]={0,0,0,0};
+ char *entitymap[5][2]={
+ {"amp;", "&"},
+ {"quot;", "\""},
+ {"lt;", "<"},
+ {"gt;", "<"},
+ {"nbsp;", " "}
+ };
+ unsigned dec;
+ int len = strlen(instr);
+
+ if(!(str = y_new(char, len+1) ))
+ return "";
+
+ while(instr[ipos]) {
+ while(instr[ipos] && instr[ipos]!='&')
+ if(instr[ipos]=='+') {
+ str[bpos++]=' ';
+ ipos++;
+ } else
+ str[bpos++] = instr[ipos++];
+ if(!instr[ipos] || !instr[ipos+1])
+ break;
+ ipos++;
+
+ if(instr[ipos] == '#') {
+ ipos++;
+ epos=0;
+ while(instr[ipos] != ';')
+ entity[epos++]=instr[ipos++];
+ sscanf(entity, "%u", &dec);
+ str[bpos++] = (char)dec;
+ ipos++;
+ } else {
+ int i;
+ for (i=0; i<5; i++)
+ if(!strncmp(instr+ipos, entitymap[i][0],
+ strlen(entitymap[i][0]))) {
+ str[bpos++] = entitymap[i][1][0];
+ ipos += strlen(entitymap[i][0]);
+ break;
+ }
+ }
+ }
+ str[bpos]='\0';
+
+ /* free extra alloc'ed mem. */
+ len = strlen(str);
+ str = y_renew(char, str, len+1);
+
+ return (str);
+}
+
+typedef void (*http_connected)(int id, int fd, int error);
+
+struct callback_data {
+ int id;
+ yahoo_get_fd_callback callback;
+ char *request;
+ void *user_data;
+};
+
+static void connect_complete(int fd, int error, void *data)
+{
+ struct callback_data *ccd = data;
+ if(error == 0 && fd > 0)
+ write(fd, ccd->request, strlen(ccd->request));
+ FREE(ccd->request);
+ ccd->callback(ccd->id, fd, error, ccd->user_data);
+ FREE(ccd);
+}
+
+static void yahoo_send_http_request(int id, char *host, int port, char *request,
+ yahoo_get_fd_callback callback, void *data)
+{
+ struct callback_data *ccd=y_new0(struct callback_data, 1);
+ ccd->callback = callback;
+ ccd->id = id;
+ ccd->request = strdup(request);
+ ccd->user_data = data;
+
+ YAHOO_CALLBACK(ext_yahoo_connect_async)(id, host, port, connect_complete, ccd);
+}
+
+void yahoo_http_post(int id, const char *url, const char *cookies, long content_length,
+ yahoo_get_fd_callback callback, void *data)
+{
+ char host[255];
+ int port = 80;
+ char path[255];
+ char buff[1024];
+
+ if(!url_to_host_port_path(url, host, &port, path))
+ return;
+
+ snprintf(buff, sizeof(buff),
+ "POST %s HTTP/1.0\r\n"
+ "Content-length: %ld\r\n"
+ "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n"
+ "Host: %s:%d\r\n"
+ "Cookie: %s\r\n"
+ "\r\n",
+ path, content_length,
+ host, port,
+ cookies);
+
+ yahoo_send_http_request(id, host, port, buff, callback, data);
+}
+
+void yahoo_http_get(int id, const char *url, const char *cookies,
+ yahoo_get_fd_callback callback, void *data)
+{
+ char host[255];
+ int port = 80;
+ char path[255];
+ char buff[1024];
+
+ if(!url_to_host_port_path(url, host, &port, path))
+ return;
+
+ snprintf(buff, sizeof(buff),
+ "GET %s HTTP/1.0\r\n"
+ "Host: %s:%d\r\n"
+ "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n"
+ "Cookie: %s\r\n"
+ "\r\n",
+ path, host, port, cookies);
+
+ yahoo_send_http_request(id, host, port, buff, callback, data);
+}
+
+struct url_data {
+ yahoo_get_url_handle_callback callback;
+ void *user_data;
+};
+
+static void yahoo_got_url_fd(int id, int fd, int error, void *data)
+{
+ char *tmp=NULL;
+ char buff[1024];
+ unsigned long filesize=0;
+ char *filename=NULL;
+ int n;
+
+ struct url_data *ud = data;
+
+ if(error || fd < 0) {
+ ud->callback(id, fd, error, filename, filesize, ud->user_data);
+ FREE(ud);
+ return;
+ }
+
+ while((n=yahoo_tcp_readline(buff, sizeof(buff), fd)) > 0) {
+ LOG(("Read:%s:\n", buff));
+ if(!strcmp(buff, ""))
+ break;
+
+ if( !strncasecmp(buff, "Content-length:",
+ strlen("Content-length:")) ) {
+ tmp = strrchr(buff, ' ');
+ if(tmp)
+ filesize = atol(tmp);
+ }
+
+ if( !strncasecmp(buff, "Content-disposition:",
+ strlen("Content-disposition:")) ) {
+ tmp = strstr(buff, "name=");
+ if(tmp) {
+ tmp+=strlen("name=");
+ if(tmp[0] == '"') {
+ char *tmp2;
+ tmp++;
+ tmp2 = strchr(tmp, '"');
+ if(tmp2)
+ *tmp2 = '\0';
+ } else {
+ char *tmp2;
+ tmp2 = strchr(tmp, ';');
+ if(!tmp2)
+ tmp2 = strchr(tmp, '\r');
+ if(!tmp2)
+ tmp2 = strchr(tmp, '\n');
+ if(tmp2)
+ *tmp2 = '\0';
+ }
+
+ filename = strdup(tmp);
+ }
+ }
+ }
+
+ LOG(("n == %d\n", n));
+ LOG(("Calling callback, filename:%s, size: %ld\n", filename, filesize));
+ ud->callback(id, fd, error, filename, filesize, ud->user_data);
+ FREE(ud);
+ FREE(filename);
+}
+
+void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd,
+ yahoo_get_url_handle_callback callback, void *data)
+{
+ char buff[1024];
+ struct url_data *ud = y_new0(struct url_data, 1);
+ snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+ ud->callback = callback;
+ ud->user_data = data;
+ yahoo_http_get(id, url, buff, yahoo_got_url_fd, ud);
+}
+
diff --git a/protocols/yahoo/yahoo_httplib.h b/protocols/yahoo/yahoo_httplib.h
new file mode 100644
index 00000000..fd28ad48
--- /dev/null
+++ b/protocols/yahoo/yahoo_httplib.h
@@ -0,0 +1,48 @@
+/*
+ * libyahoo2: yahoo_httplib.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef YAHOO_HTTPLIB_H
+#define YAHOO_HTTPLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yahoo2_types.h"
+
+char *yahoo_urlencode(const char *instr);
+char *yahoo_urldecode(const char *instr);
+char *yahoo_xmldecode(const char *instr);
+
+int yahoo_tcp_readline(char *ptr, int maxlen, int fd);
+void yahoo_http_post(int id, const char *url, const char *cookies, long size,
+ yahoo_get_fd_callback callback, void *data);
+void yahoo_http_get(int id, const char *url, const char *cookies,
+ yahoo_get_fd_callback callback, void *data);
+void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd,
+ yahoo_get_url_handle_callback callback, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/protocols/yahoo/yahoo_list.c b/protocols/yahoo/yahoo_list.c
new file mode 100644
index 00000000..cda631c6
--- /dev/null
+++ b/protocols/yahoo/yahoo_list.c
@@ -0,0 +1,236 @@
+/*
+ * yahoo_list.c: linked list routines
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ * Other code copyright Meredydd Luff <meredydd AT everybuddy.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Some of this code was borrowed from elist.c in the eb-lite sources
+ *
+ */
+
+#include <stdlib.h>
+
+#include "yahoo_list.h"
+
+YList *y_list_append(YList * list, void *data)
+{
+ YList *n;
+ YList *new_list = malloc(sizeof(YList));
+ YList *attach_to = NULL;
+
+ new_list->next = NULL;
+ new_list->data = data;
+
+ for (n = list; n != NULL; n = n->next) {
+ attach_to = n;
+ }
+
+ if (attach_to == NULL) {
+ new_list->prev = NULL;
+ return new_list;
+ } else {
+ new_list->prev = attach_to;
+ attach_to->next = new_list;
+ return list;
+ }
+}
+
+YList *y_list_prepend(YList * list, void *data)
+{
+ YList *n = malloc(sizeof(YList));
+
+ n->next = list;
+ n->prev = NULL;
+ n->data = data;
+ if (list)
+ list->prev = n;
+
+ return n;
+}
+
+YList *y_list_concat(YList * list, YList * add)
+{
+ YList *l;
+
+ if(!list)
+ return add;
+
+ if(!add)
+ return list;
+
+ for (l = list; l->next; l = l->next)
+ ;
+
+ l->next = add;
+ add->prev = l;
+
+ return list;
+}
+
+YList *y_list_remove(YList * list, void *data)
+{
+ YList *n;
+
+ for (n = list; n != NULL; n = n->next) {
+ if (n->data == data) {
+ list=y_list_remove_link(list, n);
+ y_list_free_1(n);
+ break;
+ }
+ }
+
+ return list;
+}
+
+/* Warning */
+/* link MUST be part of list */
+/* caller must free link using y_list_free_1 */
+YList *y_list_remove_link(YList * list, const YList * link)
+{
+ if (!link)
+ return list;
+
+ if (link->next)
+ link->next->prev = link->prev;
+ if (link->prev)
+ link->prev->next = link->next;
+
+ if (link == list)
+ list = link->next;
+
+ return list;
+}
+
+int y_list_length(const YList * list)
+{
+ int retval = 0;
+ const YList *n = list;
+
+ for (n = list; n != NULL; n = n->next) {
+ retval++;
+ }
+
+ return retval;
+}
+
+/* well, you could just check for list == NULL, but that would be
+ * implementation dependent
+ */
+int y_list_empty(const YList * list)
+{
+ if(!list)
+ return 1;
+ else
+ return 0;
+}
+
+int y_list_singleton(const YList * list)
+{
+ if(!list || list->next)
+ return 0;
+ return 1;
+}
+
+YList *y_list_copy(YList * list)
+{
+ YList *n;
+ YList *copy = NULL;
+
+ for (n = list; n != NULL; n = n->next) {
+ copy = y_list_append(copy, n->data);
+ }
+
+ return copy;
+}
+
+void y_list_free_1(YList * list)
+{
+ free(list);
+}
+
+void y_list_free(YList * list)
+{
+ YList *n = list;
+
+ while (n != NULL) {
+ YList *next = n->next;
+ free(n);
+ n = next;
+ }
+}
+
+YList *y_list_find(YList * list, const void *data)
+{
+ YList *l;
+ for (l = list; l && l->data != data; l = l->next)
+ ;
+
+ return l;
+}
+
+void y_list_foreach(YList * list, YListFunc fn, void * user_data)
+{
+ for (; list; list = list->next)
+ fn(list->data, user_data);
+}
+
+YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp)
+{
+ YList *l;
+ for (l = list; l; l = l->next)
+ if (comp(l->data, data) == 0)
+ return l;
+
+ return NULL;
+}
+
+YList *y_list_nth(YList * list, int n)
+{
+ int i=n;
+ for ( ; list && i; list = list->next, i--)
+ ;
+
+ return list;
+}
+
+YList *y_list_insert_sorted(YList * list, void *data, YListCompFunc comp)
+{
+ YList *l, *n, *prev = NULL;
+ if (!list)
+ return y_list_append(list, data);
+
+ n = malloc(sizeof(YList));
+ n->data = data;
+ for (l = list; l && comp(l->data, n->data) <= 0; l = l->next)
+ prev = l;
+
+ if (l) {
+ n->prev = l->prev;
+ l->prev = n;
+ } else
+ n->prev = prev;
+
+ n->next = l;
+
+ if(n->prev) {
+ n->prev->next = n;
+ return list;
+ } else {
+ return n;
+ }
+
+}
diff --git a/protocols/yahoo/yahoo_list.h b/protocols/yahoo/yahoo_list.h
new file mode 100644
index 00000000..a7a69635
--- /dev/null
+++ b/protocols/yahoo/yahoo_list.h
@@ -0,0 +1,74 @@
+/*
+ * yahoo_list.h: linked list routines
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ * Other code copyright Meredydd Luff <meredydd AT everybuddy.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * This is a replacement for the GList. It only provides functions that
+ * we use in Ayttm. Thanks to Meredyyd from everybuddy dev for doing
+ * most of it.
+ */
+
+#ifndef __YLIST_H__
+#define __YLIST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _YList {
+ struct _YList *next;
+ struct _YList *prev;
+ void *data;
+} YList;
+
+typedef int (*YListCompFunc) (const void *, const void *);
+typedef void (*YListFunc) (void *, void *);
+
+YList *y_list_append(YList * list, void *data);
+YList *y_list_prepend(YList * list, void *data);
+YList *y_list_remove_link(YList * list, const YList * link);
+YList *y_list_remove(YList * list, void *data);
+
+YList *y_list_insert_sorted(YList * list, void * data, YListCompFunc comp);
+
+YList *y_list_copy(YList * list);
+
+YList *y_list_concat(YList * list, YList * add);
+
+YList *y_list_find(YList * list, const void *data);
+YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp);
+
+YList *y_list_nth(YList * list, int n);
+
+void y_list_foreach(YList * list, YListFunc fn, void *user_data);
+
+void y_list_free_1(YList * list);
+void y_list_free(YList * list);
+int y_list_length(const YList * list);
+int y_list_empty(const YList * list);
+int y_list_singleton(const YList * list);
+
+#define y_list_next(list) list->next
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c
new file mode 100644
index 00000000..cb155e3c
--- /dev/null
+++ b/protocols/yahoo/yahoo_util.c
@@ -0,0 +1,159 @@
+/*
+ * libyahoo2: yahoo_util.c
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "yahoo_util.h"
+
+char * y_string_append(char * string, char * append)
+{
+ int size = strlen(string) + strlen(append) + 1;
+ char * new_string = y_renew(char, string, size);
+
+ if(new_string == NULL) {
+ new_string = y_new(char, size);
+ strcpy(new_string, string);
+ FREE(string);
+ }
+
+ strcat(new_string, append);
+
+ return new_string;
+}
+
+char * y_str_to_utf8(const char *in)
+{
+ unsigned int n, i = 0;
+ char *result = NULL;
+
+ if(in == NULL || *in == '\0')
+ return "";
+
+ result = y_new(char, strlen(in) * 2 + 1);
+
+ /* convert a string to UTF-8 Format */
+ for (n = 0; n < strlen(in); n++) {
+ unsigned char c = (unsigned char)in[n];
+
+ if (c < 128) {
+ result[i++] = (char) c;
+ } else {
+ result[i++] = (char) ((c >> 6) | 192);
+ result[i++] = (char) ((c & 63) | 128);
+ }
+ }
+ result[i] = '\0';
+ return result;
+}
+
+char * y_utf8_to_str(const char *in)
+{
+ int i = 0;
+ unsigned int n;
+ char *result = NULL;
+
+ if(in == NULL || *in == '\0')
+ return "";
+
+ result = y_new(char, strlen(in) + 1);
+
+ /* convert a string from UTF-8 Format */
+ for (n = 0; n < strlen(in); n++) {
+ unsigned char c = in[n];
+
+ if (c < 128) {
+ result[i++] = (char) c;
+ } else {
+ result[i++] = (c << 6) | (in[++n] & 63);
+ }
+ }
+ result[i] = '\0';
+ return result;
+}
+
+#if !HAVE_GLIB
+
+void y_strfreev(char ** vector)
+{
+ char **v;
+ for(v = vector; *v; v++) {
+ FREE(*v);
+ }
+ FREE(vector);
+}
+
+char ** y_strsplit(char * str, char * sep, int nelem)
+{
+ char ** vector;
+ char *s, *p;
+ int i=0;
+ int l = strlen(sep);
+ if(nelem < 0) {
+ char * s;
+ nelem=0;
+ for(s=strstr(str, sep); s; s=strstr(s+l, sep),nelem++)
+ ;
+ if(strcmp(str+strlen(str)-l, sep))
+ nelem++;
+ }
+
+ vector = y_new(char *, nelem + 1);
+
+ for(p=str, s=strstr(p,sep); i<nelem && s; p=s+l, s=strstr(p,sep), i++) {
+ int len = s-p;
+ vector[i] = y_new(char, len+1);
+ strncpy(vector[i], p, len);
+ vector[i][len] = '\0';
+ }
+
+ if(i<nelem) /* str didn't end with sep */
+ vector[i++] = strdup(p);
+
+ vector[i] = NULL;
+
+ return vector;
+}
+
+void * y_memdup(const void * addr, int n)
+{
+ void * new_chunk = malloc(n);
+ if(new_chunk)
+ memcpy(new_chunk, addr, n);
+ return new_chunk;
+}
+
+#endif
diff --git a/protocols/yahoo/yahoo_util.h b/protocols/yahoo/yahoo_util.h
new file mode 100644
index 00000000..0046fe16
--- /dev/null
+++ b/protocols/yahoo/yahoo_util.h
@@ -0,0 +1,102 @@
+/*
+ * libyahoo2: yahoo_util.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __YAHOO_UTIL_H__
+#define __YAHOO_UTIL_H__
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_GLIB
+# include <glib.h>
+
+# define FREE(x) if(x) {g_free(x); x=NULL;}
+
+# define y_new g_new
+# define y_new0 g_new0
+# define y_renew g_renew
+
+# define y_memdup g_memdup
+# define y_strsplit g_strsplit
+# define y_strfreev g_strfreev
+# ifndef strdup
+# define strdup g_strdup
+# endif
+# ifndef strncasecmp
+# define strncasecmp g_strncasecmp
+# define strcasecmp g_strcasecmp
+# endif
+
+# define snprintf g_snprintf
+# define vsnprintf g_vsnprintf
+
+#else
+
+# include <stdlib.h>
+# include <stdarg.h>
+
+# define FREE(x) if(x) {free(x); x=NULL;}
+
+# define y_new(type, n) (type *)malloc(sizeof(type) * (n))
+# define y_new0(type, n) (type *)calloc((n), sizeof(type))
+# define y_renew(type, mem, n) (type *)realloc(mem, n)
+
+void * y_memdup(const void * addr, int n);
+char ** y_strsplit(char * str, char * sep, int nelem);
+void y_strfreev(char ** vector);
+
+int strncasecmp(const char * s1, const char * s2, size_t n);
+int strcasecmp(const char * s1, const char * s2);
+
+char * strdup(const char *s);
+
+int snprintf(char *str, size_t size, const char *format, ...);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+/*
+ * The following three functions return newly allocated memory.
+ * You must free it yourself
+ */
+char * y_string_append(char * str, char * append);
+char * y_str_to_utf8(const char * in);
+char * y_utf8_to_str(const char * in);
+
+#endif
+