changeset 21468:94b3275eafcc

merge of '7a98d978e56502762335bda8f19011a139ac460a' and 'd2959ce162eb97cc02e8a7f61fe9a804391fdd46'
author Mark Doliner <mark@kingant.net>
date Tue, 13 Nov 2007 18:00:22 +0000
parents 3d70e3ec8a47 (diff) eb0f36c3a58f (current diff)
children 0464ae06d7ff f16534ec2897
files
diffstat 28 files changed, 1372 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Nov 12 08:27:56 2007 +0000
+++ b/ChangeLog	Tue Nov 13 18:00:22 2007 +0000
@@ -6,6 +6,11 @@
 	libpurple:
 	* We now honor a PURPLE_DISABLE_DEPRECATED define to allow plugins to
 	  catch deprecated functions earlier rather than later.
+	* Thanks to a patch from Intel, the Bonjour prpl now supports file
+	  transfers using XEP-0096 and XEP-0065.  This should enable file
+	  transfers between libpurple clients and Gaijm clients, but will
+	  not work with iChat or Adium as they use a different file transfer
+	  implementation.
 
 	Pidgin:
 	* If a plugin says it can't be unloaded, we now display an error and
--- a/ChangeLog.API	Mon Nov 12 08:27:56 2007 +0000
+++ b/ChangeLog.API	Tue Nov 13 18:00:22 2007 +0000
@@ -50,6 +50,9 @@
 		* purple_util_init()
 		* purple_util_uninit()
 
+		* purple_network_listen_map_external() to temporarily disable
+		  mapping ports externally via NAT-PMP or UPnP.
+
 		* pidgin_dialogs_about_with_parent()
 		* pidgin_log_show_contact_with_parent()
 		* pidgin_log_show_with_parent()
--- a/Makefile.mingw	Mon Nov 12 08:27:56 2007 +0000
+++ b/Makefile.mingw	Tue Nov 13 18:00:22 2007 +0000
@@ -62,8 +62,8 @@
 	saslGSSAPI.dll \
 	saslLOGIN.dll \
 	saslPLAIN.dll \
-	silc.dll \
-	silcclient.dll \
+	libsilc-1-1-2.dll \
+	libsilcclient-1-1-2.dll \
 	smime3.dll \
 	softokn3.dll \
 	ssl3.dll
--- a/finch/finch.pc.in	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/finch.pc.in	Tue Nov 13 18:00:22 2007 +0000
@@ -2,8 +2,8 @@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+datarootdir=@datarootdir@
 datadir=@datadir@
-datarootdir=@datarootdir@
 sysconfdir=@sysconfdir@
 
 Name: Finch
