diff options
Diffstat (limited to 'protocols')
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],"&",5); + j += 5; + break; + case '\'': + memcpy(&temp[j],"'",6); + j += 6; + break; + case '\"': + memcpy(&temp[j],""",6); + j += 6; + break; + case '<': + memcpy(&temp[j],"<",4); + j += 4; + break; + case '>': + memcpy(&temp[j],">",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 ߪ 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, ¶ms); + + 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, "&" ); + break; + case '<': + ret = g_string_append( ret, "<" ); + break; + case '>': + ret = g_string_append( ret, ">" ); + break; + case '"': + ret = g_string_append( ret, """ ); + 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 + |