diff options
Diffstat (limited to 'protocols/jabber/jabber.c')
| -rw-r--r-- | protocols/jabber/jabber.c | 2595 | 
1 files changed, 304 insertions, 2291 deletions
| diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index e765a475..edad5dbd 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -1,2392 +1,405 @@ -/* -*- 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 - * - */ - -#ifndef _WIN32 -#include <sys/utsname.h> -#endif -#include <errno.h> +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Jabber module - Main file                                                * +*                                                                           * +*  Copyright 2006 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.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +#include <glib.h>  #include <string.h> -#include <stdlib.h> +#include <unistd.h> +#include <ctype.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_PORT_MIN 5220 -#define JABBER_PORT_MAX 5229 - -#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 */ - - -#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; -} +#include "ssl_client.h" +#include "xmltree.h" +#include "bitlbee.h" +#include "jabber.h" -static void gjab_recv(gjconn gjc) +static void jabber_init( account_t *acc )  { -	static char buf[4096]; -	int len; - -	if (!gjc || gjc->state == JCONN_STATE_OFF) -		return; +	set_t *s; -	if (gjc->ssl) -		len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1); -	else -		len = read(gjc->fd, buf, sizeof(buf) - 1); +	s = set_add( &acc->set, "port", JABBER_PORT_DEFAULT, set_eval_int, acc ); +	s->flags |= ACC_SET_OFFLINE_ONLY; -	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 || (len < 0 && (!sockerr_again() || gjc->ssl))) { -		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 gboolean jabber_callback(gpointer data, gint source, b_input_condition condition) -{ -	struct gaim_connection *gc = (struct gaim_connection *)data; -	struct jabber_data *jd = (struct jabber_data *)gc->proto_data; - -	gjab_recv(jd->gjc); +	s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); -	return TRUE; -} - -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 gboolean gjab_connected(gpointer data, gint source, b_input_condition 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 FALSE; -	} - -	jd = gc->proto_data; -	gjc = jd->gjc; - -	if (gjc->fd != source) -		gjc->fd = source; - -	if (source == -1) { -		STATE_EVT(JCONN_STATE_OFF) -		return FALSE; -	} - -	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 = b_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc); -	 -	return FALSE; -} - -static gboolean gjab_connected_ssl(gpointer data, void *source, b_input_condition cond) -{ -	struct gaim_connection *gc = data; -	struct jabber_data *jd; -	gjconn gjc; +	s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); +	s->flags |= ACC_SET_OFFLINE_ONLY; -	jd = gc->proto_data; -	gjc = jd->gjc; +	s = set_add( &acc->set, "resource_select", "priority", NULL, acc ); -	if (source == NULL) { -		STATE_EVT(JCONN_STATE_OFF) -		return FALSE; -	} +	s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); +	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; -	if (!g_slist_find(get_connections(), gc)) { -		ssl_disconnect(source); -		return FALSE; -	} +	s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); +	s->flags |= ACC_SET_OFFLINE_ONLY; -	return gjab_connected(data, gjc->fd, cond); +	s = set_add( &acc->set, "tls", "try", set_eval_tls, acc ); +	s->flags |= ACC_SET_OFFLINE_ONLY;  } -static void gjab_start(gjconn gjc) +static void jabber_login( account_t *acc )  { -	account_t *acc; -	int port = -1, ssl = 0; -	char *server = NULL; - -	if (!gjc || gjc->state != JCONN_STATE_OFF) -		return; - -	acc = GJ_GC(gjc)->acc; -	server = acc->server; -	port = set_getint(&acc->set, "port"); -	ssl = set_getbool(&acc->set, "ssl"); +	struct im_connection *ic = imcb_new( acc ); +	struct jabber_data *jd = g_new0( struct jabber_data, 1 ); +	struct ns_srv_reply *srv = NULL; +	char *connect_to, *s; -	if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { -		serv_got_crap(GJ_GC(gjc), "For security reasons, the Jabber port number must be in the %d-%d range.", JABBER_PORT_MIN, JABBER_PORT_MAX); -		STATE_EVT(JCONN_STATE_OFF) -		return; -	} +	jd->ic = ic; +	ic->proto_data = jd; -	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); +	jd->username = g_strdup( acc->user ); +	jd->server = strchr( jd->username, '@' ); -	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)); -	} -	 -	if (!acc->gc || (gjc->fd < 0)) { -		STATE_EVT(JCONN_STATE_OFF) +	if( jd->server == NULL ) +	{ +		imcb_error( ic, "Incomplete account name (format it like <username@jabberserver.name>)" ); +		imc_logout( ic, FALSE );  		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)) +	 +	/* So don't think of free()ing jd->server.. :-) */ +	*jd->server = 0; +	jd->server ++; +	 +	if( ( s = strchr( jd->server, '/' ) ) )  	{ -		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 +		*s = 0; +		set_setstr( &acc->set, "resource", s + 1 ); +		 +		/* Also remove the /resource from the original variable so we +		   won't have to do this again every time. */ +		s = strchr( acc->user, '/' ); +		*s = 0;  	} - -	return retval; -} - -static void jabber_handlemessage(gjconn gjc, jpacket p) -{ -	xmlnode y, xmlns, z; -	time_t time_sent = time(NULL); - -	char *from = NULL, *msg = NULL, *type = NULL; -	char m[BUF_LONG * 2]; - -	type = xmlnode_get_attrib(p->x, "type"); - -	z = xmlnode_get_firstchild(p->x); - -	while(z) +	 +	/* This code isn't really pretty. Backwards compatibility never is... */ +	s = acc->server; +	while( s )  	{ -	   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")) { -			/* do nothing */ -		} 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; -				 -				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); -			} -		} - -	} 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, "headline")) { -		char *subject, *body, *url; -		 -		y = xmlnode_get_tag( p->x, "body" ); -		body = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; -		 -		y = xmlnode_get_tag( p->x, "subject" ); -		subject = y ? g_strdup( xmlnode_get_data( y ) ) : NULL; +		static int had_port = 0; -		url = NULL; -		z = xmlnode_get_firstchild(p->x); -		while( z ) +		if( strncmp( s, "ssl", 3 ) == 0 )  		{ -			char *xtype = xmlnode_get_attrib( z, "xmlns" ); +			set_setstr( &acc->set, "ssl", "true" ); -			if( xtype && g_strcasecmp( xtype, "jabber:x:oob" ) == 0 && -			             ( y = xmlnode_get_tag( z, "url" ) ) ) -			{ -				url = g_strdup( xmlnode_get_data( y ) ); -				break; -			} +			/* Flush this part so that (if this was the first +			   part of the server string) acc->server gets +			   flushed. We don't want to have to do this another +			   time. :-) */ +			*s = 0; +			s ++; -			z = xmlnode_get_nextsibling( z ); +			/* Only set this if the user didn't specify a custom +			   port number already... */ +			if( !had_port ) +				set_setint( &acc->set, "port", 5223 );  		} -		 -		g_snprintf( m, BUF_LONG, "Subject: %s\nURL: %s\nMessage:\n%s", subject ? subject : "(none)", -		                     url ? url : "(none)", body ? body : "(none)" ); - -		if( p->from->user ) -			from = g_strdup_printf( "%s@%s", p->from->user, p->from->server ); -		else -			from = g_strdup( p->from->server ); -		 -		serv_got_im( GJ_GC(gjc), from, m, 0, time_sent, -1 ); -		 -		g_free( from ); -		g_free( subject ); -		g_free( body ); -		g_free( url ); -	} -} -	    -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), jap->user, 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 his/her 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); +		else if( isdigit( *s ) ) +		{ +			int i; +			 +			/* The first character is a digit. It could be an +			   IP address though. Only accept this as a port# +			   if there are only digits. */ +			for( i = 0; isdigit( s[i] ); i ++ ); +			 +			/* If the first non-digit character is a colon or +			   the end of the string, save the port number +			   where it should be. */ +			if( s[i] == ':' || s[i] == 0 ) +			{ +				sscanf( s, "%d", &i ); +				set_setint( &acc->set, "port", i ); -				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); -	} - -	account_online(GJ_GC(gjc)); -} - -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; +				/* See above. */ +				*s = 0; +				s ++;  			} -			gjab_auth(gjc); -		} else { -			gjab_reqroster(gjc); -			((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE; +			had_port = 1;  		} -	} 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")); +		 +		s = strchr( s, ':' ); +		if( s ) +		{ +			*s = 0; +			s ++;  		} - -		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]; +	jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free ); +	jd->buddies = g_hash_table_new( g_str_hash, g_str_equal ); -	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; +	/* Figure out the hostname to connect to. */ +	if( acc->server && *acc->server ) +		connect_to = acc->server; +	else if( ( srv = srv_lookup( "xmpp-client", "tcp", jd->server ) ) || +		 ( srv = srv_lookup( "jabber-client", "tcp", jd->server ) ) ) +		connect_to = srv->name; +	else +		connect_to = jd->server; +	 +	imcb_log( ic, "Connecting" ); +	 +	if( set_getint( &acc->set, "port" ) < JABBER_PORT_MIN || +	    set_getint( &acc->set, "port" ) > JABBER_PORT_MAX ) +	{ +		imcb_log( ic, "Incorrect port number, must be in the %d-%d range", +		               JABBER_PORT_MIN, JABBER_PORT_MAX ); +		imc_logout( ic, FALSE ); +		return;  	} - -	/* 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); -			} -		} +	 +	/* For non-SSL connections we can try to use the port # from the SRV +	   reply, but let's not do that when using SSL, SSL usually runs on +	   non-standard ports... */ +	if( set_getbool( &acc->set, "ssl" ) ) +	{ +		jd->ssl = ssl_connect( connect_to, set_getint( &acc->set, "port" ), jabber_connected_ssl, ic ); +		jd->fd = jd->ssl ? ssl_getfd( jd->ssl ) : -1;  	} - -	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; +	else +	{ +		jd->fd = proxy_connect( connect_to, srv ? srv->port : set_getint( &acc->set, "port" ), jabber_connected_plain, ic );  	} - -	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; +	g_free( srv ); +	 +	if( jd->fd == -1 ) +	{ +		imcb_error( ic, "Could not connect to server" ); +		imc_logout( ic, TRUE );  	} -	return;  } -static void jabber_acc_init(account_t *acc) +static void jabber_logout( struct im_connection *ic )  { -	set_t *s; +	struct jabber_data *jd = ic->proto_data; -	s = set_add( &acc->set, "port", "5222", set_eval_int, acc ); -	s->flags |= ACC_SET_OFFLINE_ONLY; +	jabber_end_stream( ic ); -	s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); -	s->flags |= ACC_SET_OFFLINE_ONLY; +	if( jd->r_inpa >= 0 ) +		b_event_remove( jd->r_inpa ); +	if( jd->w_inpa >= 0 ) +		b_event_remove( jd->w_inpa ); -	s = set_add( &acc->set, "server", NULL, set_eval_account, acc ); -	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY; +	if( jd->ssl ) +		ssl_disconnect( jd->ssl ); +	if( jd->fd >= 0 ) +		closesocket( jd->fd ); -	s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc ); -	s->flags |= ACC_SET_OFFLINE_ONLY; +	if( jd->tx_len ) +		g_free( jd->txq ); +	 +	g_hash_table_destroy( jd->node_cache ); +	 +	xt_free( jd->xt ); +	 +	g_free( jd->away_message ); +	g_free( jd->username ); +	g_free( jd );  } -static void jabber_login(account_t *acc) +static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message, int flags )  { -	struct gaim_connection *gc; -	struct jabber_data *jd; -	char *resource, *loginname; +	struct jabber_data *jd = ic->proto_data; +	struct jabber_buddy *bud; +	struct xt_node *node; +	int st; -	/* Time to move some data/things from the old syntax to the new one: */ -	if (acc->server) { -		char *s, *tmp_server; -		int port; -		 -		if (g_strcasecmp(acc->server, "ssl") == 0) { -			set_setstr(&acc->set, "server", ""); -			set_setint(&acc->set, "port", DEFAULT_PORT_SSL); -			set_setstr(&acc->set, "ssl", "true"); -			 -			g_free(acc->server); -			acc->server = NULL; -		} else if ((s = strchr(acc->server, ':'))) { -			if (strstr(acc->server, ":ssl")) { -				set_setint(&acc->set, "port", DEFAULT_PORT_SSL); -				set_setstr(&acc->set, "ssl", "true"); -			} -			if (isdigit(s[1])) { -				if (sscanf(s + 1, "%d", &port) == 1) -					set_setint(&acc->set, "port", port); -			} -			tmp_server = g_strndup(acc->server, s - acc->server); -			set_setstr(&acc->set, "server", tmp_server); -			g_free(tmp_server); -		} -	} +	bud = jabber_buddy_by_jid( ic, who, 0 ); -	gc = new_gaim_conn(acc); -	jd = gc->proto_data = g_new0(struct jabber_data, 1); +	node = xt_new_node( "body", message, NULL ); +	node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node ); -	if( strchr( acc->user, '@' ) == NULL ) +	if( bud && ( jd->flags & JFLAG_WANT_TYPING ) && +	    ( ( bud->flags & JBFLAG_DOES_XEP85 ) || +	     !( bud->flags & JBFLAG_PROBED_XEP85 ) ) )  	{ -		hide_login_progress( gc, "Invalid account name" ); -		signoff( gc ); -		return; +		struct xt_node *act; +		 +		/* If the user likes typing notification and if we don't know +		   (and didn't probe before) if this resource supports XEP85, +		   include a probe in this packet now. Also, if we know this +		   buddy does support XEP85, we have to send this <active/> +		   tag to tell that the user stopped typing (well, that's what +		   we guess when s/he pressed Enter...). */ +		act = xt_new_node( "active", NULL, NULL ); +		xt_add_attr( act, "xmlns", XMLNS_CHATSTATES ); +		xt_add_child( node, act ); +		 +		/* Just make sure we do this only once. */ +		bud->flags |= JBFLAG_PROBED_XEP85;  	} -	resource = set_getstr(&acc->set, "resource"); -	loginname = create_valid_jid(acc->user, DEFAULT_SERVER, resource); +	st = jabber_write_packet( ic, node ); +	xt_free_node( node ); -	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, acc->pass, 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, gint fd, b_input_condition cond) -{ -	struct jabber_data *jd = data; - -	if(jd->gjc != NULL) { -		gjab_delete(jd->gjc); -		/* YAY for modules with their own memory pool managers!... -		g_free(jd->gjc->sid); -		And a less sarcastic yay for valgrind. :-) */ -		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) -		b_event_remove(gc->inpa); - -	if(jd) { -		b_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; +	return st;  } -/* - * Add/update buddy's roster entry on server - */ -static void jabber_roster_update(struct gaim_connection *gc, char *name) +static GList *jabber_away_states( struct im_connection *ic )  { -	xmlnode x, y; -	char *realwho; -	gjconn gjc; -	struct buddy *buddy = NULL; -	/* struct group *buddy_group = NULL; */ +	static GList *l = NULL; +	int i; -	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); +	if( l == NULL ) +		for( i = 0; jabber_away_state_list[i].full_name; i ++ ) +			l = g_list_append( l, (void*) jabber_away_state_list[i].full_name ); +	 +	return l;  } -static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group) +static void jabber_get_info( struct im_connection *ic, char *who )  { -	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); +	struct jabber_data *jd = ic->proto_data; +	struct jabber_buddy *bud; +	 +	if( strchr( who, '/' ) ) +		bud = jabber_buddy_by_jid( ic, who, 0 );  	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); +	{ +		char *s = jabber_normalize( who ); +		bud = g_hash_table_lookup( jd->buddies, s ); +		g_free( s );  	} -	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"); +	while( bud ) +	{ +		imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s", +		                   bud->full_jid, bud->priority, +		                   bud->away_state ? bud->away_state->full_name : "(none)", +		                   bud->away_message ? : "(none)" ); +		bud = bud->next;  	} -	*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); +	jabber_get_vcard( ic, bud ? bud->full_jid : who );  } -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"); -			xmlnode_insert_cdata(y, message, -1); -			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_keepalive(struct gaim_connection *gc) { -	struct jabber_data *jd = (struct jabber_data *)gc->proto_data; -	gjab_send_raw(jd->gjc, "  \t  "); -} - -/*---------------------------------------*/ -/* 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) +static void jabber_set_away( struct im_connection *ic, char *state_txt, char *message )  { -	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; -	} +	struct jabber_data *jd = ic->proto_data; +	struct jabber_away_state *state; +	 +	/* Save all this info. We need it, for example, when changing the priority setting. */ +	state = (void *) jabber_away_state_by_name( state_txt ); +	jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */ +	g_free( jd->away_message ); +	jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL; +	 +	presence_send_update( ic );  } -/* - * Parse an XML-encoded string into an xmlnode tree - * - * Caller is responsible for freeing the returned xmlnode - */ -static xmlnode xmlstr2xmlnode(char *xmlstring) +static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )  { -	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); +	if( jabber_add_to_roster( ic, who, NULL ) ) +		presence_send_request( ic, who, "subscribe" );  } -/* - * 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) +static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group )  { -	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)); +	/* We should always do this part. Clean up our administration a little bit. */ +	jabber_buddy_remove_bare( ic, who ); +	 +	if( jabber_remove_from_roster( ic, who ) ) +		presence_send_request( ic, who, "unsubscribe" );  } -/* - * Send vCard info to Jabber server - */ -static void jabber_set_info(struct gaim_connection *gc, char *info) +static void jabber_keepalive( struct im_connection *ic )  { -	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); +	/* Just any whitespace character is enough as a keepalive for XMPP sessions. */ +	jabber_write( ic, "\n", 1 ); -	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); +	/* This runs the garbage collection every minute, which means every packet +	   is in the cache for about a minute (which should be enough AFAIK). */ +	jabber_cache_clean( ic );  } -/* - * displays a Jabber vCard - */ -static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) +static int jabber_send_typing( struct im_connection *ic, char *who, int typing )  { -	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; +	struct jabber_data *jd = ic->proto_data; +	struct jabber_buddy *bud; -	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"); +	/* Enable typing notification related code from now. */ +	jd->flags |= JFLAG_WANT_TYPING; +	 +	if( ( bud = jabber_buddy_by_jid( ic, who, 0 ) ) == NULL ) +	{ +		/* Sending typing notifications to unknown buddies is +		   unsupported for now. Shouldn't be a problem, I think. */ +		return 0;  	} - -	g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"), -	                       status); - -	for(child = querynode->firstchild; child; child = child->next) +	 +	if( bud->flags & JBFLAG_DOES_XEP85 )  	{ -		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")); -		} +		/* We're only allowed to send this stuff if we know the other +		   side supports it. */ +		 +		struct xt_node *node; +		char *type; +		int st; +		 +		if( typing & OPT_TYPING ) +			type = "composing"; +		else if( typing & OPT_THINKING ) +			type = "paused"; +		else +			type = "active"; +		 +		node = xt_new_node( type, NULL, NULL ); +		xt_add_attr( node, "xmlns", XMLNS_CHATSTATES ); +		node = jabber_make_packet( "message", "chat", bud->full_jid, node ); +		 +		st = jabber_write_packet( ic, node ); +		xt_free_node( node ); +		 +		return st;  	} - -	serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str); - -	g_free(buddy); -	g_string_free(str, TRUE); +	 +	return 1;  } -void jabber_init() +void jabber_initmodule()  { -	struct prpl *ret = g_new0(struct prpl, 1); - +	struct prpl *ret = g_new0( struct prpl, 1 ); +	  	ret->name = "jabber"; -	ret->away_states = jabber_away_states; -	ret->acc_init = jabber_acc_init;  	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->init = jabber_init; +	ret->logout = jabber_logout; +	ret->buddy_msg = jabber_buddy_msg; +	ret->away_states = jabber_away_states; +//	ret->get_status_string = jabber_get_status_string;  	ret->set_away = jabber_set_away; -	ret->get_away = jabber_get_away_msg; +//	ret->set_info = jabber_set_info; +	ret->get_info = jabber_get_info;  	ret->add_buddy = jabber_add_buddy;  	ret->remove_buddy = jabber_remove_buddy; +//	ret->chat_msg = jabber_chat_msg; +//	ret->chat_invite = jabber_chat_invite; +//	ret->chat_leave = jabber_chat_leave; +//	ret->chat_open = jabber_chat_open;  	ret->keepalive = jabber_keepalive; -	ret->alias_buddy = jabber_roster_update; -	ret->group_buddy = jabber_group_change; +	ret->send_typing = jabber_send_typing;  	ret->handle_cmp = g_strcasecmp; -	register_protocol (ret); +	register_protocol( ret );  } | 