--- a/finch/gntblist.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/gntblist.c	Tue Nov 13 18:00:22 2007 +0000
@@ -1506,6 +1506,8 @@
 {
 	if (text[0] == 27 && text[1] == 0) {
 		/* Escape was pressed */
+		if (gnt_tree_is_searching(GNT_TREE(ggblist->tree)))
+			gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "end-search", NULL);
 		remove_peripherals(ggblist);
 	} else if (strcmp(text, GNT_KEY_CTRL_O) == 0) {
 		purple_prefs_set_bool(PREF_ROOT "/showoffline",
--- a/finch/libgnt/gntmenu.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gntmenu.c	Tue Nov 13 18:00:22 2007 +0000
@@ -235,7 +235,7 @@
 
 	if (nth == NULL)
 		return FALSE;
-		
+
 	find = find_item_with_trigger(nth->next, NULL, trigger);
 	if (!find)
 		find = find_item_with_trigger(menu->list, nth->next, trigger);
--- a/finch/libgnt/gnttextview.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gnttextview.c	Tue Nov 13 18:00:22 2007 +0000
@@ -61,6 +61,8 @@
 static gchar *select_end;
 static gboolean double_click;
 
+static void reset_text_view(GntTextView *view);
+
 static void
 gnt_text_view_draw(GntWidget *widget)
 {
@@ -370,7 +372,7 @@
 
 	string = view->string;
 	view->string = NULL;
-	gnt_text_view_clear(view);
+	reset_text_view(view);
 
 	view->string = g_string_set_size(view->string, string->len);
 	view->string->len = 0;
@@ -654,7 +656,7 @@
 	return fl;
 }
 
-void gnt_text_view_clear(GntTextView *view)
+static void reset_text_view(GntTextView *view)
 {
 	GntTextLine *line;
 
@@ -667,6 +669,14 @@
 	if (view->string)
 		g_string_free(view->string, TRUE);
 	view->string = g_string_new(NULL);
+}
+
+void gnt_text_view_clear(GntTextView *view)
+{
+	reset_text_view(view);
+
+	g_list_foreach(view->tags, free_tag, NULL);
+	view->tags = NULL;
 
 	if (GNT_WIDGET(view)->window)
 		gnt_widget_draw(GNT_WIDGET(view));
@@ -833,7 +843,7 @@
 	if (status == 0) {
 		char *text = NULL;
 		if (g_file_get_contents(pageditor.file, &text, NULL, NULL)) {
-			gnt_text_view_clear(pageditor.tv);
+			reset_text_view(pageditor.tv);
 			gnt_text_view_append_text_with_flags(pageditor.tv, text, GNT_TEXT_FLAG_NORMAL);
 			gnt_text_view_scroll(GNT_TEXT_VIEW(pageditor.tv), 0);
 			g_free(text);
--- a/finch/libgnt/gntwidget.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gntwidget.c	Tue Nov 13 18:00:22 2007 +0000
@@ -466,7 +466,6 @@
 		*width = wid->priv.width + shadow;
 	if (height)
 		*height = wid->priv.height + shadow;
-	
 }
 
 static void
--- a/finch/libgnt/gntwindow.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gntwindow.c	Tue Nov 13 18:00:22 2007 +0000
@@ -28,6 +28,7 @@
 struct _GntWindowPriv
 {
 	GHashTable *accels;   /* key => menuitem-id */
+	GntWindowFlags flags;
 };
 
 enum
@@ -189,16 +190,33 @@
 	if (name && window->priv) {
 		if (!gnt_style_read_menu_accels(name, window->priv->accels)) {
 			g_hash_table_destroy(window->priv->accels);
-			g_free(window->priv);
-			window->priv = NULL;
+			window->priv->accels = NULL;
 		}
 	}
 }
 
 const char * gnt_window_get_accel_item(GntWindow *window, const char *key)
 {
-	if (window->priv)
+	if (window->priv->accels)
 		return g_hash_table_lookup(window->priv->accels, key);
 	return NULL;
 }
 
+void gnt_window_set_maximize(GntWindow *window, GntWindowFlags maximize)
+{
+	if (maximize & GNT_WINDOW_MAXIMIZE_X)
+		window->priv->flags |= GNT_WINDOW_MAXIMIZE_X;
+	else
+		window->priv->flags &= ~GNT_WINDOW_MAXIMIZE_X;
+
+	if (maximize & GNT_WINDOW_MAXIMIZE_Y)
+		window->priv->flags |= GNT_WINDOW_MAXIMIZE_Y;
+	else
+		window->priv->flags &= ~GNT_WINDOW_MAXIMIZE_Y;
+}
+
+GntWindowFlags gnt_window_get_maximize(GntWindow *window)
+{
+	return (window->priv->flags & (GNT_WINDOW_MAXIMIZE_X | GNT_WINDOW_MAXIMIZE_Y));
+}
+
--- a/finch/libgnt/gntwindow.h	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gntwindow.h	Tue Nov 13 18:00:22 2007 +0000
@@ -48,6 +48,12 @@
 typedef struct _GntWindowPriv		GntWindowPriv;
 typedef struct _GntWindowClass		GntWindowClass;
 
+typedef enum
+{
+	GNT_WINDOW_MAXIMIZE_X = 1 << 0,
+	GNT_WINDOW_MAXIMIZE_Y = 1 << 1,
+} GntWindowFlags;
+
 struct _GntWindow
 {
 	GntBox parent;
@@ -112,6 +118,27 @@
  */
 const char * gnt_window_get_accel_item(GntWindow *window, const char *key);
 
+/**
+ * Maximize a window, either horizontally or vertically, or both.
+ *
+ * @param window    The window to maximize.
+ * @param maximize  The maximization state of the window.
+ *
+ * @since 2.3.0
+ */
+void gnt_window_set_maximize(GntWindow *window, GntWindowFlags maximize);
+
+/**
+ * Get the maximization state of a window.
+ *
+ * @param window  The window.
+ *
+ * @return  The maximization state of the window.
+ *
+ * @since 2.3.0
+ */
+GntWindowFlags gnt_window_get_maximize(GntWindow *window);
+
 void gnt_window_workspace_hiding(GntWindow *);
 void gnt_window_workspace_showing(GntWindow *);
 
--- a/finch/libgnt/gntwm.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/finch/libgnt/gntwm.c	Tue Nov 13 18:00:22 2007 +0000
@@ -109,12 +109,10 @@
 gnt_wm_copy_win(GntWidget *widget, GntNode *node)
 {
 	WINDOW *src, *dst;
-	int shadow;
 	if (!node)
 		return;
 	src = widget->window;
 	dst = node->window;
-	shadow = gnt_widget_has_shadow(widget) ? 1 : 0;
 	copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0);
 }
 
@@ -226,17 +224,22 @@
 }
 
 static gboolean
-sanitize_position(GntWidget *widget, int *x, int *y)
+sanitize_position(GntWidget *widget, int *x, int *y, gboolean m)
 {
 	int X_MAX = getmaxx(stdscr);
 	int Y_MAX = getmaxy(stdscr) - 1;
 	int w, h;
 	int nx, ny;
 	gboolean changed = FALSE;
+	GntWindowFlags flags = GNT_IS_WINDOW(widget) ?
+			gnt_window_get_maximize(GNT_WINDOW(widget)) : 0;
 
 	gnt_widget_get_size(widget, &w, &h);
 	if (x) {
-		if (*x + w > X_MAX) {
+		if (m && (flags & GNT_WINDOW_MAXIMIZE_X) && *x != 0) {
+			*x = 0;
+			changed = TRUE;
+		} else if (*x + w > X_MAX) {
 			nx = MAX(0, X_MAX - w);
 			if (nx != *x) {
 				*x = nx;
@@ -245,7 +248,10 @@
 		}
 	}
 	if (y) {
-		if (*y + h > Y_MAX) {
+		if (m && (flags & GNT_WINDOW_MAXIMIZE_Y) && *y != 0) {
+			*y = 0;
+			changed = TRUE;
+		} else if (*y + h > Y_MAX) {
 			ny = MAX(0, Y_MAX - h);
 			if (ny != *y) {
 				*y = ny;
@@ -257,7 +263,7 @@
 }
 
 static void
-refresh_node(GntWidget *widget, GntNode *node, gpointer null)
+refresh_node(GntWidget *widget, GntNode *node, gpointer m)
 {
 	int x, y, w, h;
 	int nw, nh;
@@ -265,14 +271,28 @@
 	int X_MAX = getmaxx(stdscr);
 	int Y_MAX = getmaxy(stdscr) - 1;
 
+	GntWindowFlags flags = 0;
+
+	if (m && GNT_IS_WINDOW(widget)) {
+		flags = gnt_window_get_maximize(GNT_WINDOW(widget));
+	}
+
 	gnt_widget_get_position(widget, &x, &y);
 	gnt_widget_get_size(widget, &w, &h);
 
-	if (sanitize_position(widget, &x, &y))
+	if (sanitize_position(widget, &x, &y, !!m))
 		gnt_screen_move_widget(widget, x, y);
 
-	nw = MIN(w, X_MAX);
-	nh = MIN(h, Y_MAX);
+	if (flags & GNT_WINDOW_MAXIMIZE_X)
+		nw = X_MAX;
+	else
+		nw = MIN(w, X_MAX);
+
+	if (flags & GNT_WINDOW_MAXIMIZE_Y)
+		nh = Y_MAX;
+	else
+		nh = MIN(h, Y_MAX);
+
 	if (nw != w || nh != h)
 		gnt_screen_resize_widget(widget, nw, nh);
 }
@@ -1004,9 +1024,9 @@
 	GntWM *wm = GNT_WM(bindable);
 
 	endwin();
+	refresh();
 
-	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
-	refresh();
+	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, GINT_TO_POINTER(TRUE));
 	g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
 	update_screen(wm);
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
@@ -1609,7 +1629,7 @@
 
 	g_hash_table_replace(wm->nodes, widget, node);
 
-	refresh_node(widget, node, NULL);
+	refresh_node(widget, node, GINT_TO_POINTER(TRUE));
 
 	transient = !!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT);
 
@@ -1622,13 +1642,11 @@
 			shadow = FALSE;
 		x = widget->priv.x;
 		y = widget->priv.y;
-		w = widget->priv.width;
-		h = widget->priv.height;
+		w = widget->priv.width + shadow;
+		h = widget->priv.height + shadow;
 
-		getmaxyx(stdscr, maxy, maxx);
-		maxy -= 1;              /* room for the taskbar */
-		maxy -= shadow;
-		maxx -= shadow;
+		maxx = getmaxx(stdscr);
+		maxy = getmaxy(stdscr) - 1;              /* room for the taskbar */
 
 		x = MAX(0, x);
 		y = MAX(0, y);
@@ -1639,7 +1657,7 @@
 
 		w = MIN(w, maxx);
 		h = MIN(h, maxy);
-		node->window = newwin(h + shadow, w + shadow, y, x);
+		node->window = newwin(h, w, y, x);
 		gnt_wm_copy_win(widget, node);
 	}
 #endif
@@ -1686,7 +1704,7 @@
 		const char *title = GNT_BOX(widget)->title;
 		GntPosition *p = NULL;
 		if (title && (p = g_hash_table_lookup(wm->positions, title)) != NULL) {
-			sanitize_position(widget, &p->x, &p->y);
+			sanitize_position(widget, &p->x, &p->y, TRUE);
 			gnt_widget_set_position(widget, p->x, p->y);
 			mvwin(widget->window, p->y, p->x);
 		}
@@ -1884,9 +1902,8 @@
 {
 	gboolean ret = TRUE;
 	GntNode *node;
-	int shadow;
 	int maxx, maxy;
-	
+
 	while (widget->parent)
 		widget = widget->parent;
 	node = g_hash_table_lookup(wm->nodes, widget);
@@ -1900,9 +1917,8 @@
 	gnt_widget_set_size(widget, width, height);
 	gnt_widget_draw(widget);
 
-	shadow = gnt_widget_has_shadow(widget) ? 1 : 0;
-	maxx = getmaxx(stdscr) - shadow;
-	maxy = getmaxy(stdscr) - 1 - shadow;
+	maxx = getmaxx(stdscr);
+	maxy = getmaxy(stdscr) - 1;
 	height = MIN(height, maxy);
 	width = MIN(width, maxx);
 	wresize(node->window, height, width);
--- a/libpurple/network.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/network.c	Tue Nov 13 18:00:22 2007 +0000
@@ -259,6 +259,12 @@
 	return FALSE;
 }
 
+static gboolean listen_map_external = TRUE;
+void purple_network_listen_map_external(gboolean map_external)
+{
+	listen_map_external = map_external;
+}
+
 static PurpleNetworkListenData *
 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
 {
@@ -356,11 +362,17 @@
 	listen_data->cb_data = cb_data;
 	listen_data->socket_type = socket_type;
 
+	if (!listen_map_external)
+	{
+		purple_debug_info("network", "Skipping external port mapping.\n");
+		/* The pmp_map_cb does what we want to do */
+		purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
+	}
 	/* Attempt a NAT-PMP Mapping, which will return immediately */
-	if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
+	else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
 							  actual_port, actual_port, PURPLE_PMP_LIFETIME))
 	{
-		purple_debug_info("network", "Created NAT-PMP mapping on port %i\n",actual_port);
+		purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
 		/* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
 		purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
 	}
@@ -567,7 +579,7 @@
 
 		retval = WSALookupServiceEnd(h);
 
-		g_idle_add(wpurple_network_change_thread_cb, NULL);
+		purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
 
 	}
 
--- a/libpurple/network.h	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/network.h	Tue Nov 13 18:00:22 2007 +0000
@@ -106,6 +106,20 @@
  */
 const char *purple_network_get_my_ip(int fd);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Should calls to purple_network_listen() and purple_network_listen_range()
+ * map the port externally using NAT-PMP or UPnP?
+ * The default value is TRUE
+ *
+ * @param map_external Should the open port be mapped externally?
+ * @deprecated In 3.0.0 a boolean will be added to the above functions to
+ *             perform the same function.
+ * @since 2.3.0
+ */
+void purple_network_listen_map_external(gboolean map_external);
+#endif
+
 /**
  * Attempts to open a listening port ONLY on the specified port number.
  * You probably want to use purple_network_listen_range() instead of this.
--- a/libpurple/protocols/bonjour/Makefile.am	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Tue Nov 13 18:00:22 2007 +0000
@@ -18,7 +18,9 @@
 	mdns_interface.h \
 	mdns_types.h \
 	parser.c \
-	parser.h
+	parser.h \
+	bonjour_ft.c \
+	bonjour_ft.h 
 
 if MDNS_AVAHI
   BONJOURSOURCES += mdns_avahi.c
--- a/libpurple/protocols/bonjour/Makefile.mingw	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Tue Nov 13 18:00:22 2007 +0000
@@ -43,12 +43,13 @@
 ##  SOURCES, OBJECTS
 ##
 C_SRC =			bonjour.c \
+			bonjour_ft.c \
 			buddy.c \
 			dns_sd_proxy.c \
+			jabber.c \
 			mdns_common.c \
 			mdns_win32.c \
-			parser.c \
-			jabber.c
+			parser.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- a/libpurple/protocols/bonjour/bonjour.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Tue Nov 13 18:00:22 2007 +0000
@@ -41,6 +41,7 @@
 #include "mdns_common.h"
 #include "jabber.h"
 #include "buddy.h"
+#include "bonjour_ft.h"
 
 /*
  * TODO: Should implement an add_buddy callback that removes the buddy
@@ -453,8 +454,8 @@
 	NULL,                                                    /* roomlist_cancel */
 	NULL,                                                    /* roomlist_expand_category */
 	NULL,                                                    /* can_receive_file */
-	NULL,                                                    /* send_file */
-	NULL,                                                    /* new_xfer */
+	bonjour_send_file,                                       /* send_file */
+	bonjour_new_xfer,                                        /* new_xfer */
 	NULL,                                                    /* offline_message */
 	NULL,                                                    /* whiteboard_prpl_ops */
 	NULL,                                                    /* send_raw */
--- a/libpurple/protocols/bonjour/bonjour.h	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Tue Nov 13 18:00:22 2007 +0000
@@ -44,6 +44,7 @@
 {
 	BonjourDnsSd *dns_sd_data;
 	BonjourJabber *jabber_data;
+	GList *xfer_lists;
 } BonjourData;
 
 #endif /* _BONJOUR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Tue Nov 13 18:00:22 2007 +0000
@@ -0,0 +1,836 @@
+/*
+ * purple - Bonjour Protocol Plugin
+ *
+ * Purple 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 "internal.h"
+#include "util.h"
+#include "debug.h"
+#include "notify.h"
+#include "proxy.h"
+#include "ft.h"
+#include "buddy.h"
+#include "bonjour.h"
+#include "bonjour_ft.h"
+#include "cipher.h"
+
+static void
+bonjour_bytestreams_init(PurpleXfer *xfer);
+static void
+bonjour_bytestreams_connect(PurpleXfer *xfer);
+static void
+bonjour_xfer_init(PurpleXfer *xfer);
+static void
+bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from,
+		     const int filesize, const char *filename, int option);
+static void bonjour_free_xfer(PurpleXfer *xfer);
+
+/* Look for specific xfer handle */
+static unsigned int next_id = 0;
+
+static void
+xep_ft_si_reject(PurpleXfer *xfer, char *to)
+{
+	xmlnode *error_node = NULL;
+	xmlnode *tmp_node = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+
+	if(!to || !xfer)
+		return;
+	xf = xfer->data;
+	if(!xf)
+		return;
+
+	purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
+	iq = xep_iq_new(xf->data, XEP_IQ_ERROR, to, xf->sid);
+	if(iq == NULL)
+		return;
+
+	error_node = xmlnode_new_child(iq->node, "error");
+	xmlnode_set_attrib(error_node, "code", "403");
+	xmlnode_set_attrib(error_node, "type", "cancel");
+
+	tmp_node = xmlnode_new_child(error_node, "forbidden");
+	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+
+	tmp_node = xmlnode_new_child(error_node, "text");
+	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+	xmlnode_insert_data(tmp_node, "Offer Declined", -1);
+
+	xep_iq_send_and_free(iq);
+}
+
+static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
+{
+	purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
+	bonjour_free_xfer(xfer);
+}
+
+static void bonjour_xfer_request_denied(PurpleXfer *xfer)
+{
+	purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
+	xep_ft_si_reject(xfer, xfer->who);
+	bonjour_free_xfer(xfer);
+}
+
+static void bonjour_xfer_cancel_recv(PurpleXfer *xfer)
+{
+	purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
+	bonjour_free_xfer(xfer);
+}
+
+struct socket_cleanup {
+	int fd;
+	guint handle;
+};
+
+static void
+_wait_for_socket_close(gpointer data, gint source, PurpleInputCondition cond)
+{
+	struct socket_cleanup *sc = data;
+	char buf[1];
+	int ret;
+
+	ret = recv(source, buf, 1, 0);
+
+	if (ret == 0 || (ret == -1 && !(errno == EAGAIN || errno == EWOULDBLOCK))) {
+		purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n");
+		purple_input_remove(sc->handle);
+		close(sc->fd);
+		g_free(sc);
+	}
+}
+
+static void bonjour_xfer_end(PurpleXfer *xfer)
+{
+	purple_debug_info("bonjour", "Bonjour-xfer-end.\n");
+
+	/* We can't allow the server side to close the connection until the client is complete,
+	 * otherwise there is a RST resulting in an error on the client side */
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && purple_xfer_is_completed(xfer)) {
+		struct socket_cleanup *sc = g_new0(struct socket_cleanup, 1);
+		sc->fd = xfer->fd;
+		xfer->fd = -1;
+		sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ,
+						 _wait_for_socket_close, sc);
+	}
+
+	bonjour_free_xfer(xfer);
+}
+
+static PurpleXfer*
+bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
+{
+	GList *xfers = NULL;
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf = NULL;
+
+	if(!sid || !from || !bd)
+		return NULL;
+
+	purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
+			  sid, from);
+
+	for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
+		xfer = xfers->data;
+		if(xfer == NULL)
+			break;
+		xf = xfer->data;
+		if(xf == NULL)
+			break;
+		if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
+				!strcmp(xfer->who, from))
+			return xfer;
+	}
+
+	purple_debug_info("bonjour", "Look for xfer list fail\n");
+
+	return NULL;
+}
+
+static void
+xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
+{
+	xmlnode *si_node = NULL;
+	xmlnode *feature = NULL;
+	xmlnode *field = NULL;
+	xmlnode *option = NULL;
+	xmlnode *value = NULL;
+	xmlnode *file = NULL;
+	xmlnode *x = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+	char buf[32];
+
+	xf = xfer->data;
+	if(!xf)
+		return;
+
+	bd = xf->data;
+	if(!bd)
+		return;
+
+	purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
+
+	/* Assign stream id. */
+	memset(buf, 0, 32);
+	g_snprintf(buf, sizeof(buf), "%u", next_id++);
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, buf);
+	if(iq == NULL)
+		return;
+
+	g_free(xf->sid);
+	xf->sid = g_strdup(buf);
+	/*Construct Stream initialization offer message.*/
+	si_node = xmlnode_new_child(iq->node, "si");
+	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+
+	file = xmlnode_new_child(si_node, "file");
+	xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
+	xmlnode_set_attrib(file, "name", xfer->filename);
+	memset(buf, 0, 32);
+	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
+	xmlnode_set_attrib(file, "size", buf);
+
+	feature = xmlnode_new_child(si_node, "feature");
+	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+
+	x = xmlnode_new_child(feature, "x");
+	xmlnode_set_namespace(x, "jabber:x:data");
+	xmlnode_set_attrib(x, "type", "form");
+
+	field = xmlnode_new_child(x, "field");
+	xmlnode_set_attrib(field, "var", "stream-method");
+	xmlnode_set_attrib(field, "type", "list-single");
+
+	if (xf->mode & XEP_BYTESTREAMS) {
+		option = xmlnode_new_child(field, "option");
+		value = xmlnode_new_child(option, "value");
+		xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+	}
+	if (xf->mode & XEP_IBB) {
+		option = xmlnode_new_child(field, "option");
+		value = xmlnode_new_child(option, "value");
+		xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+	}
+
+	xep_iq_send_and_free(iq);
+}
+
+static void
+xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid)
+{
+	xmlnode *error_node = NULL;
+	xmlnode *tmp_node = NULL;
+	XepIq *iq = NULL;
+
+	g_return_if_fail(bd != NULL);
+
+	if(!to || !sid)
+		return;
+
+	purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
+	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, sid);
+	if(iq == NULL)
+		return;
+
+	error_node = xmlnode_new_child(iq->node, "error");
+	xmlnode_set_attrib(error_node, "code", "403");
+	xmlnode_set_attrib(error_node, "type", "cancel");
+
+	tmp_node = xmlnode_new_child(error_node, "forbidden");
+	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+
+	tmp_node = xmlnode_new_child(error_node, "text");
+	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+	xmlnode_insert_data(tmp_node, "Offer Declined", -1);
+
+	xep_iq_send_and_free(iq);
+}
+
+static void
+xep_ft_si_result(PurpleXfer *xfer, char *to)
+{
+	xmlnode *si_node = NULL;
+	xmlnode *feature = NULL;
+	xmlnode *field = NULL;
+	xmlnode *value = NULL;
+	xmlnode *x = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+
+	if(!to || !xfer)
+		return;
+	xf = xfer->data;
+	if(!xf)
+		return;
+
+	purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
+	iq = xep_iq_new(xf->data, XEP_IQ_RESULT, to, xf->sid);
+	if(iq == NULL)
+		return;
+
+	si_node = xmlnode_new_child(iq->node, "si");
+	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+
+	feature = xmlnode_new_child(si_node, "feature");
+	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+
+	x = xmlnode_new_child(feature, "x");
+	xmlnode_set_namespace(x, "jabber:x:data");
+	xmlnode_set_attrib(x, "type", "submit");
+
+	field = xmlnode_new_child(x, "field");
+	xmlnode_set_attrib(field, "var", "stream-method");
+
+	value = xmlnode_new_child(field, "value");
+	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+
+	xep_iq_send_and_free(iq);
+}
+
+static void
+bonjour_free_xfer(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+
+	if(xfer == NULL) {
+		purple_debug_info("bonjour", "bonjour-free-xfer-null.\n");
+		return;
+	}
+
+	purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer);
+
+	xf = (XepXfer*)xfer->data;
+	if(xf != NULL) {
+		bd = (BonjourData*)xf->data;
+		if(bd != NULL) {
+			bd->xfer_lists = g_list_remove(bd->xfer_lists, xfer);
+			purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
+		}
+		if (xf->proxy_connection != NULL)
+			purple_proxy_connect_cancel(xf->proxy_connection);
+		if (xf->listen_data != NULL)
+			purple_network_listen_cancel(xf->listen_data);
+		g_free(xf->jid);
+		g_free(xf->proxy_host);
+		g_free(xf->buddy_ip);
+		g_free(xf->sid);
+		g_free(xf);
+		xfer->data = NULL;
+	}
+
+	purple_debug_info("bonjour", "Need close socket=%d.\n", xfer->fd);
+}
+
+PurpleXfer *
+bonjour_new_xfer(PurpleConnection *gc, const char *who)
+{
+	PurpleXfer *xfer;
+	XepXfer *xep_xfer = NULL;
+	BonjourData *bd = NULL;
+
+	if(who == NULL || gc == NULL)
+		return NULL;
+
+	purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
+	bd = (BonjourData*) gc->proto_data;
+	if(bd == NULL)
+		return NULL;
+
+	/* Build the file transfer handle */
+	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+	xfer->data = xep_xfer = g_new0(XepXfer, 1);
+	xep_xfer->data = bd;
+
+	purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
+
+	xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;
+	xep_xfer->sid = NULL;
+
+	purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
+	purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
+	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
+
+	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+
+	return xfer;
+}
+
+void
+bonjour_send_file(PurpleConnection *gc, const char *who, const char *file)
+{
+
+	PurpleXfer *xfer = NULL;
+	if(gc == NULL || who == NULL)
+		return;
+	purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who);
+	xfer = bonjour_new_xfer(gc, who);
+	if(xfer == NULL)
+		return;
+	if (file)
+		purple_xfer_request_accepted(xfer, file);
+	else
+		purple_xfer_request(xfer);
+
+}
+
+static void
+bonjour_xfer_init(PurpleXfer *xfer)
+{
+	PurpleBuddy *buddy = NULL;
+	BonjourBuddy *bd = NULL;
+	XepXfer *xf = NULL;
+
+	if(xfer == NULL)
+		return;
+	xf = (XepXfer*)xfer->data;
+	if(xf == NULL)
+		return;
+	purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
+
+	buddy = purple_find_buddy(xfer->account, xfer->who);
+	/* this buddy is offline. */
+	if (buddy == NULL)
+		return;
+
+	bd = (BonjourBuddy *)buddy->proto_data;
+	xf->buddy_ip = g_strdup(bd->ip);
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+		/* initiate file transfer, send SI offer. */
+		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
+		xep_ft_si_offer(xfer, xfer->who);
+
+	} else {
+		/* accept file transfer request, send SI result. */
+		xep_ft_si_result(xfer, xfer->who);
+		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
+	}
+	return;
+}
+
+
+void
+xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
+{
+	const char *type = NULL, *from = NULL, *id = NULL;
+	xmlnode *si = NULL, *file = NULL;
+	BonjourData *bd = NULL;
+	PurpleXfer *xfer = NULL;
+	const char *filename = NULL, *filesize_str = NULL;
+	int filesize = 0, option = XEP_BYTESTREAMS;
+
+	if(pc == NULL || packet == NULL || pb == NULL)
+		return;
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+
+	purple_debug_info("bonjour", "xep-si-parse.\n");
+
+	type = xmlnode_get_attrib(packet, "type");
+	from = pb->name;
+	id = xmlnode_get_attrib(packet, "id");
+	if(type) {
+		if(!strcmp(type, "set")){
+			si = xmlnode_get_child(packet,"si");
+			purple_debug_info("bonjour", "si offer Message type - SET.\n");
+			file = xmlnode_get_child(si, "file");
+			/**/
+			filename = xmlnode_get_attrib(file, "name");
+			if((filesize_str = xmlnode_get_attrib(file, "size")))
+				filesize = atoi(filesize_str);
+			bonjour_xfer_receive(pc, id, from, filesize, filename, option);
+		} else if(!strcmp(type, "result")){
+			si = xmlnode_get_child(packet,"si");
+			purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
+			xfer = bonjour_si_xfer_find(bd, id, from);
+			if(xfer == NULL){
+				purple_debug_info("bonjour", "xfer find fail.\n");
+				xep_ft_si_reject2((BonjourData *)pc->proto_data, from, id);
+			} else {
+				bonjour_bytestreams_init(xfer);
+			}
+		} else if(!strcmp(type, "error")){
+			purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
+			xfer = bonjour_si_xfer_find(bd, id, from);
+			if(xfer == NULL){
+				purple_debug_info("bonjour", "xfer find fail.\n");
+			} else {
+				purple_xfer_cancel_remote(xfer);
+			}
+		} else {
+			purple_debug_info("bonjour", "si offer Message type - Unknown-%d.\n", type);
+		}
+	}
+}
+
+void
+xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
+{
+	const char *type = NULL, *from = NULL, *id = NULL;
+	xmlnode *query = NULL, *streamhost = NULL;
+	BonjourData *bd = NULL;
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf = NULL;
+	const char *jid=NULL, *host=NULL, *port=NULL;
+	int portnum;
+
+	if(pc == NULL || packet == NULL || pb == NULL)
+		return;
+
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+
+	purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
+
+	type = xmlnode_get_attrib(packet, "type");
+	from = pb->name;
+	query = xmlnode_get_child(packet,"query");
+	if(type) {
+		if(!strcmp(type, "set")){
+			purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
+
+			id = xmlnode_get_attrib(query, "sid");
+			xfer = bonjour_si_xfer_find(bd, id, from);
+
+			if(xfer){
+				xf = (XepXfer*)xfer->data;
+				for(streamhost = xmlnode_get_child(query, "streamhost");
+						streamhost;
+						streamhost = xmlnode_get_next_twin(streamhost)) {
+
+					if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
+					   (host = xmlnode_get_attrib(streamhost, "host")) &&
+					   (port = xmlnode_get_attrib(streamhost, "port")) &&
+					   (portnum = atoi(port))) {
+
+						if(!strcmp(host, xf->buddy_ip)) {
+							xf->jid = g_strdup(jid);
+							xf->proxy_host = g_strdup(host);
+							xf->proxy_port = portnum;
+							purple_debug_info("bonjour", "bytestream offer parse"
+									  "jid=%s host=%s port=%d.\n", jid, host, portnum);
+							bonjour_bytestreams_connect(xfer);
+							break;
+						}
+
+					} else {
+						purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
+					}
+				}
+			} else {
+
+			}
+
+		} else {
+			purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%d.\n", type);
+		}
+	}
+}
+
+static void
+bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from,
+		     const int filesize, const char *filename, int option)
+{
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+
+	if(pc == NULL || id == NULL || from == NULL || filename == NULL)
+		return;
+
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+
+	purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
+
+	/* Build the file transfer handle */
+	xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from);
+	xfer->data = xf = g_new0(XepXfer, 1);
+	xf->data = bd;
+	purple_xfer_set_filename(xfer, filename);
+	xf->sid = g_strdup(id);
+
+	if(filesize > 0)
+		purple_xfer_set_size(xfer, filesize);
+	purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
+	purple_xfer_set_request_denied_fnc(xfer, bonjour_xfer_request_denied);
+	purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
+	purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
+
+	bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+
+	purple_xfer_request(xfer);
+}
+
+static void
+bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleXfer *xfer = data;
+	XepXfer *xf = NULL;
+	int acceptfd;
+	int len = 0;
+
+	if(xfer == NULL)
+		return;
+
+	xf = xfer->data;
+	if(xf == NULL)
+		return;
+
+	purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf->sock5_req_state);
+
+	switch(xf->sock5_req_state){
+	case 0x00:
+		acceptfd = accept(source, NULL, 0);
+		if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+
+		} else if(acceptfd == -1) {
+
+		} else {
+			int flags;
+
+			purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd);
+
+			flags = fcntl(acceptfd, F_GETFL);
+			fcntl(acceptfd, F_SETFL, flags | O_NONBLOCK);
+
+			purple_input_remove(xfer->watcher);
+			close(source);
+			xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+		}
+		break;
+	case 0x01:
+		xfer->fd = source;
+		len = read(source, xf->rx_buf + xf->rxlen, 3);
+		if(len < 0 && errno == EAGAIN)
+			return;
+		else if(len <= 0){
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
+		}
+		break;
+	case 0x02:
+		xf->tx_buf[0] = 0x05;
+		xf->tx_buf[1] = 0x00;
+		len = write(source, xf->tx_buf, 2);
+		if (len < 0 && errno == EAGAIN)
+			return;
+		else if (len < 0) {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+		}
+		break;
+	case 0x03:
+		len = read(source, xf->rx_buf + xf->rxlen, 20);
+		if(len<=0){
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
+		}
+		break;
+	case 0x04:
+		xf->tx_buf[0] = 0x05;
+		xf->tx_buf[1] = 0x00;
+		xf->tx_buf[2] = 0x00;
+		xf->tx_buf[3] = 0x03;
+		xf->tx_buf[4] = strlen(xf->buddy_ip);
+		memcpy(xf->tx_buf + 5, xf->buddy_ip, strlen(xf->buddy_ip));
+		xf->tx_buf[5+strlen(xf->buddy_ip)] = 0x00;
+		xf->tx_buf[6+strlen(xf->buddy_ip)] = 0x00;
+		len = write(source, xf->tx_buf, 7 + strlen(xf->buddy_ip));
+		if (len < 0 && errno == EAGAIN) {
+			return;
+		} else if (len < 0) {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			xf->rxlen = 0;
+			/*close(source);*/
+			purple_xfer_start(xfer, source, NULL, -1);
+		}
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+static void
+bonjour_bytestreams_listen(int sock, gpointer data)
+{
+	PurpleXfer *xfer = data;
+	XepXfer *xf;
+	XepIq *iq;
+	xmlnode *query, *streamhost;
+	char *port;
+	const char *next_ip;
+	const char *local_ip = NULL;
+	char token [] = ";";
+
+	purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
+	if (sock < 0 || xfer == NULL) {
+		/*purple_xfer_cancel_local(xfer);*/
+		return;
+	}
+
+	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
+					 bonjour_sock5_request_cb, xfer);
+	xf = (XepXfer*)xfer->data;
+	xf->listen_data = NULL;
+
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, xfer->who, xf->sid);
+
+	query = xmlnode_new_child(iq->node, "query");
+	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
+	xmlnode_set_attrib(query, "sid", xf->sid);
+	xmlnode_set_attrib(query, "mode", "tcp");
+
+	xfer->local_port = purple_network_get_port_from_fd(sock);
+
+	local_ip = purple_network_get_my_ip_ext2(sock);
+	/* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
+	next_ip = strtok((char *)local_ip, token);
+
+	while(next_ip != NULL) {
+		streamhost = xmlnode_new_child(query, "streamhost");
+		xmlnode_set_attrib(streamhost, "jid", xf->sid);
+		xmlnode_set_attrib(streamhost, "host", next_ip);
+		port = g_strdup_printf("%hu", xfer->local_port);
+		xmlnode_set_attrib(streamhost, "port", port);
+		g_free(port);
+		next_ip = strtok(NULL, token);
+	}
+
+	xep_iq_send_and_free(iq);
+}
+
+static void
+bonjour_bytestreams_init(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	if(xfer == NULL)
+		return;
+	purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
+	xf = xfer->data;
+	purple_network_listen_map_external(FALSE);
+	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+						      bonjour_bytestreams_listen, xfer);
+	purple_network_listen_map_external(TRUE);
+	if (xf->listen_data == NULL) {
+		purple_xfer_cancel_local(xfer);
+	}
+	return;
+}
+
+static void
+bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleXfer *xfer = data;
+	XepXfer *xf = xfer->data;
+
+	if(data == NULL || source < 0)
+		return;
+
+	purple_proxy_info_destroy(xf->proxy_info);
+	xf->proxy_connection = NULL;
+	xf->proxy_info = NULL;
+	/* Here, start the file transfer.*/
+	purple_xfer_start(xfer, source, NULL, -1);
+}
+
+static void
+bonjour_bytestreams_connect(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	char dstaddr[41];
+	unsigned char hashval[20];
+	char *p = NULL;
+	int i;
+
+	if(xfer == NULL)
+		return;
+
+	purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
+
+	xf = (XepXfer*)xfer->data;
+	if(!xf)
+		return;
+
+	p = g_strdup_printf("%s@%s", xf->sid, xfer->who);
+	purple_cipher_digest_region("sha1", (guchar *)p, strlen(p),
+				    sizeof(hashval), hashval, NULL);
+	g_free(p);
+
+	memset(dstaddr, 0, 41);
+	p = dstaddr;
+	for(i = 0; i < 20; i++, p += 2)
+		snprintf(p, 3, "%02x", hashval[i]);
+
+	xf->proxy_info = purple_proxy_info_new();
+	purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
+	purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host);
+	purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port);
+	xf->proxy_connection = purple_proxy_connect_socks5(NULL, xf->proxy_info,
+							   dstaddr, 0,
+							   bonjour_bytestreams_connect_cb, xfer);
+
+	if(xf->proxy_connection == NULL) {
+		purple_proxy_info_destroy(xf->proxy_info);
+		xf->proxy_info = NULL;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Tue Nov 13 18:00:22 2007 +0000
@@ -0,0 +1,75 @@
+/*
+ * purple - Bonjour Protocol Plugin
+ *
+ * Purple 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_FT_H_
+#define _BONJOUR_FT_H_
+#include "network.h"
+#include "proxy.h"
+typedef struct _XepXfer XepXfer;
+typedef enum {
+	XEP_BYTESTREAMS = 1,
+	XEP_IBB = 2,
+	XEP_UNKNOWN = 4,
+} XepSiMode;
+
+struct _XepXfer
+{
+	void *data;
+	char *filename;
+	int filesize;
+	int id;
+	char *sid;
+	char *recv_id;
+	char *buddy_ip;
+	int mode;
+	PurpleNetworkListenData *listen_data;
+	int sock5_req_state;
+	int rxlen;
+	char rx_buf[0x500];
+	char tx_buf[0x500];
+	PurpleProxyInfo *proxy_info;
+	PurpleProxyConnectData *proxy_connection;
+	char *jid;
+	char *proxy_host;
+	int proxy_port;
+};
+
+/**
+ * Create a new PurpleXfer
+ *
+ * @param gc The PurpleConnection handle.
+ * @param who Who will we be sending it to?
+ */
+PurpleXfer *bonjour_new_xfer(PurpleConnection *gc, const char *who);
+
+/**
+ * Send a file.
+ *
+ * @param gc The PurpleConnection handle.
+ * @param who Who are we sending it to?
+ * @param file What file? If NULL, user will choose after this call.
+ */
+void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file);
+
+void xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+void
+xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+#endif
--- a/libpurple/protocols/bonjour/jabber.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Tue Nov 13 18:00:22 2007 +0000
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #ifndef _WIN32
+#include <net/if.h>
+#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -45,12 +47,22 @@
 #include "parser.h"
 #include "bonjour.h"
 #include "buddy.h"
