aboutsummaryrefslogtreecommitdiffstats
path: root/doc/bitlbee.8
blob: 36576e598c25c6dc97890397d4ad42e41527b7b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
.\" BitlBee 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; see the file COPYING.  If not, write to
.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
.\"
.TH bitlbee 8 "07 March 2004"
.SH NAME
BitlBee \- IRC gateway to IM chat networks
.SH SYNOPSIS
.PP
.B bitlbee
[-I]
[-c \fIconfiguration file\fP]
[-d \fIconfiguration directory\fP]
.PP
.B bitlbee
-D
[-i \fIaddress\fP]
[-p \fIport number\fP]
[-n]
[-v]
[-c \fIconfiguration file\fP]
[-d \fIconfiguration directory\fP]
.PP
.B bitlbee
-h
.RI
.SH DESCRIPTION
BitlBee is an IRC daemon that can talk to instant messaging 
networks and acts as a gateway. Users can connect to the server
with any normal IRC client and see their 'buddy list' in
&bitlbee. It currently supports Oscar (AIM and ICQ),
MSN, Jabber, Yahoo! and Twitter.

\fBbitlbee\fP should be called by
.BR inetd (8),
or you can run it as a stand-alone daemon.
.PP
.SH OPTIONS
.PP
.IP "-I"
Run in 
.BR inetd (8)
mode. This is the default setting, you usually don't have to specify this
option.
.IP "-D"
Run in daemon mode. In this mode, BitlBee forks to the background and
waits for new connections. All clients will be served from one process.
.IP "-F"
Run in ForkDaemon mode. This is similar to ordinary daemon mode, but every
client gets its own process. Easier to set up than inetd mode, and without
the possible stability issues.
.IP "-i \fIaddress\fP"
Only useful when running in daemon mode, to specify the network interface
(identified by IP address) to which the daemon should attach. Use this if
you don't want BitlBee to listen on every interface (which is the default
behaviour).
.IP "-p \fIport number\fP"
Only useful when running in daemon mode, to specify the port number on
which BitlBee should listen for connections. 6667 is the default value.
.IP "-n"
Only useful when running in daemon mode. This option prevents BitlBee from
forking into the background.
.IP "-v"
Be more verbose. This only works together with the \fB-n\fP flag.
.IP "-c \fIpath to other configuration file\fP"
Use a different configuration file.
.IP "-d \fIpath to user settings directory\fP"
BitlBee normally saves every user's settings in \fB/var/lib/bitlbee/\fP. If
you want the settings to be stored somewhere else (for example, if you don't
have write permissions in the default location), use this option.
.IP "-h"
Show help information.
.SH COMMANDS
To get a complete list of commands, please use the \fBhelp commands\fP
command in the &bitlbee channel.
.SH "SEE ALSO"
.pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #00880
r: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * 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;
}