Mercurial > pidgin
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>