+#include "bonjour_ft.h"
+
+#ifdef _SIZEOF_ADDR_IFREQ
+#  define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
+#else
+#  define HX_SIZE_OF_IFREQ(a) sizeof(a)
+#endif
 
 #define STREAM_END "</stream:stream>"
 /* TODO: specify version='1.0' and send stream features */
 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
 		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
 
+static void
+xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
+
 #if 0 /* this isn't used anywhere... */
 static const char *
 _font_size_purple_to_ichat(int size)
@@ -316,6 +328,8 @@
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
 	if (!strcmp(packet->name, "message"))
 		_jabber_parse_and_write_message_to_ui(packet, pb);
+	else if(!strcmp(packet->name, "iq"))
+		xep_iq_parse(packet, NULL, pb);
 	else
 		purple_debug_warning("bonjour", "Unknown packet: %s\n",
 				packet->name);
@@ -697,23 +711,21 @@
 	}
 }
 
-int
-bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+static PurpleBuddy *
+_find_or_start_conversation(BonjourJabber *data, const gchar *to)
 {
-	xmlnode *message_node, *node, *node2;
-	gchar *message;
-	PurpleBuddy *pb;
-	BonjourBuddy *bb;
-	int ret;
+	PurpleBuddy *pb = NULL;
+	BonjourBuddy *bb = NULL;
+
+	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(to != NULL, NULL);
 
 	pb = purple_find_buddy(data->account, to);
-	if (pb == NULL) {
-		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+	if (pb == NULL || pb->proto_data == NULL)
 		/* You can not send a message to an offline buddy */
-		return -10000;
-	}
+		return NULL;
 
-	bb = pb->proto_data;
+	bb = (BonjourBuddy *) pb->proto_data;
 
 	/* Check if there is a previously open conversation */
 	if (bb->conversation == NULL)
@@ -721,6 +733,8 @@
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
 
+		purple_debug_info("bonjour", "Starting conversation with %s\n", to);
+
 		/* Make sure that the account always has a proxy of "none".
 		 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
 		proxy_info = purple_account_get_proxy_info(data->account);
@@ -730,13 +744,12 @@
 		}
 		purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
 
-		connect_data =
-			purple_proxy_connect(data->account->gc, data->account, bb->ip,
-					     bb->port_p2pj, _connected_to_buddy, pb);
+		connect_data = purple_proxy_connect(data->account->gc, data->account,
+						    bb->ip, bb->port_p2pj, _connected_to_buddy, pb);
 
 		if (connect_data == NULL) {
 			purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
-			return -10001;
+			return NULL;
 		}
 
 		bb->conversation = bonjour_jabber_conv_new();
@@ -745,6 +758,26 @@
 		 * that neeeds to wait until we're actually connected. */
 		bb->conversation->tx_handler = 0;
 	}
