changeset 11477:36f575351c49

[gaim-migrate @ 13719] Commit the Bonjour code from oldstatus to HEAD. It hasn't been updated for the new status code yet. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Fri, 09 Sep 2005 12:34:27 +0000
parents 5d3f8d9e8f92
children 67abb500f600
files src/protocols/bonjour/Makefile.am src/protocols/bonjour/Makefile.mingw src/protocols/bonjour/bonjour.c src/protocols/bonjour/bonjour.h src/protocols/bonjour/buddy.c src/protocols/bonjour/buddy.h src/protocols/bonjour/dns_sd.c src/protocols/bonjour/dns_sd.h src/protocols/bonjour/issues.txt src/protocols/bonjour/jabber.c src/protocols/bonjour/jabber.h src/protocols/bonjour/messages.txt
diffstat 12 files changed, 1928 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/Makefile.am	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,36 @@
+EXTRA_DIST = \
+		Makefile.mingw
+
+pkgdir = $(libdir)/gaim
+
+BONJOURSOURCES = \
+	bonjour.c \
+	buddy.c \
+	dns_sd.c \
+	jabber.c
+
+AM_CFLAGS = $(st)
+
+libbonjour_la_LDFLAGS = -module -avoid-version $(GLIB_LIBS) $(HOWL_LIBS)
+
+if STATIC_BONJOUR
+
+st = -DGAIM_STATIC_PRPL
+noinst_LIBRARIES     = libbonjour.a
+libbonjour_a_SOURCES = $(BONJOURSOURCES)
+libbonjour_a_CFLAGS  = $(AM_CFLAGS)
+
+else
+
+st =
+pkg_LTLIBRARIES       = libbonjour.la
+libbonjour_la_SOURCES = $(BONJOURSOURCES)
+
+endif
+
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	$(GLIB_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(HOWL_CFLAGS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/Makefile.mingw	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,133 @@
+#
+# Makefile.mingw
+#
+# Description: Makefile for win32 (mingw) version of libbonjour
+#
+
+#
+# PATHS
+#
+
+INCLUDE_DIR :=		.
+GTK_TOP :=		../../../../win32-dev/gtk_2_0
+GAIM_TOP :=		../../..
+BONJOUR_ROOT :=		.
+GAIM_INSTALL_DIR :=	$(GAIM_TOP)/win32-install-dir
+
+##
+## VARIABLE DEFINITIONS
+##
+
+TARGET = libbonjour
+
+# Compiler Options
+
+CFLAGS =
+
+DEFINES =
+
+# Static or Plugin... 
+ifeq ($(TYPE),STATIC)
+  DEFINES += -DSTATIC
+  DLL_INSTALL_DIR =	$(GAIM_INSTALL_DIR)
+else
+ifeq ($(TYPE),PLUGIN)
+  DLL_INSTALL_DIR =	$(GAIM_INSTALL_DIR)/plugins
+endif
+endif
+
+
+##
+## INCLUDE  MAKEFILES
+##
+
+include $(GAIM_TOP)/src/win32/global.mak
+
+##
+## INCLUDE PATHS
+##
+
+INCLUDE_PATHS +=	-I$(BONJOUR_ROOT) \
+			-I$(GTK_TOP)/include \
+			-I$(GTK_TOP)/include/gtk-2.0 \
+			-I$(GTK_TOP)/include/glib-2.0 \
+			-I$(GTK_TOP)/include/pango-1.0 \
+			-I$(GTK_TOP)/include/atk-1.0 \
+			-I$(GTK_TOP)/lib/glib-2.0/include \
+			-I$(GTK_TOP)/lib/gtk-2.0/include \
+			-I$(GAIM_TOP)/src \
+			-I$(GAIM_TOP)/src/win32 \
+			-I$(GAIM_TOP)
+
+
+LIB_PATHS =		-L$(GTK_TOP)/lib \
+			-L$(GAIM_TOP)/src
+
+
+##
+##  SOURCES, OBJECTS
+##
+
+C_SRC =			rendezvous.c
+
+
+OBJECTS = $(C_SRC:%.c=%.o)
+
+
+##
+## LIBRARIES
+##
+
+LIBS =			-lgtk-win32-2.0 \
+			-lglib-2.0 \
+			-lgdk-win32-2.0 \
+			-lgmodule-2.0 \
+			-lgobject-2.0 \
+			-lws2_32 \
+			-lintl \
+			-lgaim
+
+
+##
+## RULES
+##
+
+# How to make a C file
+
+%.o: %.c
+	$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@ -c $<
+
+##
+## TARGET DEFINITIONS
+##
+
+.PHONY: all clean
+
+all: $(TARGET).dll
+
+install:
+	cp $(BONJOUR_ROOT)/$(TARGET).dll $(DLL_INSTALL_DIR)
+
+
+##
+## BUILD Dependencies
+##
+
+$(GAIM_TOP)/src/gaim.lib:
+	$(MAKE) -C $(GAIM_TOP)/src -f Makefile.mingw gaim.lib
+
+##
+## BUILD DLL
+##
+
+$(TARGET).dll: $(OBJECTS) $(GAIM_TOP)/src/gaim.lib
+	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--out-implib,$(TARGET).lib -o $(TARGET).dll
+
+##
+## CLEAN RULES
+##
+
+clean:
+	rm -rf *.o
+	rm -rf $(TARGET).dll
+	rm -rf $(TARGET).lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/bonjour.c	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,369 @@
+/*
+ * gaim - Bonjour Protocol Plugin
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <glib.h>
+
+#include "internal.h"
+#include "account.h"
+#include "accountopt.h"
+#include "version.h"
+#include "debug.h"
+
+#include "bonjour.h"
+#include "dns_sd.h"
+#include "jabber.h"
+#include "buddy.h"
+
+void bonjour_login(GaimAccount* account)
+{
+	GaimConnection *gc = gaim_account_get_connection(account);
+	GaimGroup* bonjour_group = NULL;
+	BonjourData* bd = NULL;
+	
+	gc->flags |= GAIM_CONNECTION_HTML;
+	gc->proto_data = g_new(BonjourData, 1);
+	bd = gc->proto_data;
+	
+	// Start waiting for jabber connections (iChat style)
+	bd->jabber_data = g_new(BonjourJabber, 1);
+	bd->jabber_data->name = gc->account->username;
+	bd->jabber_data->port = gaim_account_get_int(account, "port", BONJOUR_DEFAULT_PORT_INT);
+	bd->jabber_data->account = account;
+	
+	if (bonjour_jabber_start(bd->jabber_data) == -1) {
+		// Send a message about the connection error
+		gaim_debug_error("bonjour", "Unable to listen to ichat connections");
+		
+		// Free the data
+		g_free(bd->jabber_data);
+		g_free(bd);
+		return;
+	}
+	
+	// Connect to the mDNS daemon looking for buddies in the LAN
+	bd->dns_sd_data = bonjour_dns_sd_new();
+	bd->dns_sd_data->name = (sw_string)gaim_account_get_username(account);
+	bd->dns_sd_data->txtvers = g_strdup("1");
+	bd->dns_sd_data->version = g_strdup("1");
+	bd->dns_sd_data->first = gaim_account_get_string(account, "first", "Juanjo");
+	bd->dns_sd_data->last = gaim_account_get_string(account, "last", "");
+	bd->dns_sd_data->port_p2pj = gaim_account_get_int(account, "port", BONJOUR_DEFAULT_PORT_INT);
+	bd->dns_sd_data->phsh = g_strdup("");
+	bd->dns_sd_data->status = g_strdup("avail"); //<-- Check the real status if different from avail
+	bd->dns_sd_data->email = gaim_account_get_string(account, "email", "");
+	bd->dns_sd_data->vc = g_strdup("");
+	bd->dns_sd_data->jid = g_strdup("");
+	bd->dns_sd_data->AIM = g_strdup("");
+	bd->dns_sd_data->msg = g_strdup(gc->away);
+	
+	bd->dns_sd_data->account = account;
+	bonjour_dns_sd_start(bd->dns_sd_data);
+	
+	// Create a group for bonjour buddies
+	bonjour_group = gaim_group_new(BONJOUR_GROUP_NAME);
+	gaim_blist_add_group(bonjour_group, NULL);
+	
+	// Show the buddy list by telling Gaim we have already connected
+	gaim_connection_set_state(gc, GAIM_CONNECTED);
+}
+
+void bonjour_close(GaimConnection* connection)
+{
+	GaimGroup* bonjour_group = gaim_find_group(BONJOUR_GROUP_NAME);
+	GSList* buddies;
+	GSList* l;
+	BonjourData* bd = (BonjourData*)connection->proto_data;
+	
+	// Stop looking for buddies in the LAN
+	if (connection != NULL) {
+		bonjour_dns_sd_stop(bd->dns_sd_data);
+		if (bd != NULL) {
+			bonjour_dns_sd_free(bd->dns_sd_data);
+		}
+	}
+	
+	// Stop waiting for conversations
+	bonjour_jabber_stop(bd->jabber_data);
+	g_free(bd->jabber_data);
+	
+	// Remove all the bonjour buddies
+	if(connection != NULL){
+		buddies = gaim_find_buddies(connection->account, connection->account->username);
+		for(l = buddies; l; l = l->next){
+			bonjour_buddy_delete(((GaimBuddy*)(l->data))->proto_data);
+			gaim_blist_remove_buddy(l->data);
+		}
+		g_slist_free(buddies);
+	}
+	
+	// Delete the bonjour group
+	gaim_blist_remove_group(bonjour_group);
+	
+}
+
+const char* bonjour_list_icon(GaimAccount* account, GaimBuddy* buddy)
+{
+	return BONJOUR_ICON_NAME;
+}
+
+int bonjour_send_im(GaimConnection* connection, const char* to, const char* msg, GaimConvImFlags flags)
+{
+	if(!to || !msg)
+		return 0;
+		
+	bonjour_jabber_send_message(((BonjourData*)(connection->proto_data))->jabber_data, to, msg);
+		
+	return 1;
+}
+
+void bonjour_set_status(GaimConnection* connection, const char* state, const char* message)
+{
+	char* status_dns_sd = NULL;
+	char* stripped = NULL;
+
+	if(message) {
+		stripped = g_strdup(message);
+	} else {
+		stripped = g_strdup("");
+	}
+
+	if(connection->away){
+		g_free(connection->away);
+	}
+	connection->away = stripped;
+	
+	if (g_ascii_strcasecmp(state, _("Online")) == 0) {
+		status_dns_sd = g_strdup("avail");
+	} else if (g_ascii_strcasecmp(state, _("Away")) == 0) {
+		status_dns_sd = g_strdup("away");
+	} else if (g_ascii_strcasecmp(state, _("Do Not Disturb")) == 0) {
+		status_dns_sd = g_strdup("dnd");
+	} else if (g_ascii_strcasecmp(state, _("Custom")) == 0) {
+		status_dns_sd = g_strdup("away");
+	}
+
+	if (status_dns_sd != NULL) {
+		bonjour_dns_sd_send_status(((BonjourData*)(connection->proto_data))->dns_sd_data, 
+			status_dns_sd, stripped);
+	}
+}
+
+static GList* bonjour_status_types(GaimConnection* connection)
+{
+	GList *types = NULL;
+
+	types = g_list_append(types, _("Online"));
+	types = g_list_append(types, _("Away"));
+	types = g_list_append(types, _("Do Not Disturb"));
+	types = g_list_append(types, GAIM_AWAY_CUSTOM);
+
+	return types;
+}
+
+static void bonjour_convo_closed(GaimConnection* connection, const char* who)
+{
+	GaimBuddy* buddy = gaim_find_buddy(connection->account, who);
+	
+	bonjour_jabber_close_conversation(((BonjourData*)(connection->proto_data))->jabber_data, buddy);
+}
+
+static void bonjour_list_emblems(GaimBuddy *b, char **se, char **sw,
+		char **nw, char **ne)
+{
+	switch (b->uc) {
+		case BONJOUR_STATE_AWAY:
+			*se = "away";
+			break;
+		case BONJOUR_STATE_DND:
+			*se = "dnd";
+			break;
+		case BONJOUR_STATE_ERROR:
+			*se = "error";
+			break;
+	}
+}
+
+static char* bonjour_status_text(GaimBuddy *b)
+{
+	BonjourBuddy* bb = (BonjourBuddy*)b->proto_data;
+	
+	if (bb->msg != NULL) {
+		return g_strdup(bb->msg);
+	} else {
+		return g_strdup("");
+	}
+}
+
+static char* bonjour_tooltip_text(GaimBuddy *b)
+{
+	char* status = NULL;
+	
+	switch (b->uc) {
+		case BONJOUR_STATE_AVAILABLE:
+			status = g_strdup(_("Online"));
+			break;
+		case BONJOUR_STATE_AWAY:
+			status = g_strdup(_("Away"));
+			break;
+		case BONJOUR_STATE_DND:
+			status = g_strdup(_("Do Not Disturb"));
+			break;
+		case BONJOUR_STATE_ERROR:
+			status = g_strdup("Error");
+			break;
+	}
+	
+	return g_strconcat("\n<b>Status: </b>", status, NULL);
+}
+
+static GaimPlugin *my_protocol = NULL;
+
+static GaimPluginProtocolInfo prpl_info =
+{
+	OPT_PROTO_NO_PASSWORD,
+	NULL,                                                    /* user_splits */
+	NULL,                                                    /* protocol_options */
+	{"png", 0, 0, 96, 96, GAIM_ICON_SCALE_DISPLAY},          /* icon_spec */
+	bonjour_list_icon,                                       /* list_icon */
+	bonjour_list_emblems,                                    /* list_emblems */
+	bonjour_status_text,                                     /* status_text */
+	bonjour_tooltip_text,                                    /* tooltip_text */
+	bonjour_status_types,                                    /* status_types */
+	NULL,                                                    /* blist_node_menu */
+	NULL,                                                    /* chat_info */
+	NULL,                                                    /* chat_info_defaults */
+	bonjour_login,                                           /* login */
+	bonjour_close,                                           /* close */
+	bonjour_send_im,                                         /* send_im */
+	NULL,                                                    /* set_info */
+	NULL,                                                    /* send_typing */
+	NULL,                                                    /* get_info */
+	bonjour_set_status,                                      /* set_status */
+	NULL,                                                    /* set_idle */
+	NULL,                                                    /* change_passwd */
+	NULL,                                                    /* add_buddy */
+	NULL,                                                    /* add_buddies */
+	NULL,                                                    /* remove_buddy */
+	NULL,                                                    /* remove_buddies */
+	NULL,                                                    /* add_permit */
+	NULL,                                                    /* add_deny */
+	NULL,                                                    /* rem_permit */
+	NULL,                                                    /* rem_deny */
+	NULL,                                                    /* set_permit_deny */
+	NULL,                                                    /* warn */
+	NULL,                                                    /* join_chat */
+	NULL,                                                    /* reject_chat */
+	NULL,                                                    /* get_chat_name */
+	NULL,                                                    /* chat_invite */
+	NULL,                                                    /* chat_leave */
+	NULL,                                                    /* chat_whisper */
+	NULL,                                                    /* chat_send */
+	NULL,                                                    /* keepalive */
+	NULL,                                                    /* register_user */
+	NULL,                                                    /* get_cb_info */
+	NULL,                                                    /* get_cb_away */
+	NULL,                                                    /* alias_buddy */
+	NULL,                                                    /* group_buddy */
+	NULL,                                                    /* rename_group */
+	NULL,                                                    /* buddy_free */
+	bonjour_convo_closed,                                    /* convo_closed */
+	NULL,                                                    /* normalize */
+	NULL,                                                    /* set_buddy_icon */
+	NULL,                                                    /* remove_group */
+	NULL,                                                    /* get_cb_real_name */
+	NULL,                                                    /* set_chat_topic */
+	NULL,                                                    /* find_blist_chat */
+	NULL,                                                    /* roomlist_get_list */
+	NULL,                                                    /* roomlist_cancel */
+	NULL,                                                    /* roomlist_expand_category */
+	NULL,                                                    /* can_receive_file */
+	NULL                                                     /* send_file */
+};
+
+static GaimPluginInfo info =
+{
+	GAIM_PLUGIN_MAGIC,
+	GAIM_MAJOR_VERSION,
+	GAIM_MINOR_VERSION,
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-bonjour",                                   /**< id             */
+	"Bonjour",                                        /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Bonjour Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Bonjour Protocol Plugin"),
+	NULL,                                             /**< author         */
+	GAIM_WEBSITE,                                     /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info,                                       /**< extra_info     */
+	NULL,                                             /**< prefs_info     */
+	NULL
+};
+
+static void
+init_plugin(GaimPlugin *plugin)
+{
+	GaimAccountUserSplit *split;
+	GaimAccountOption *option;
+	char hostname[255];
+
+	if (gethostname(hostname, 255) != 0) {
+		gaim_debug_warning("rendezvous", "Error %d when getting host name.  Using \"localhost.\"\n", errno);
+		strcpy(hostname, "localhost");
+	}
+
+	// Creating the user splits
+	split = gaim_account_user_split_new(_("Host name"), hostname, '@');
+	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
+	
+	// Creating the options for the protocol
+	option = gaim_account_option_int_new(_("Port"), "port", 5298);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = gaim_account_option_string_new(_("First name"), "first", "Gaim");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = gaim_account_option_string_new(_("Last name"), "last", "User");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = gaim_account_option_string_new(_("Email"), "email", "");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+	
+	/*
+	option = gaim_account_option_string_new(_("Status Message"), "message", "Available");
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+	*/
+	
+	my_protocol = plugin;
+}
+
+GAIM_INIT_PLUGIN(bonjour, init_plugin, info);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/bonjour.h	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,48 @@
+/**
+ * @file bonjour.h The Gaim interface to mDNS and peer to peer Jabber.
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 _BONJOUR_H_
+#define _BONJOUR_H_
+
+#include <howl.h>
+
+#include "dns_sd.h"
+#include "jabber.h"
+
+#define BONJOUR_GROUP_NAME "Bonjour"
+#define BONJOUR_PROTOCOL_NAME "bonjour"
+#define BONJOUR_ICON_NAME "bonjour"
+
+#define BONJOUR_STATE_AWAY  		(0x02 | UC_UNAVAILABLE)
+#define BONJOUR_STATE_AVAILABLE  	(0x04)
+#define BONJOUR_STATE_DND   		(0x10 | UC_UNAVAILABLE)
+#define BONJOUR_STATE_ERROR 		(0x20 | UC_UNAVAILABLE)
+
+typedef struct _bonjour_data{
+	BonjourDnsSd* dns_sd_data;
+	BonjourJabber* jabber_data;
+}BonjourData;
+
+#endif /* _BONJOUR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/buddy.c	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,147 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+
+#include <glib.h>
+#include <stdlib.h>
+
+#include "buddy.h"
+#include "account.h"
+#include "blist.h"
+#include "bonjour.h"
+#include "debug.h"
+
+/**
+ * Creates a new buddy.
+ */
+BonjourBuddy* bonjour_buddy_new(gchar* name, gchar* first, gint port_p2pj,
+	gchar* phsh, gchar* status, gchar* email, gchar* last, gchar* jid, gchar* AIM,
+	gchar* vc, gchar* ip, gchar* msg)
+{
+	BonjourBuddy* buddy = malloc(sizeof(BonjourBuddy));
+
+	buddy->name = g_strdup(name);
+	buddy->first = g_strdup(first);
+	buddy->port_p2pj = port_p2pj;
+	buddy->phsh = g_strdup(phsh);
+	buddy->status = g_strdup(status);
+	buddy->email = g_strdup(email);
+	buddy->last = g_strdup(last);
+	buddy->jid = g_strdup(jid);
+	buddy->AIM = g_strdup(AIM);
+	buddy->vc = g_strdup(vc);
+	buddy->ip = g_strdup(ip);
+	buddy->msg = g_strdup(msg);
+	buddy->conversation = NULL;
+	
+	return buddy;
+}
+
+/**
+ * Check if all the compulsory buddy data is present.
+ */
+gboolean bonjour_buddy_check(BonjourBuddy* buddy)
+{
+	if(buddy->name == NULL){
+		return FALSE;
+	}
+	
+	if(buddy->first == NULL){
+		return FALSE;
+	}
+	
+	if(buddy->last == NULL){
+		return FALSE;
+	}
+	
+	if(buddy->port_p2pj == -1){
+		return FALSE;
+	}
+	
+	if(buddy->status == NULL){
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+/**
+ * If the buddy doesn't previoulsy exists, it is created. Else, its data is changed (???)
+ */
+void bonjour_buddy_add_to_gaim(BonjourBuddy* buddy, GaimAccount* account)
+{
+	GaimBuddy* gb = gaim_find_buddy(account, buddy->name);
+	GaimGroup* bonjour_group = gaim_find_group(BONJOUR_GROUP_NAME);
+	gchar* buddy_alias = NULL;
+	gint buddy_status;
+
+	// Create the alias for the buddy using the first and the last name
+	buddy_alias = g_strconcat(buddy->first, " ", buddy->last, NULL);
+
+	// Transformation between the bonjour status and Gaim status
+	if (g_ascii_strcasecmp("avail", buddy->status) == 0) {
+		buddy_status = BONJOUR_STATE_AVAILABLE;
+	} else if (g_ascii_strcasecmp("away", buddy->status) == 0) {
+		buddy_status = BONJOUR_STATE_AWAY;
+	} else if (g_ascii_strcasecmp("dnd", buddy->status) == 0) {
+		buddy_status = BONJOUR_STATE_DND;
+	} else {
+		buddy_status = BONJOUR_STATE_ERROR;
+	}
+	
+	if (gb != NULL) {
+		// The buddy already exists
+		serv_got_update(account->gc, gb->name, TRUE, gb->evil, gb->signon, gb->idle, buddy_status);
+	} else {
+		// We have to create the buddy
+		gb = gaim_buddy_new(account, buddy->name, buddy_alias);
+		gb->node.flags = GAIM_BLIST_NODE_FLAG_NO_SAVE;
+		gb->proto_data = buddy;
+		gaim_blist_add_buddy(gb, NULL, bonjour_group, NULL);
+		gaim_blist_server_alias_buddy(gb, buddy_alias);
+		gaim_blist_update_buddy_status(gb, buddy_status);
+		gaim_blist_update_buddy_presence(gb, TRUE);
+		gaim_blist_update_buddy_signon(gb, 0);
+		gaim_blist_update_buddy_idle(gb, 0);
+		gaim_blist_update_buddy_evil(gb, 0);
+		g_free(buddy_alias);
+	}
+}
+
+/**
+ * Deletes a buddy from memory.
+ */
+void bonjour_buddy_delete(BonjourBuddy* buddy)
+{
+	g_free(buddy->name);
+	g_free(buddy->first);
+	g_free(buddy->phsh);
+	g_free(buddy->status);
+	g_free(buddy->email);
+	g_free(buddy->last);
+	g_free(buddy->jid);
+	g_free(buddy->AIM);
+	g_free(buddy->vc);
+	g_free(buddy->ip);
+	g_free(buddy->msg);
+	
+	if (buddy->conversation != NULL) {
+		g_free(buddy->conversation->buddy_name);
+		g_free(buddy->conversation);
+	}
+	
+	free(buddy);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/buddy.h	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,64 @@
+/*
+ *  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 Library 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 _BONJOUR_BUDDY
+#define _BONJOUR_BUDDY
+
+#include <howl.h>
+#include <glib.h>
+
+#include "account.h"
+#include "jabber.h"
+
+typedef struct _bonjour_buddy{
+	gchar* name;
+	gchar* first;
+	gint port_p2pj;
+	gchar* phsh;
+	gchar* status;
+	gchar* email;
+	gchar* last;
+	gchar* jid;
+	gchar* AIM;
+	gchar* vc;
+	gchar* ip;
+	gchar* msg;
+	BonjourJabberConversation* conversation;
+}BonjourBuddy;
+
+/**
+ * Creates a new buddy.
+ */
+BonjourBuddy* bonjour_buddy_new(gchar* name, gchar* first, gint port_p2pj,
+	gchar* phsh, gchar* status, gchar* email, gchar* last, gchar* jid, gchar* AIM,
+	gchar* vc, gchar* ip, gchar* msg);
+
+/**
+ * Check if all the compulsory buddy data is present.
+ */
+gboolean bonjour_buddy_check(BonjourBuddy* buddy);
+
+/**
+ * If the buddy doesn't previoulsy exists, it is created. Else, its data is changed (???)
+ */
+void bonjour_buddy_add_to_gaim(BonjourBuddy* buddy, GaimAccount* account);
+ 
+/**
+ * Deletes a buddy from memory.
+ */
+void bonjour_buddy_delete(BonjourBuddy* buddy);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/dns_sd.c	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,369 @@
+/*
+ *  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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#include "dns_sd.h"
+#include "bonjour.h"
+#include "buddy.h"
+#include "debug.h"
+
+// Private data
+
+typedef struct _dns_sd_packet{
+	gchar* name;
+	gchar* txtvers;
+	gchar* version;
+	gchar* first;
+	gchar* last;
+	gint port_p2pj;
+	gchar* phsh;
+	gchar* status;
+	gchar* message;
+	gchar* email;
+	gchar* vc;
+	gchar* jid;
+	gchar* AIM;
+}dns_sd_packet;
+
+// End private data
+
+// Private functions
+
+static sw_result HOWL_API _publish_reply(sw_discovery discovery, 
+	sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra)
+{
+gaim_debug_warning("bonjour", "_publish_reply --> Start\n");
+	// Check the answer from the mDNS daemon
+	switch(status){
+		case SW_DISCOVERY_PUBLISH_STARTED :
+			gaim_debug_info("bonjour", "_publish_reply --> Service started\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_STOPPED :
+			gaim_debug_info("bonjour", "_publish_reply --> Service stopped\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_NAME_COLLISION :
+			gaim_debug_info("bonjour", "_publish_reply --> Name collision\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_INVALID :
+			gaim_debug_info("bonjour", "_publish_reply --> Service invalid\n");
+			break;
+	}
+	
+	return SW_OKAY;
+}
+
+static sw_result HOWL_API _resolve_reply(sw_discovery discovery, 
+	sw_discovery_oid oid, sw_uint32 interface_index, sw_const_string name, 
+	sw_const_string type, sw_const_string domain, sw_ipv4_address address, 
+	sw_port port, sw_octets text_record, sw_ulong text_record_len, 
+	sw_opaque extra)
+{
+	BonjourBuddy* buddy;
+	GaimAccount* account = (GaimAccount*)extra;
+	gchar* txtvers = NULL;
+	gchar* version = NULL;
+	gchar* first = NULL;
+	gint port_p2pj = -1;
+	gchar* phsh = NULL;
+	gchar* status = NULL;
+	gchar* email = NULL;
+	gchar* last = NULL;
+	gchar* jid = NULL;
+	gchar* AIM = NULL;
+	gchar* vc = NULL;
+	gchar* msg = NULL;
+	gint address_length = 16;
+	gchar* ip = NULL;
+	sw_text_record_iterator iterator;
+	sw_int8 key[SW_TEXT_RECORD_MAX_LEN];
+	sw_int8 value[SW_TEXT_RECORD_MAX_LEN];
+	sw_uint32 value_length;
+
+	sw_discovery_cancel(discovery, oid);
+	
+	// Get the ip as a string
+	ip = malloc(address_length);
+	sw_ipv4_address_name(address, ip, address_length);
+
+	// Obtain the parameters from the text_record
+	if ((text_record_len > 0) && (text_record) && (*text_record != '\0')) {
+		sw_text_record_iterator_init(&iterator, text_record, text_record_len);
+		while (sw_text_record_iterator_next(iterator, key, value, &value_length) == SW_OKAY) {
+			// Compare the keys with the possible ones and save them on 
+			// the appropiate place of the buddy_list
+			if (strcmp(key, "txtvers") == 0) {
+				txtvers = g_strdup(value);
+			} else if (strcmp(key, "version") == 0) {
+				version = g_strdup(value);
+			} else if (strcmp(key, "1st") == 0) {
+				first = g_strdup(value);
+			} else if (strcmp(key, "port.p2pj") == 0) {
+				port_p2pj = atoi(value);
+			} else if (strcmp(key, "status") == 0) {
+				status = g_strdup(value);
+			} else if (strcmp(key, "email") == 0) {
+				email = g_strdup(value);
+			} else if (strcmp(key, "last") == 0) {
+				last = g_strdup(value);
+			} else if (strcmp(key, "jid") == 0) {
+				jid = g_strdup(value);
+			} else if (strcmp(key, "AIM") == 0) {
+				AIM = g_strdup(value);
+			} else if (strcmp(key, "vc") == 0) {
+				vc = g_strdup(value);
+			} else if (strcmp(key, "phsh") == 0) {
+				phsh = g_strdup(value);
+			} else if (strcmp(key, "msg") == 0) {
+				msg = g_strdup(value);
+			}
+		}
+	}
+
+	// Put the parameters of the text_record in a buddy and add the buddy to 
+	// the buddy list
+	buddy = bonjour_buddy_new((gchar*)name, first, port_p2pj, phsh, status, email,
+		last, jid, AIM, vc, ip, msg);
+		
+	if (bonjour_buddy_check(buddy) == FALSE) {
+		return SW_DISCOVERY_E_UNKNOWN;
+	}
+	
+	// Look for the buddy within the buddy list, if exist, change the status information, 
+	// else create a new buddy within the group Bonjour
+	bonjour_buddy_add_to_gaim(buddy, account);
+	
+	// Free all the temporal strings
+	g_free(txtvers);
+	g_free(version);
+	g_free(first);
+	g_free(last);
+	g_free(status);
+	g_free(email);
+	g_free(jid);
+	g_free(AIM);
+	g_free(vc);
+	g_free(phsh);
+	g_free(msg);
+	
+	return SW_OKAY;
+}
+
+static sw_result HOWL_API _browser_reply(sw_discovery discovery, sw_discovery_oid oid, 
+			sw_discovery_browse_status status, sw_uint32 interface_index, sw_const_string name, 
+			sw_const_string type, sw_const_string domain, sw_opaque_t extra)
+{
+	sw_discovery_resolve_id rid;
+	GaimAccount* account = (GaimAccount*)extra;
+	GaimBuddy* gb = NULL;
+	
+	switch(status){
+		case SW_DISCOVERY_BROWSE_INVALID:
+			gaim_debug_warning("bonjour", "_browser_reply --> Invalid\n");
+			break;
+		case SW_DISCOVERY_BROWSE_RELEASE:
+			gaim_debug_warning("bonjour", "_browser_reply --> Release\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_DOMAIN:
+			gaim_debug_warning("bonjour", "_browser_reply --> Add domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN:
+			gaim_debug_warning("bonjour", "_browser_reply --> Add default domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN:
+			gaim_debug_warning("bonjour", "_browser_reply --> Remove domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_SERVICE:
+			// A new peer has join the network and uses iChat bonjour
+			gaim_debug_info("bonjour", "_browser_reply --> Add service\n");
+			if (g_ascii_strcasecmp(name, account->username) != 0){
+				if (sw_discovery_resolve(discovery, interface_index, name, type, 
+						domain, _resolve_reply, extra, &rid) != SW_OKAY) {
+					gaim_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n");
+				}
+			}
+			break;
+		case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
+			gaim_debug_info("bonjour", "_browser_reply --> Remove service\n");
+			gb = gaim_find_buddy((GaimAccount*)extra, name);
+			if (gb != NULL) {
+				bonjour_buddy_delete(gb->proto_data);
+				gaim_blist_remove_buddy(gb);
+			}
+			break;
+		case SW_DISCOVERY_BROWSE_RESOLVED:
+			gaim_debug_info("bonjour", "_browse_reply --> Resolved\n");
+			break;
+		default:
+			break;
+	}
+	
+	return SW_OKAY;
+}
+
+int _dns_sd_publish(BonjourDnsSd* data, PublishType type)
+{
+	sw_text_record dns_data;
+	sw_result publish_result;
+
+	// Fill the data for the service	
+	if(sw_text_record_init(&dns_data) != SW_OKAY){
+		gaim_debug_error("bonjour", "Unable to initialize the data for the mDNS.");
+		return -1;
+	}
+
+	sw_text_record_add_key_and_string_value(dns_data, "txtvers", data->txtvers);
+	sw_text_record_add_key_and_string_value(dns_data, "version", data->version);
+	sw_text_record_add_key_and_string_value(dns_data, "1st", data->first);
+	sw_text_record_add_key_and_string_value(dns_data, "last", data->last);
+	// sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", itoa(data->port_p2pj));
+	sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", BONJOUR_DEFAULT_PORT);
+	sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh);
+	sw_text_record_add_key_and_string_value(dns_data, "status", data->status);
+	sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg);
+	sw_text_record_add_key_and_string_value(dns_data, "email", data->email);
+	sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc);
+	sw_text_record_add_key_and_string_value(dns_data, "jid", data->jid);
+	sw_text_record_add_key_and_string_value(dns_data, "AIM", data->AIM);
+
+	// Publish the service
+	switch (type) {
+		case PUBLISH_START:
+			publish_result = sw_discovery_publish(*(data->session), 0, data->name, ICHAT_SERVICE, NULL, 
+								NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data), 
+								_publish_reply, NULL, &(data->session_id));
+			break;
+		case PUBLISH_UPDATE:
+			publish_result = sw_discovery_publish_update(*(data->session),data->session_id,
+								sw_text_record_bytes(dns_data), sw_text_record_len(dns_data));
+			break;
+	}
+	if(publish_result != SW_OKAY){
+		gaim_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.");
+		return -1;
+	}
+
+	// Free the memory used by temp data
+	sw_text_record_fina(dns_data);
+
+	return 0;
+}
+
+gboolean _dns_sd_handle_packets(GIOChannel* source, GIOCondition condition, gpointer data)
+{
+	sw_discovery_read_socket(*((sw_discovery*)data));
+	return TRUE;
+}
+
+gpointer _dns_sd_wait_for_connections(gpointer data)
+{
+	sw_discovery_oid session_id;
+	BonjourDnsSd* dns_sd_data = (BonjourDnsSd*)data;
+
+	// Advise the daemon that we are waiting for connections
+	if(sw_discovery_browse(*(dns_sd_data->session), 0, ICHAT_SERVICE, NULL, _browser_reply,
+			dns_sd_data->account, &session_id) != SW_OKAY){
+		gaim_debug_error("bonjour", "Unable to get service.");
+		return NULL;
+	}
+
+	// Yields control of the cpu to the daemon
+	sw_discovery_run(*(dns_sd_data->session));
+	
+	return NULL;
+}
+
+// End private functions
+
+/**
+ * Allocate space for the dns-sd data.
+ */
+BonjourDnsSd* bonjour_dns_sd_new()
+{
+	BonjourDnsSd* data = g_new(BonjourDnsSd, 1);
+	data->session = g_malloc(sizeof(sw_discovery));
+	
+	return data;
+}
+
+/**
+ * Deallocate the space of the dns-sd data.
+ */
+void bonjour_dns_sd_free(BonjourDnsSd* data)
+{
+	g_free(data->session);
+	g_free(data);
+}
+
+/**
+ * Send a new dns-sd packet updating our status.
+ */
+void bonjour_dns_sd_send_status(BonjourDnsSd* data, char* status, const char* status_message)
+{
+	if (data->status) g_free(data->status);
+	if (data->msg) g_free(data->msg);
+
+	data->status = g_strdup(status);
+	data->msg = g_strdup(status_message);
+
+	// Update our text record with the new status
+	_dns_sd_publish(data, PUBLISH_UPDATE); // <--We must control the errors
+}
+
+/**
+ * Advertise our presence within the dns-sd daemon and start browsing for other 
+ * bonjour peers.
+ */
+void bonjour_dns_sd_start(BonjourDnsSd* data)
+{	
+	GIOChannel* io_channel;
+	gint dns_sd_socket;
+	sw_discovery_oid session_id;
+	
+	// Initilizations of the dns-sd data and session
+	data->session = malloc(sizeof(sw_discovery));
+	if(sw_discovery_init(data->session) != SW_OKAY){
+		gaim_debug_error("bonjour", "Unable to initialize a mDNS session.");
+		return;
+	}
+	
+	// Publish our bonjour IM client at the mDNS daemon
+	_dns_sd_publish(data, PUBLISH_START); // <--We must control the errors
+	
+	
+	// Advise the daemon that we are waiting for connections
+	if(sw_discovery_browse(*(data->session), 0, ICHAT_SERVICE, NULL, _browser_reply,
+			data->account, &session_id) != SW_OKAY){
+		gaim_debug_error("bonjour", "Unable to get service.");
+		return;
+	}
+	
+	// Get the socket that communicates with the mDNS daemon and bind it to a 
+	// callback that will handle the dns_sd packets
+	dns_sd_socket = sw_discovery_socket(*(data->session));
+	io_channel = g_io_channel_unix_new(dns_sd_socket);
+	g_io_add_watch(io_channel, G_IO_IN, _dns_sd_handle_packets, data->session); // Add more for other conditions like when the conn. has been broken
+}
+
+/**
+ * Unregister the "_presence._tcp" service at the mDNS daemon.
+ */
+int bonjour_dns_sd_stop(BonjourDnsSd* data)
+{
+	sw_discovery_cancel(*(data->session), data->session_id);
+	
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/dns_sd.h	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,82 @@
+/*
+ *  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 Library 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 _BONJOUR_DNS_SD
+#define _BONJOUR_DNS_SD
+
+#include <howl.h>
+#include <glib.h>
+#include "account.h"
+	
+#define BONJOUR_DEFAULT_PORT "5298"
+#define BONJOUR_DEFAULT_PORT_INT 5298
+#define ICHAT_SERVICE "_presence._tcp."
+
+/**
+ * Data to be used by the dns-sd connection.
+ */
+typedef struct _bonjour_dns_sd{
+	sw_discovery* session;
+	sw_discovery_oid session_id;
+	GaimAccount* account;
+	gchar* name;
+	gchar* txtvers;
+	gchar* version;
+	gchar* first;
+	gchar* last;
+	gint port_p2pj;
+	gchar* phsh;
+	gchar* status;
+	gchar* email;
+	gchar* vc;
+	gchar* jid;
+	gchar* AIM;
+	gchar* msg;
+	GHashTable* buddies;
+}BonjourDnsSd;
+
+typedef enum _PublishType{
+	PUBLISH_START,
+	PUBLISH_UPDATE
+}PublishType;
+
+/**
+ * Allocate space for the dns-sd data.
+ */
+BonjourDnsSd* bonjour_dns_sd_new();
+
+/**
+ * Deallocate the space of the dns-sd data.
+ */
+void bonjour_dns_sd_free(BonjourDnsSd* data);
+
+/**
+ * Send a new dns-sd packet updating our status.
+ */
+void bonjour_dns_sd_send_status(BonjourDnsSd* data, char* status, const char* status_message);
+
+/**
+ * Advertise our presence within the dns-sd daemon and start browsing for other 
+ * bonjour peers.
+ */
+void bonjour_dns_sd_start(BonjourDnsSd* data);
+ 
+/**
+ * Unregister the "_presence._tcp" service at the mDNS daemon.
+ */
+int bonjour_dns_sd_stop(BonjourDnsSd* data);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/issues.txt	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,18 @@
+==========================================
+============= Known issues ===============
+==========================================
+
+(1) Messages are limited in length (5000 char) <-- FIXED
+(2) Messages formated by Gaim didn't work <-- FIXED
+(3) iChat sends the size in points, Gaim wants a 1..7 range <-- FIXED Gaim2iChat (iChat2Gaim left)
+(4) When the other end closes the socket without sending the end of stream, Gaim crashes and coredump <-- FIXED
+(5) I18n
+(6) Status changes don't work
+(7) When the conversation is closed in Gaim with the X button, we don't send the end of stream <-- FIXED
+(8) The server socket is not reusable, after an error, you cannot connect for a while <-- FIXED
+(9) Avatars
+(10) File transfers
+(11) Typing notifications
+(12) Gaim HTML syntax is not shown properly <-- FIXED
+(13) Strange messages creates coredump <-- FIXED
+(14) Check if it works on win32
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/jabber.c	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,591 @@
+/*
+ * gaim - Bonjour Protocol Plugin
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "network.h"
+#include "eventloop.h"
+#include "connection.h"
+#include "blist.h"
+#include "xmlnode.h"
+#include "debug.h"
+#include "notify.h"
+#include "util.h"
+
+#include "jabber.h"
+#include "bonjour.h"
+#include "buddy.h"
+
+gint _connect_to_buddy(GaimBuddy* gb)
+{
+	gint socket_fd;
+	gint retorno = 0;
+	struct sockaddr_in buddy_address;
+	
+	// Create a socket and make it non-blocking
+	socket_fd = socket(PF_INET, SOCK_STREAM, 0);
+	
+	buddy_address.sin_family = PF_INET;
+	buddy_address.sin_port = htons(((BonjourBuddy*)(gb->proto_data))->port_p2pj);
+	inet_aton(((BonjourBuddy*)(gb->proto_data))->ip, &(buddy_address.sin_addr));
+	memset(&(buddy_address.sin_zero), '\0', 8);
+	
+	retorno = connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr));
+	if (retorno == -1) {
+		perror("connect");	
+	}
+	fcntl(socket_fd, F_SETFL, O_NONBLOCK);
+
+	return socket_fd;
+}
+
+char* _font_size_gaim_to_ichat(int size)
+{
+	GString* result = NULL;
+	
+	switch(size){
+		case 1 :
+			result = g_string_new("8");
+			break;
+		case 2 :
+			result = g_string_new("10");
+			break;
+		case 3 :
+			result = g_string_new("12");
+			break;
+		case 4 :
+			result = g_string_new("14");
+			break;
+		case 5 :
+			result = g_string_new("17");
+			break;
+		case 6 :
+			result = g_string_new("21");
+			break;
+		case 7 :
+			result = g_string_new("24");
+			break;
+		default:
+			result = g_string_new("12");
+	}
+	
+	return g_string_free(result, FALSE);
+}
+
+char* _font_size_ichat_to_gaim(int size)
+{
+	GString* result = NULL;
+	
+	if (size > 24) {
+		result = g_string_new("7");
+	} else if(size >= 21) {
+		result = g_string_new("6");
+	} else if(size >= 17) {
+		result = g_string_new("5");
+	} else if(size >= 14) {
+		result = g_string_new("4");
+	} else if(size >= 12) {
+		result = g_string_new("3");
+	} else if(size >= 10) {
+		result = g_string_new("2");
+	} else {
+		result = g_string_new("1");
+	}
+
+	return g_string_free(result, FALSE);
+}
+void _jabber_parse_and_write_message_to_ui(char* message, GaimConnection* connection, GaimBuddy* gb)
+{
+	xmlnode* body_node = NULL;
+	char* body = NULL;
+	xmlnode* html_node = NULL;
+	gboolean isHTML = FALSE;
+	xmlnode* html_body_node = NULL;
+	char* ichat_balloon_color = NULL;
+	char* ichat_text_color = NULL;
+	xmlnode* html_body_font_node = NULL;
+	char* font_face = NULL;
+	char* font_size = NULL;
+	char* font_color = NULL;
+	char* html_body = NULL;
+	xmlnode* events_node = NULL;
+	gboolean composing_event = FALSE;
+	gint garbage = -1;
+	xmlnode* message_node = NULL;
+
+	// Parsing of the message
+	message_node = xmlnode_from_str(message, strlen(message));
+	if (message_node == NULL) {
+		return;
+	}
+	
+	body_node = xmlnode_get_child(message_node, "body");
+	if (body_node != NULL) {
+		body = xmlnode_get_data(body_node);
+	} else {
+		return;
+	}
+	
+	html_node = xmlnode_get_child(message_node, "html");
+	if (html_node != NULL) {
+		isHTML = TRUE;
+		html_body_node = xmlnode_get_child(html_node, "body");
+		if (html_body_node != NULL) {
+			ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor");
+			ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor");
+			html_body_font_node = xmlnode_get_child(html_body_node, "font");
+			if (html_body_font_node != NULL) { // Types of messages sent by iChat
+				font_face = xmlnode_get_attrib(html_body_font_node, "face");
+				// The absolute iChat font sizes should be converted to 1..7 range
+				font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ");
+				if (font_size != NULL) {
+					font_size = _font_size_ichat_to_gaim(atoi(font_size)); //<-- This call will probably leak memory
+				}
+				font_color = xmlnode_get_attrib(html_body_font_node, "color");
+				html_body = xmlnode_get_data(html_body_font_node);
+				if (html_body == NULL) { // This is the kind of formated messages that Gaim creates
+					html_body = xmlnode_to_str(html_body_font_node, &garbage);
+				}
+			} else {
+				isHTML = FALSE;
+			}
+		} else {
+			isHTML = FALSE;
+		}
+			
+	}
+	
+	events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
+	if (events_node != NULL) {
+		if (xmlnode_get_child(events_node, "composing") != NULL) {
+			composing_event = TRUE;
+		}
+		if (xmlnode_get_child(events_node, "id") != NULL) { // The user is just typing
+			xmlnode_free(message_node);
+			g_free(body);
+			g_free(html_body);
+			return;
+		}
+	}
+	
+	// Compose the message
+	if (isHTML) {
+		if (font_face == NULL) font_face = "Helvetica";
+		if (font_size == NULL) font_size = "3";
+		if (ichat_text_color == NULL) ichat_text_color = "#000000";
+		if (ichat_balloon_color == NULL) ichat_balloon_color = "#FFFFFF";
+		body = g_strconcat("<font face='", font_face, "' size='", font_size, "' color='", ichat_text_color, 
+							"' back='", ichat_balloon_color, "'>", html_body, "</font>", NULL);
+	}
+	
+	// Send the message to the UI
+	serv_got_im(connection, gb->name, body, 0, time(NULL));
+	
+	// Free all the strings and nodes (the attributes are freed with their nodes)
+	xmlnode_free(message_node);
+	g_free(body);
+	g_free(html_body);
+}
+
+gboolean _check_buddy_by_address(gpointer key, gpointer value, gpointer address)
+{
+	GaimBuddy* gb = (GaimBuddy*)value;
+	BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data;
+	
+	if (bb != NULL) {
+		if (g_strcasecmp(bb->ip, (char*)address) == 0) {
+			return TRUE;
+		} else {
+			return FALSE;
+		}
+	} else {
+		return FALSE;	
+	}
+}
+
+gint _read_data(gint socket, char** message)
+{
+	GString* data = g_string_new("");
+	char parcial_data[512];
+	gint total_message_length = 0;
+	gint parcial_message_length = 0;
+
+	// Read chunks of 512 bytes till the end of the data
+	while ((parcial_message_length = recv(socket, parcial_data, 512, 0)) > 0) {
+			g_string_append_len(data, parcial_data, parcial_message_length);
+			total_message_length += parcial_message_length;
+	}
+	
+	if (parcial_message_length == -1) {
+		perror("recv");
+		if (total_message_length == 0) {
+			return -1;
+		}
+	}
+
+	*message = data->str;
+	g_string_free(data, FALSE);
+if (total_message_length != 0) gaim_debug_info("bonjour", "Receive: -%s- %d bytes\n", *message, total_message_length);
+	return total_message_length;
+}
+
+gint _send_data(gint socket, char* message)
+{
+	gint message_len = strlen(message);
+	gint parcial_sent = 0;
+	gchar* parcial_message = message;
+	
+	while ((parcial_sent = send(socket, parcial_message, message_len, 0)) < message_len) {
+		if (parcial_sent != -1) {
+			parcial_message += parcial_sent;
+			message_len -= parcial_sent;
+		} else {
+			return -1;
+		}
+	}
+	
+	return strlen(message);
+}
+
+void _client_socket_handler(gpointer data, gint socket, GaimInputCondition condition)
+{
+	char* message = NULL;
+	gint message_length;
+	GaimBuddy* gb = (GaimBuddy*)data;
+	GaimAccount* account = gb->account;
+	GaimConversation* conversation;
+	char* closed_conv_message;
+	BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data;
+	gboolean closed_conversation = FALSE;
+	char* error_message;
+
+	// Read the data from the socket
+	if ((message_length = _read_data(socket, &message)) == -1) {
+		// There have been an error reading from the socket
+		return;
+	} else if (message_length == 0) { // The other end has closed the socket
+		closed_conversation = TRUE;
+	} else {
+		message[message_length] = '\0';
+		
+		while (g_ascii_iscntrl(message[message_length - 1])) {
+			message[message_length - 1] = '\0';
+			message_length--;
+		}
+	}
+	
+	// Check if the start of the doctype has been received, if not check that the current
+	// data is the doctype
+	if (!(bb->conversation->start_step_one)) {
+		if (g_str_has_prefix(message, DOCTYPE_DECLARATION)){
+			bb->conversation->start_step_one = TRUE;
+		}
+	}
+	
+	// Check if the start of the stream has been received, if not check that the current 
+	// data is the start of the stream
+	if (!(bb->conversation->start_step_two)) {
+		if (g_str_has_suffix(message, STREAM_START)) {
+			bb->conversation->start_step_two = TRUE;
+			
+			// If we haven't done it yet, we have to sent the start of the stream to the other buddy
+			if (!(bb->conversation->stream_started)) {
+				if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1) {
+					gaim_debug_error("bonjour", "Unable to start a conversation with %s\n", bb->name);
+				}
+			}
+		}
+		return;
+	}
+	
+	// Check that this is not the end of the conversation
+	if (g_str_has_prefix(message, STREAM_END) || (closed_conversation == TRUE)) {
+		// Close the socket, clear the watcher and free memory
+		if (bb->conversation != NULL) {
+			close(bb->conversation->socket);
+			gaim_input_remove(bb->conversation->watcher_id);
+			g_free(bb->conversation->buddy_name);
+			g_free(bb->conversation);
+			bb->conversation = NULL;
+		}
+		
+		// Inform the user that the conversation has been closed
+		conversation = gaim_find_conversation_with_account(gb->name, account);
+		closed_conv_message = g_strconcat(gb->name, " has closed the conversation.", NULL);
+		gaim_conversation_write(conversation, NULL, closed_conv_message, GAIM_MESSAGE_SYSTEM, time(NULL));
+	} else {
+		// Parse the message to get the data and send to the ui
+		_jabber_parse_and_write_message_to_ui(message, account->gc, gb);
+	}
+}
+
+void _server_socket_handler(gpointer data, int server_socket, GaimInputCondition condition)
+{
+	GaimBuddy* gb = NULL;
+	struct sockaddr_in their_addr; // connector's address information
+	int sin_size = sizeof(struct sockaddr);
+	int client_socket;
+	BonjourBuddy* bb = NULL;
+	char* address_text = NULL;
+	GaimBuddyList* bl = gaim_get_blist();
+
+	//Check that it is a read condition
+	if (condition != GAIM_INPUT_READ) {
+		return;
+	}
+
+	if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
+		return;
+	}
+	fcntl(client_socket, F_SETFL, O_NONBLOCK);
+
+	// Look for the buddy that has open the conversation and fill information
+	address_text = inet_ntoa(their_addr.sin_addr);
+	gb = (GaimBuddy*)g_hash_table_find(bl->buddies, _check_buddy_by_address, address_text);
+	if (gb == NULL) {
+		gaim_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
+		close(client_socket);
+		return;
+	}
+	bb = (BonjourBuddy*)gb->proto_data;
+
+	// Check if the conversation has been previously started
+	if (bb->conversation == NULL) {
+		bb->conversation = g_new(BonjourJabberConversation, 1);
+		bb->conversation->socket = client_socket;
+		bb->conversation->start_step_one = FALSE;
+		bb->conversation->start_step_two = FALSE;
+		bb->conversation->stream_started = FALSE;
+		bb->conversation->buddy_name = g_strdup(gb->name);
+		bb->conversation->message_id = 1;
+		
+		if (bb->conversation->stream_started == FALSE) {
+			// Start the stream
+			send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0);
+			bb->conversation->stream_started = TRUE;
+		}
+
+		// Open a watcher for the client socket
+		bb->conversation->watcher_id = gaim_input_add(client_socket, GAIM_INPUT_READ, 
+													_client_socket_handler, gb);
+	} else {
+		close(client_socket);
+	}
+}
+
+gint bonjour_jabber_start(BonjourJabber* data)
+{
+	struct sockaddr_in my_addr;
+	int yes = 1;
+	char* error_message = NULL;
+	
+
+	// Open a listening socket for incoming conversations
+	if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+		gaim_debug_error("bonjour", "Cannot get socket\n");
+		error_message = strerror(errno);
+		gaim_debug_error("bonjour", "%s\n", error_message);
+		gaim_connection_error(data->account->gc, "Cannot open socket");
+		return -1;
+	}
+	
+	// Make the socket reusable
+	if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0) {
+		gaim_debug_error("bonjour", "Cannot make socket reusable\n");
+		error_message = strerror(errno);
+		gaim_debug_error("bonjour", "%s\n", error_message);
+		gaim_connection_error(data->account->gc, "Error setting socket options");
+		return -1;
+	}
+
+	memset(&my_addr, 0, sizeof(struct sockaddr_in));
+	my_addr.sin_family = PF_INET;
+	my_addr.sin_port = htons(data->port);
+	
+	if (bind(data->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) {
+		gaim_debug_error("bonjour", "Cannot bind socket\n");
+		error_message = strerror(errno);
+		gaim_debug_error("bonjour", "%s\n", error_message);
+		gaim_connection_error(data->account->gc, "Cannot bind socket to port");
+		return -1;
+	}
+	
+	if (listen(data->socket, 10) != 0) {
+		gaim_debug_error("bonjour", "Cannot listen to socket\n");
+		error_message = strerror(errno);
+		gaim_debug_error("bonjour", "%s\n", error_message);
+		gaim_connection_error(data->account->gc, "Cannot listen to socket");
+		return -1;
+	}
+
+	//data->socket = gaim_network_listen(data->port);
+
+	//if (data->socket == -1) {
+	//	gaim_debug_error("bonjour", "No se ha podido crear el socket\n");
+	//}
+
+	// Open a watcher in the socket we have just opened
+	data->watcher_id = gaim_input_add(data->socket, GAIM_INPUT_READ, _server_socket_handler, data);
+	
+	return 0;
+}
+
+void bonjour_jabber_send_message(BonjourJabber* data, const gchar* to, const gchar* body)
+{
+	xmlnode* message_node = NULL;
+	gchar* message = NULL;
+	gint message_length = -1;
+	xmlnode* message_body_node = NULL;
+	xmlnode* message_html_node = NULL;
+	xmlnode* message_html_body_node = NULL;
+	xmlnode* message_html_body_font_node = NULL;
+	xmlnode* message_x_node = NULL;
+	GaimBuddy* gb = gaim_find_buddy(data->account, to);
+	BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data;
+	char* conv_message = NULL;
+	GaimConversation* conversation = NULL;
+	char* message_from_ui = NULL;
+	char* stripped_message = NULL;
+	
+	// Enclose the message from the UI within a "font" node
+	message_body_node = xmlnode_new("body");
+	stripped_message = gaim_markup_strip_html(body);
+	xmlnode_insert_data(message_body_node, stripped_message, strlen(stripped_message));
+	
+	message_from_ui = g_strconcat("<font>", body, "</font>", NULL);
+	message_html_body_font_node = xmlnode_from_str(message_from_ui, strlen(message_from_ui));
+	
+	message_html_body_node = xmlnode_new("body");
+	xmlnode_insert_child(message_html_body_node, message_html_body_font_node);
+	
+	message_html_node = xmlnode_new("html");
+	xmlnode_set_attrib(message_html_node, "xmlns", "http://www.w3.org/1999/xhtml");
+	xmlnode_insert_child(message_html_node, message_html_body_node);
+
+	message_x_node = xmlnode_new("x");
+	xmlnode_set_attrib(message_x_node, "xmlns", "jabber:x:event");
+	xmlnode_insert_child(message_x_node, xmlnode_new("composing"));
+	
+	message_node = xmlnode_new("message");
+	xmlnode_set_attrib(message_node, "to", ((BonjourBuddy*)(gb->proto_data))->name);
+	xmlnode_set_attrib(message_node, "type", "chat");
+	xmlnode_insert_child(message_node, message_body_node);
+	xmlnode_insert_child(message_node, message_html_node);
+	xmlnode_insert_child(message_node, message_x_node);
+	
+	message = xmlnode_to_str(message_node, &message_length);
+
+	// Check if there is a previously open conversation
+	if (bb->conversation == NULL) {
+		bb->conversation = g_new(BonjourJabberConversation, 1);
+		bb->conversation->socket = _connect_to_buddy(gb);;
+		bb->conversation->start_step_one = FALSE;
+		bb->conversation->start_step_two = FALSE;
+		bb->conversation->stream_started = FALSE;
+		bb->conversation->buddy_name = g_strdup(gb->name);
+		bb->conversation->watcher_id = gaim_input_add(bb->conversation->socket, 
+														GAIM_INPUT_READ, _client_socket_handler, gb);
+	}
+
+	// Check if the stream for the conversation has been started
+	if (bb->conversation->stream_started == FALSE) {
+		// Start the stream
+		if (send(bb->conversation->socket, DOCTYPE, strlen(DOCTYPE), 0) == -1) {
+				gaim_debug_error("bonjour", "Unable to start a conversation\n");
+				perror("send");
+				conv_message = g_strdup("Unable to send the message, the conversation couldn't be started.");
+				conversation = gaim_find_conversation_with_account(bb->name, data->account);
+				gaim_conversation_write(conversation, NULL, conv_message, GAIM_MESSAGE_SYSTEM, time(NULL));
+				close(bb->conversation->socket);
+				gaim_input_remove(bb->conversation->watcher_id);
+				
+				// Free all the data related to the conversation
+				g_free(bb->conversation->buddy_name);
+				g_free(bb->conversation);
+				bb->conversation = NULL;
+				return;
+		}
+		
+		bb->conversation->stream_started = TRUE;
+	}
+	
+	// Send the message
+	if (_send_data(bb->conversation->socket, message) == -1) {
+		gaim_debug_error("bonjour", "Unable to send the message\n");
+		conv_message = g_strdup("Unable to send the message.");
+		conversation = gaim_find_conversation_with_account(bb->name, data->account);
+		gaim_conversation_write(conversation, NULL, conv_message, GAIM_MESSAGE_SYSTEM, time(NULL));
+	}
+}
+
+void bonjour_jabber_close_conversation(BonjourJabber* data, GaimBuddy* gb)
+{
+	BonjourBuddy* bb = (BonjourBuddy*)gb->proto_data;
+	
+	if (bb->conversation != NULL) {
+		// Send the end of the stream to the other end of the conversation
+		send(bb->conversation->socket, STREAM_END, strlen(STREAM_END), 0);
+		
+		// Close the socket and remove the watcher
+		close(bb->conversation->socket);
+		gaim_input_remove(bb->conversation->watcher_id);
+		
+		// Free all the data related to the conversation
+		g_free(bb->conversation->buddy_name);
+		g_free(bb->conversation);
+		bb->conversation = NULL;
+	}
+}
+
+void bonjour_jabber_stop(BonjourJabber* data)
+{
+	GaimBuddy* gb = NULL;
+	BonjourBuddy* bb = NULL;
+	GSList* buddies;
+	GSList* l;
+	
+	// Close the server socket and remove all the watcher
+	close(data->socket);
+	gaim_input_remove(data->watcher_id);
+	
+	// Close all the sockets and remove all the watchers after sending end streams
+	if(data->account->gc != NULL){
+		buddies = gaim_find_buddies(data->account, data->account->username);
+		for(l = buddies; l; l = l->next){
+			gb = (GaimBuddy*)l->data;
+			bb = (BonjourBuddy*)gb->proto_data;
+			if (bb->conversation != NULL) {
+				send(bb->conversation->socket, STREAM_END, strlen(STREAM_END), 0);
+				close(bb->conversation->socket);
+				gaim_input_remove(bb->conversation->watcher_id);
+			}
+		}
+		g_slist_free(buddies);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/jabber.h	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,67 @@
+/**
+ * @file jabber.h The Gaim interface to mDNS and peer to peer Jabber.
+ *
+ * gaim
+ *
+ * Gaim is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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 _BONJOUR_JABBER_H_
+#define _BONJOUR_JABBER_H_
+
+#include "account.h"
+
+#define DOCTYPE_DECLARATION "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
+#define STREAM_START "<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\">"
+#define CONVERSATION_START "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\">"
+#define STREAM_END "</stream:stream>"
+#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >"
+typedef struct _bonjour_jabber{
+	gint port;
+	gint socket;
+	gint watcher_id;
+	char* name;
+	GaimAccount* account;
+}BonjourJabber;
+
+typedef struct _bonjour_jabber_conversation{
+	gint socket;
+	gint watcher_id;
+	gchar* buddy_name;
+	gboolean start_step_one;
+	gboolean start_step_two;
+	gboolean stream_started;
+	gint message_id;
+}BonjourJabberConversation;
+
+/**
+ * Start listening for jabber connections. Returns 0 if the connection could be
+ * stablished, -1 if a problem appears.
+ */
+gint bonjour_jabber_start(BonjourJabber* data);
+
+void bonjour_jabber_send_message(BonjourJabber* data, const gchar* to, const gchar* body);
+
+void bonjour_jabber_close_conversation(BonjourJabber* data, GaimBuddy* gb);
+
+void bonjour_jabber_stop(BonjourJabber* data);
+
+
+#endif /* _BONJOUR_JABBER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/bonjour/messages.txt	Fri Sep 09 12:34:27 2005 +0000
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" >
+<message to="yo@chloe" type="chat"><body>hola</body><html xmlns="html://www.w3.org/1999/xhtml"><body ichatballoncolor="#111111" ichattextcolor="#000000"><font face="Courier" ABSZ="3">hola</font></body></html><x xmlns="jabber:x:event"><composing /></x></message>
+</stream:stream>