+	return pb;
+}
+
+int
+bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+{
+	xmlnode *message_node, *node, *node2;
+	gchar *message;
+	PurpleBuddy *pb;
+	BonjourBuddy *bb;
+	int ret;
+
+	pb = _find_or_start_conversation(data, to);
+	if (pb == NULL) {
+		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+		/* You can not send a message to an offline buddy */
+		return -10000;
+	}
+
+	bb = pb->proto_data;
 
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
@@ -839,3 +872,167 @@
 		g_slist_free(buddies);
 	}
 }
+
+XepIq *
+xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id)
+{
+	xmlnode *iq_node = NULL;
+	XepIq *iq = NULL;
+
+	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(to != NULL, NULL);
+	g_return_val_if_fail(id != NULL, NULL);
+
+	iq_node = xmlnode_new("iq");
+
+	xmlnode_set_attrib(iq_node, "to", to);
+	xmlnode_set_attrib(iq_node, "id", id);
+	switch (type) {
+		case XEP_IQ_SET:
+			xmlnode_set_attrib(iq_node, "type", "set");
+			break;
+		case XEP_IQ_GET:
+			xmlnode_set_attrib(iq_node, "type", "get");
+			break;
+		case XEP_IQ_RESULT:
+			xmlnode_set_attrib(iq_node, "type", "result");
+			break;
+		case XEP_IQ_ERROR:
+			xmlnode_set_attrib(iq_node, "type", "error");
+			break;
+		case XEP_IQ_NONE:
+		default:
+			xmlnode_set_attrib(iq_node, "type", "none");
+			break;
+	}
+
+	iq = g_new0(XepIq, 1);
+	iq->node = iq_node;
+	iq->type = type;
+	iq->data = ((BonjourData*)data)->jabber_data;
+	iq->to = (char*)to;
+	return iq;
+}
+
+static gboolean
+check_if_blocked(PurpleBuddy *pb)
+{
+	gboolean blocked = FALSE;
+	GSList *l = NULL;
+	PurpleAccount *acc = NULL;
+
+	if(pb == NULL)
+		return FALSE;
+
+	acc = pb->account;
+
+	for(l = acc->deny; l != NULL; l = l->next) {
+		if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) {
+			purple_debug_info("bonjour", "%s has been blocked.\n", pb->name, acc->username);
+			blocked = TRUE;
+			break;
+		}
+	}
+	return blocked;
+}
+
+static void
+xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb)
+{
+	xmlnode *child = NULL;
+
+	if(packet == NULL || pb == NULL)
+		return;
+
+	if(connection == NULL) {
+		if(pb->account != NULL)
+			connection = (pb->account)->gc;
+	}
+
+	if(check_if_blocked(pb))
+		return;
+
+	if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error")))
+		xep_si_parse(connection, packet, pb);
+	else
+		xep_bytestreams_parse(connection, packet, pb);
+}
+
+int
+xep_iq_send_and_free(XepIq *iq)
+{
+	int ret = -1;
+	PurpleBuddy *pb = NULL;
+
+	/* start the talk, reuse the message socket  */
+	pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to);
+	/* Send the message */
+	if (pb != NULL) {
+		/* Convert xml node into stream */
+		gchar *msg = xmlnode_to_str(iq->node, NULL);
+		ret = _send_data(pb, msg);
+		g_free(msg);
+	}
+
+	xmlnode_free(iq->node);
+	iq->node = NULL;
+	g_free(iq);
+
+	return (ret >= 0) ? 0 : -1;
+}
+
+/* This returns a ';' delimited string containing all non-localhost IPs */
+const char *
+purple_network_get_my_ip_ext2(int fd)
+{
+	char buffer[1024];
+	static char ip_ext[17 * 10];
+	char *tmp;
+	char *tip;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+	struct sockaddr_in *sinptr;
+	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
+	long unsigned int add;
+	int source = fd;
+	int len, count = 0;
+
+	if (fd < 0)
+		source = socket(PF_INET, SOCK_STREAM, 0);
+
+	ifc.ifc_len = sizeof(buffer);
+	ifc.ifc_req = (struct ifreq *)buffer;
+	ioctl(source, SIOCGIFCONF, &ifc);
+
+	if (fd < 0)
+		close(source);
+
+	memset(ip_ext, 0, sizeof(ip_ext));
+	memcpy(ip_ext, "0.0.0.0", 7);
+	tmp = buffer;
+	tip = ip_ext;
+	while (tmp < buffer + ifc.ifc_len && count < 10)
+	{
+		ifr = (struct ifreq *)tmp;
+		tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+		if (ifr->ifr_addr.sa_family == AF_INET)
+		{
+			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+			if (sinptr->sin_addr.s_addr != lhost)
+			{
+				add = ntohl(sinptr->sin_addr.s_addr);
+				len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
+					((add >> 24) & 255),
+					((add >> 16) & 255),
+					((add >> 8) & 255),
+					add & 255);
+				tip = (char*) ((int) tip + len);
+				count++;
+				continue;
+			}
+		}
+	}
+
+	return ip_ext;
+}
--- a/libpurple/protocols/bonjour/jabber.h	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Tue Nov 13 18:00:22 2007 +0000
@@ -75,4 +75,24 @@
 
 void bonjour_jabber_stop(BonjourJabber *data);
 
+typedef enum {
+	XEP_IQ_SET,
+	XEP_IQ_GET,
+	XEP_IQ_RESULT,
+	XEP_IQ_ERROR,
+	XEP_IQ_NONE
+} XepIqType;
+
+typedef struct _XepIq {
+	XepIqType type;
+	char *id;
+	xmlnode *node;
+	char *to;
+	void *data;
+} XepIq;
+
+XepIq *xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id);
+int xep_iq_send_and_free(XepIq *iq);
+const char *purple_network_get_my_ip_ext2(int fd);
+
 #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/purple-uninstalled.pc.in	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/purple-uninstalled.pc.in	Tue Nov 13 18:00:22 2007 +0000
@@ -2,8 +2,8 @@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+datarootdir=@datarootdir@
 datadir=@datadir@
-datarootdir=@datarootdir@
 sysconfdir=@sysconfdir@
 
 Name: libpurple
--- a/libpurple/purple.pc.in	Mon Nov 12 08:27:56 2007 +0000
+++ b/libpurple/purple.pc.in	Tue Nov 13 18:00:22 2007 +0000
@@ -2,8 +2,8 @@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+datarootdir=@datarootdir@
 datadir=@datadir@
-datarootdir=@datarootdir@
 sysconfdir=@sysconfdir@
 
 Name: libpurple
--- a/pidgin/gtkblist.c	Mon Nov 12 08:27:56 2007 +0000
+++ b/pidgin/gtkblist.c	Tue Nov 13 18:00:22 2007 +0000
@@ -4469,6 +4469,15 @@
 	g_hash_table_remove(gtkblist->connection_errors, account);
 }
 
+#define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl"
+
+static void
+ssl_faq_clicked_cb(GtkButton *button,
+                   PurpleAccount *account)
+{
+	purple_notify_uri(NULL, SSL_FAQ_URI);
+}
+
 static void
 add_generic_error_dialog(PurpleAccount *account,
                          const PurpleConnectionErrorInfo *err)
@@ -4498,6 +4507,26 @@
 	g_object_set_data(G_OBJECT(mini_dialog), OBJECT_DATA_KEY_ACCOUNT,
 		account);
 
+	if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT) {
+		GtkWidget *faq_button = gtk_button_new();
+		GtkWidget *faq_label = gtk_label_new(NULL);
+		gtk_label_set_markup(GTK_LABEL(faq_label),
+			"<span underline=\"single\" foreground=\"blue\""
+			" size=\"smaller\">" SSL_FAQ_URI "</span>");
+#if GTK_CHECK_VERSION(2,6,0)
+		g_object_set(G_OBJECT(faq_label), "ellipsize",
+			PANGO_ELLIPSIZE_MIDDLE, NULL);
+#endif
+		gtk_container_add(GTK_CONTAINER(faq_button), faq_label);
+		gtk_button_set_relief(GTK_BUTTON(faq_button), GTK_RELIEF_NONE);
+
+		g_signal_connect(faq_button, "clicked",
+			(GCallback)ssl_faq_clicked_cb, account);
+
+		gtk_box_pack_start(PIDGIN_MINI_DIALOG(mini_dialog)->contents,
+			faq_button, FALSE, FALSE, 0);
+	}
+
 	g_signal_connect_after(mini_dialog, "destroy",
 		(GCallback)generic_error_destroy_cb,
 		account);
--- a/pidgin/pidgin-uninstalled.pc.in	Mon Nov 12 08:27:56 2007 +0000
+++ b/pidgin/pidgin-uninstalled.pc.in	Tue Nov 13 18:00:22 2007 +0000
@@ -2,8 +2,8 @@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+datarootdir=@datarootdir@
 datadir=@datadir@
-datarootdir=@datarootdir@
 sysconfdir=@sysconfdir@
 
 Name: Pidgin
--- a/pidgin/pidgin.pc.in	Mon Nov 12 08:27:56 2007 +0000
+++ b/pidgin/pidgin.pc.in	Tue Nov 13 18:00:22 2007 +0000
@@ -2,8 +2,8 @@
 exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
+datarootdir=@datarootdir@
 datadir=@datadir@
-datarootdir=@datarootdir@
 sysconfdir=@sysconfdir@
 
 Name: Pidgin
--- a/po/ChangeLog	Mon Nov 12 08:27:56 2007 +0000
+++ b/po/ChangeLog	Tue Nov 13 18:00:22 2007 +0000
@@ -1,5 +1,8 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.3.0
+	* German translation updated (Bjoern Voigt, Jochen Kemnade)
+
 version 2.2.3
 	* Belarusian Latin translation updated (Ihar Hrachyshka)
 	* Indonesian translation updated (Rai S. Regawa)
--- a/po/de.po	Mon Nov 12 08:27:56 2007 +0000
+++ b/po/de.po	Tue Nov 13 18:00:22 2007 +0000
@@ -12,8 +12,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-11-11 14:59+0100\n"
-"PO-Revision-Date: 2007-11-11 14:58+0100\n"
+"POT-Creation-Date: 2007-11-12 21:38+0100\n"
+"PO-Revision-Date: 2007-11-12 09:54+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -2408,10 +2408,10 @@
 msgstr "Ermöglicht das Laden von Perl-Plugins."
 
 msgid "Psychic Mode"
-msgstr "Psycho-Modus"
+msgstr "Gesprächsvorhersage-Modus"
 
 msgid "Psychic mode for incoming conversation"
-msgstr "Psycho-Modus für eingehende Gespräche"
+msgstr "Vorhersage-Modus für eingehende Gespräche"
 
 msgid ""
 "Causes conversation windows to appear as other users begin to message you.  "
@@ -2433,7 +2433,7 @@
 msgstr "Benachrichtigung in Gesprächen anzeigen"
 
 msgid "Raise psychic conversations"
-msgstr "Psycho-Unterhaltungen anheben"
+msgstr "Gesprächsvorhersage-Meldungen im Vordergrund"
 
 #. *< type
 #. *< ui_requirement
@@ -2593,7 +2593,6 @@
 "Informationen besuchen Sie die FAQ unter: http://developer.pidgin.im/wiki/"
 "Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging."
 
-#. Send a message about the connection error
 msgid "Unable to listen for incoming IM connections\n"
 msgstr "Kann nicht auf eingehende IM-Verbindungen hören\n"
 
@@ -2681,12 +2680,13 @@
 msgid "Your buddylist is empty, nothing was written to the file."
 msgstr "Ihre Buddy-Liste ist leer, es wurde nichts in die Datei geschrieben."
 
-msgid "Couldn't open file"
-msgstr "Kann Datei nicht öffnen"
-
 msgid "Buddylist saved successfully!"
 msgstr "Buddy-Liste wurde erfolgreich gespeichert!"
 
+#, c-format
+msgid "Couldn't write buddy list for %s to %s"
+msgstr "Konnte Buddy-Liste für %s nicht nach %s schreiben"
+
 msgid "Couldn't load buddylist"
 msgstr "Konnte Buddy-Liste nicht laden"
 
@@ -2964,8 +2964,6 @@
 msgid "SSL support unavailable"
 msgstr "SSL-Unterstützung nicht verfügbar"
 
-#. TODO: try other ports if in auto mode, then save
-#. * working port and try that first next time.
 msgid "Couldn't create socket"
 msgstr "Kann Socket nicht erstellen"
 
@@ -3402,7 +3400,6 @@
 msgid "Server does not use any supported authentication method"
 msgstr "Der Server benutzt keine der unterstützten Authentifizierungsmethoden"
 
-#. This should never happen!
 msgid "Invalid response from server."
 msgstr "Ungültige Serverantwort."
 
@@ -3811,9 +3808,6 @@
 msgid "Find Rooms"
 msgstr "Finde Räume"
 
-msgid "Error initializing session"
-msgstr "Fehler bei Initialisieren der Sitzung"
-
 msgid "You require encryption, but it is not available on this server."
 msgstr ""
 "Sie fordern Verschlüsselung, aber diese ist auf dem Server nicht verfügbar."
@@ -5131,7 +5125,6 @@
 msgid "The screen name specified is invalid."
 msgstr "Der angegebene Benutzername ist ungültig."
 
-#, fuzzy
 msgid "Has you"
 msgstr "Hat Sie"
 
@@ -5255,7 +5248,6 @@
 msgid "Invalid input condition"
 msgstr "Ungültige Eingabebedingung"
 
-#. TODO: g_realloc like msn, yahoo, irc, jabber?
 msgid "Read buffer full"
 msgstr "Lesepuffer voll"
 
@@ -5681,9 +5673,6 @@
 msgid "GroupWise Conference %d"
 msgstr "GroupWise-Konferenz %d"
 
-msgid "Unable to make SSL connection to server."
-msgstr "Kann keine SSL-Verbindung zum Server herstellen."
-
 msgid "Authenticating..."
 msgstr "Authentifizierung..."
 
@@ -5725,10 +5714,6 @@
 "%s scheint offline zu sein und hat die Nachricht, die Sie gerade gesendet "
 "haben, nicht empfangen."
 
-#. TODO: Would be nice to prompt if not set!
-#. *  purple_request_fields_with_hint(gc, _("Server Address"),...);
-#.
-#. ...but for now just error out with a nice message.
 msgid ""
 "Unable to connect to server. Please enter the address of the server you wish "
 "to connect to."
@@ -6063,12 +6048,14 @@
 "sein oder mit einem Buchstaben beginnen und nur Buchstaben, Ziffern und "
 "Leerzeichen enthalten oder nur aus Ziffern bestehen."
 
+#. Unregistered screen name
 msgid "Invalid screen name."
 msgstr "Ungültiger Benutzername."
 
 msgid "Incorrect password."
 msgstr "Falsches Passwort."
 
+#. Suspended account
 msgid "Your account is currently suspended."
 msgstr "Ihr Benutzerkonto ist momentan gesperrt."
 
@@ -6076,6 +6063,8 @@
 msgid "The AOL Instant Messenger service is temporarily unavailable."
 msgstr "Der AOL-Sofortnachrichtendienst ist zur Zeit nicht erreichbar."
 
+#. screen name connecting too frequently
+#. IP address connecting too frequently
 msgid ""
 "You have been connecting and disconnecting too frequently. Wait ten minutes "
 "and try again. If you continue to try, you will need to wait even longer."
@@ -6084,6 +6073,7 @@
 "versuchen Sie es noch einmal. Wenn Sie es weiterversuchen, müssen Sie sogar "
 "noch länger warten."
 
+#. client too old
 #, c-format
 msgid "The client version you are using is too old. Please upgrade at %s"
 msgstr ""
@@ -7170,7 +7160,6 @@
 msgid "Connection lost"
 msgstr "Verbindung verloren"
 
-#. cancel login progress
 msgid "Login failed, no reply"
 msgstr "Anmeldung fehlgeschlagen, keine Antwort"
 
@@ -8666,7 +8655,6 @@
 msgid "Could not load SILC key pair: %s"
 msgstr "Konnte SILC-Schlüsselpaar nicht laden: %s"
 
-#. TODO: do we really want to disconnect on a failure to write?
 msgid "Could not write"
 msgstr "Konnte nicht schreiben"
 
@@ -9127,7 +9115,6 @@
 msgid "Unable to establish file descriptor."
 msgstr "Konnte Dateibeschreibung nicht erstellen."
 
-#. TODO: what to do here - do we really have to disconnect?
 msgid "Write Error"
 msgstr "Schreibfehler"
 
@@ -9521,7 +9508,7 @@
 msgstr "Mobil"
 
 msgid "Listening to music"
-msgstr ""
+msgstr "Musik hören"
 
 #, c-format
 msgid "%s changed status from %s to %s"
@@ -10082,8 +10069,28 @@
 msgstr "%s abgemeldet"
 
 #, c-format
-msgid "<span color=\"red\">%s disconnected: %s</span>"
-msgstr "<span color=\"red\">%s abgemeldet: %s</span>"
+msgid "%s disabled"
+msgstr "%s deaktiviert"
+
+msgid "Reconnect"
+msgstr "Wiederverbinden"
+
+msgid "Re-enable"
+msgstr "Reaktivieren"
+
+msgid "Ignore"
+msgstr "Ignorieren"
+
+#, c-format
+msgid "%d account was disabled because you signed on from another location."
+msgid_plural ""
+"%d accounts were disabled because you signed on from another location."
+msgstr[0] ""
+"%d Konto wurde deaktiviert, da Sie sich von einem anderen Ort angemeldet "
+"haben."
+msgstr[1] ""
+"%d Konten wurden deaktiviert, da Sie sich von einem anderen Ort angemeldet "
+"haben."
 
 msgid "<b>Username:</b>"
 msgstr "<b>Benutzername:</b>"
@@ -10204,18 +10211,6 @@
 msgid "SSL Servers"
 msgstr "SSL-Server"
 
-#, c-format
-msgid ""
-"%s\n"
-"\n"
-"%s will not attempt to reconnect the account until you correct the error and "
-"re-enable the account."
-msgstr ""
-"%s\n"
-"\n"
-"%s wird nicht versuchen, das Konto wieder zu verbinden bis Sie den Fehler "
-"behoben und das Konto wieder aktiviert haben."
-
 msgid "Unknown command."
 msgstr "Unbekanntes Kommando."
 
@@ -10261,9 +10256,6 @@
 msgid "Un-Ignore"
 msgstr "Nicht Ignorieren"
 
-msgid "Ignore"
-msgstr "Ignorieren"
-
 msgid "Get Away Message"
 msgstr "Neue Abwesenheitsnachricht abholen"