From 4f62e5ec4b42929fe3c1a68667e63cb1b7a35509 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Thu, 26 May 2011 20:46:49 +0200
Subject: Google Summer of Code 2011 Project: Adding support for Jingle File
 Transfers (XEP-0234), Jingle SOCKS5 Bytestreams Transport Method (XEP-0260),
 Jingle In-Band Bytestreams Transport Method (XEP-0261) and SOCKS5 Bytestreams
 (XEP-0065).

License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php

diff --git a/.gitignore b/.gitignore
index 42586a9..6500850 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,6 @@ checker-report.xml
 VERSION.*
 cppcheck.log
 /build
+/.settings/
+/nbproject/private/
+3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpcstrings.h
diff --git a/3rdParty/LibMiniUPnPc/SConscript b/3rdParty/LibMiniUPnPc/SConscript
new file mode 100644
index 0000000..be9910c
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/SConscript
@@ -0,0 +1,66 @@
+Import(["env", "conf_env"])
+
+if env.get("LIBMINIUPNPC_BUNDLED", False) :
+
+################################################################################
+# Module flags
+################################################################################
+
+	if env["SCONS_STAGE"] == "flags" :
+		env["LIBMINIUPNPC_FLAGS"] = {
+				"CPPPATH": [Dir("src/miniupnpc")],
+				"LIBPATH": [Dir(".")],
+				"LIBS": ["Swiften_MiniUPnPc"],
+			}
+		#if env["PLATFORM"] == "win32" :
+		#	env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")]
+		#	if env["MSVC_VERSION"][:3] == "9.0" :
+		#		env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")]
+
+################################################################################
+# Build
+################################################################################
+
+	if env["SCONS_STAGE"] == "build" :
+		myenv = env.Clone()
+		myenv.Append(CPPPATH = ["src"])
+		# Remove warn flags
+		myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]])
+
+		myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"])
+		
+		if myenv["PLATFORM"] != "win32":
+			myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"])
+		
+		if myenv["PLATFORM"] == "darwin":
+			myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"])
+		
+		if myenv["PLATFORM"] == "win32":
+			myenv.Append(CCFLAGS = ["-DWIN32", "-D_WIN32_WINNT=0x0501"])
+
+		myenv.WriteVal("src/miniupnpc/miniupnpcstrings.h", myenv.Value(
+"""
+#ifndef __MINIUPNPCSTRINGS_H__
+#define __MINIUPNPCSTRINGS_H__
+
+#define OS_STRING "$OS_STRING"
+#define MINIUPNPC_VERSION_STRING "1.5"
+
+#endif
+""".replace("$OS_STRING", myenv["PLATFORM"])))
+
+		myenv.StaticLibrary("Swiften_MiniUPnPc", [
+			"src/miniupnpc/igd_desc_parse.c",
+			"src/miniupnpc/miniupnpc.c",
+			"src/miniupnpc/minixml.c",
+			"src/miniupnpc/minisoap.c",
+			"src/miniupnpc/minissdpc.c",
+			"src/miniupnpc/miniwget.c",
+			#"src/miniupnpc/upnpc.c",
+			"src/miniupnpc/upnpcommands.c",
+			"src/miniupnpc/upnpreplyparse.c",
+			"src/miniupnpc/upnperrors.c",
+			"src/miniupnpc/connecthostport.c",
+			"src/miniupnpc/portlistingparse.c",
+			"src/miniupnpc/receivedata.c"
+			])
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE b/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE
new file mode 100644
index 0000000..2434c86
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/LICENSE
@@ -0,0 +1,27 @@
+MiniUPnPc
+Copyright (c) 2005-2011, Thomas BERNARD 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name of the author may not be used to endorse or promote products
+	  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h
new file mode 100644
index 0000000..1fe0599
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/bsdqueue.h
@@ -0,0 +1,531 @@
+/*	$OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists, 
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#ifdef QUEUE_MACRO_DEBUG
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+ 
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#ifdef SLIST_ENTRY
+#undef SLIST_ENTRY
+#endif
+
+#define SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_END(head)		NULL
+#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = SLIST_FIRST(head);					\
+	    (var) != SLIST_END(head);					\
+	    (var) = SLIST_NEXT(var, field))
+
+#define	SLIST_FOREACH_PREVPTR(var, varp, head, field)			\
+	for ((varp) = &SLIST_FIRST((head));				\
+	    ((var) = *(varp)) != SLIST_END(head);			\
+	    (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) {						\
+	SLIST_FIRST(head) = SLIST_END(head);				\
+}
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define	SLIST_REMOVE_NEXT(head, elm, field) do {			\
+	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->slh_first;		\
+									\
+		while (curelm->field.sle_next != (elm))			\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+		_Q_INVALIDATE((elm)->field.sle_next);			\
+	}								\
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List access methods
+ */
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_END(head)			NULL
+#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head))
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)					\
+	for((var) = LIST_FIRST(head);					\
+	    (var)!= LIST_END(head);					\
+	    (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST(head) = LIST_END(head);				\
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev =			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {				\
+	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\
+		(elm2)->field.le_next->field.le_prev =			\
+		    &(elm2)->field.le_next;				\
+	(elm2)->field.le_prev = (elm)->field.le_prev;			\
+	*(elm2)->field.le_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
+#define	SIMPLEQ_END(head)	    NULL
+#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)				\
+	for((var) = SIMPLEQ_FIRST(head);				\
+	    (var) != SIMPLEQ_END(head);					\
+	    (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {			\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/* 
+ * tail queue access methods 
+ */
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define	TAILQ_EMPTY(head)						\
+	(TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for((var) = TAILQ_LAST(head, headname);				\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue access methods 
+ */
+#define	CIRCLEQ_FIRST(head)		((head)->cqh_first)
+#define	CIRCLEQ_LAST(head)		((head)->cqh_last)
+#define	CIRCLEQ_END(head)		((void *)(head))
+#define	CIRCLEQ_NEXT(elm, field)	((elm)->field.cqe_next)
+#define	CIRCLEQ_PREV(elm, field)	((elm)->field.cqe_prev)
+#define	CIRCLEQ_EMPTY(head)						\
+	(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field)				\
+	for((var) = CIRCLEQ_FIRST(head);				\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for((var) = CIRCLEQ_LAST(head);					\
+	    (var) != CIRCLEQ_END(head);					\
+	    (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) do {						\
+	(head)->cqh_first = CIRCLEQ_END(head);				\
+	(head)->cqh_last = CIRCLEQ_END(head);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == CIRCLEQ_END(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = CIRCLEQ_END(head);			\
+	if ((head)->cqh_last == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.cqe_next = CIRCLEQ_END(head);			\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if ((elm)->field.cqe_next == CIRCLEQ_END(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == CIRCLEQ_END(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+	_Q_INVALIDATE((elm)->field.cqe_prev);				\
+	_Q_INVALIDATE((elm)->field.cqe_next);				\
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.cqe_next = (elm)->field.cqe_next) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_last = (elm2);				\
+	else								\
+		(elm2)->field.cqe_next->field.cqe_prev = (elm2);	\
+	if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) ==		\
+	    CIRCLEQ_END(head))						\
+		(head).cqh_first = (elm2);				\
+	else								\
+		(elm2)->field.cqe_prev->field.cqe_next = (elm2);	\
+	_Q_INVALIDATE((elm)->field.cqe_prev);				\
+	_Q_INVALIDATE((elm)->field.cqe_next);				\
+} while (0)
+
+#endif	/* !_SYS_QUEUE_H_ */
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h
new file mode 100644
index 0000000..f11e5e9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/codelength.h
@@ -0,0 +1,24 @@
+/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2008 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __CODELENGTH_H__
+#define __CODELENGTH_H__
+
+/* Encode length by using 7bit per Byte :
+ * Most significant bit of each byte specifies that the
+ * following byte is part of the code */
+#define DECODELENGTH(n, p) n = 0; \
+                           do { n = (n << 7) | (*p & 0x7f); } \
+                           while(*(p++)&0x80);
+
+#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
+                         if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
+                         if(n>=16384) *(p++) = (n >> 14) | 0x80; \
+                         if(n>=128) *(p++) = (n >> 7) | 0x80; \
+                         *(p++) = n & 0x7f;
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c
new file mode 100644
index 0000000..76e8e37
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.c
@@ -0,0 +1,241 @@
+/* $Id: connecthostport.c,v 1.5 2011/04/09 08:49:50 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2010-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+/* use getaddrinfo() or gethostbyname()
+ * uncomment the following line in order to use gethostbyname() */
+#ifdef NO_GETADDRINFO
+#define USE_GETHOSTBYNAME
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define snprintf _snprintf
+#define herror
+#define socklen_t int
+#else /* #ifdef WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#include <errno.h>
+#define closesocket close
+#include <netdb.h>
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#ifndef USE_GETHOSTBYNAME
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif /* #ifndef USE_GETHOSTBYNAME */
+#endif /* #else WIN32 */
+
+/* definition of PRINT_SOCKET_ERROR */
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#if defined(__amigaos__) || defined(__amigaos4__)
+#define herror(A) printf("%s\n", A)
+#endif
+
+#include "connecthostport.h"
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port)
+{
+	int s, n;
+#ifdef USE_GETHOSTBYNAME
+	struct sockaddr_in dest;
+	struct hostent *hp;
+#else /* #ifdef USE_GETHOSTBYNAME */
+	char tmp_host[MAXHOSTNAMELEN+1];
+	char port_str[8];
+	struct addrinfo *ai, *p;
+	struct addrinfo hints;
+#endif /* #ifdef USE_GETHOSTBYNAME */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+	struct timeval timeout;
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+	
+#ifdef USE_GETHOSTBYNAME
+	hp = gethostbyname(host);
+	if(hp == NULL)
+	{
+		herror(host);
+		return -1;
+	}
+	memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
+	memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
+	s = socket(PF_INET, SOCK_STREAM, 0);
+	if(s < 0)
+	{
+		PRINT_SOCKET_ERROR("socket");
+		return -1;
+	}
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+	/* setting a 3 seconds timeout for the connect() call */
+	timeout.tv_sec = 3;
+	timeout.tv_usec = 0;
+	if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+	{
+		PRINT_SOCKET_ERROR("setsockopt");
+	}
+	timeout.tv_sec = 3;
+	timeout.tv_usec = 0;
+	if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+	{
+		PRINT_SOCKET_ERROR("setsockopt");
+	}
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+	dest.sin_family = AF_INET;
+	dest.sin_port = htons(port);
+	n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
+#ifdef MINIUPNPC_IGNORE_EINTR
+	while(n < 0 && errno == EINTR)
+	{
+		socklen_t len;
+		fd_set wset;
+		int err;
+		FD_ZERO(&wset);
+		FD_SET(s, &wset);
+		if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+			continue;
+		/*len = 0;*/
+		/*n = getpeername(s, NULL, &len);*/
+		len = sizeof(err);
+		if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+			PRINT_SOCKET_ERROR("getsockopt");
+			closesocket(s);
+			return -1;
+		}
+		if(err != 0) {
+			errno = err;
+			n = -1;
+		}
+	}
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+	if(n<0)
+	{
+		PRINT_SOCKET_ERROR("connect");
+		closesocket(s);
+		return -1;
+	}
+#else /* #ifdef USE_GETHOSTBYNAME */
+	/* use getaddrinfo() instead of gethostbyname() */
+	memset(&hints, 0, sizeof(hints));
+	/* hints.ai_flags = AI_ADDRCONFIG; */
+#ifdef AI_NUMERICSERV
+	hints.ai_flags = AI_NUMERICSERV;
+#endif
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
+	/* hints.ai_protocol = IPPROTO_TCP; */
+	snprintf(port_str, sizeof(port_str), "%hu", port);
+	if(host[0] == '[')
+	{
+		/* literal ip v6 address */
+		int i;
+		for(i = 0; host[i+1] && (host[i+1] != ']') && i < MAXHOSTNAMELEN; i++)
+		{
+			tmp_host[i] = host[i+1];
+		}
+		tmp_host[i] = '\0';
+	}
+	else
+	{
+		strncpy(tmp_host, host, MAXHOSTNAMELEN);
+	}
+	tmp_host[MAXHOSTNAMELEN] = '\0';
+	n = getaddrinfo(tmp_host, port_str, &hints, &ai);
+	if(n != 0)
+	{
+#ifdef WIN32
+		fprintf(stderr, "getaddrinfo() error : %d\n", n);
+#else
+		fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
+#endif
+		return -1;
+	}
+	s = -1;
+	for(p = ai; p; p = p->ai_next)
+	{
+		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+		if(s < 0)
+			continue;
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+		/* setting a 3 seconds timeout for the connect() call */
+		timeout.tv_sec = 3;
+		timeout.tv_usec = 0;
+		if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
+		{
+			PRINT_SOCKET_ERROR("setsockopt");
+		}
+		timeout.tv_sec = 3;
+		timeout.tv_usec = 0;
+		if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
+		{
+			PRINT_SOCKET_ERROR("setsockopt");
+		}
+#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
+		n = connect(s, p->ai_addr, p->ai_addrlen);
+#ifdef MINIUPNPC_IGNORE_EINTR
+		while(n < 0 && errno == EINTR)
+		{
+			socklen_t len;
+			fd_set wset;
+			int err;
+			FD_ZERO(&wset);
+			FD_SET(s, &wset);
+			if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
+				continue;
+			/*len = 0;*/
+			/*n = getpeername(s, NULL, &len);*/
+			len = sizeof(err);
+			if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+				PRINT_SOCKET_ERROR("getsockopt");
+				closesocket(s);
+				freeaddrinfo(ai);
+				return -1;
+			}
+			if(err != 0) {
+				errno = err;
+				n = -1;
+			}
+		}
+#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
+		if(n < 0)
+		{
+			closesocket(s);
+			continue;
+		}
+		else
+		{
+			break;
+		}
+	}
+	freeaddrinfo(ai);
+	if(s < 0)
+	{
+		PRINT_SOCKET_ERROR("socket");
+		return -1;
+	}
+	if(n < 0)
+	{
+		PRINT_SOCKET_ERROR("connect");
+		return -1;
+	}
+#endif /* #ifdef USE_GETHOSTBYNAME */
+	return s;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h
new file mode 100644
index 0000000..57e24eb
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/connecthostport.h
@@ -0,0 +1,17 @@
+/* $Id: connecthostport.h,v 1.1 2010/04/04 23:21:03 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2010 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __CONNECTHOSTPORT_H__
+#define __CONNECTHOSTPORT_H__
+
+/* connecthostport()
+ * return a socket connected (TCP) to the host and port
+ * or -1 in case of error */
+int connecthostport(const char * host, unsigned short port);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h
new file mode 100644
index 0000000..b804247
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/declspec.h
@@ -0,0 +1,15 @@
+#ifndef __DECLSPEC_H__
+#define __DECLSPEC_H__
+
+#if defined(WIN32) && !defined(STATICLIB)
+	#ifdef MINIUPNP_EXPORTS
+		#define LIBSPEC __declspec(dllexport)
+	#else
+		#define LIBSPEC __declspec(dllimport)
+	#endif
+#else
+	#define LIBSPEC
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c
new file mode 100644
index 0000000..6c3e656
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.c
@@ -0,0 +1,125 @@
+/* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2010 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include "igd_desc_parse.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Start element handler :
+ * update nesting level counter and copy element name */
+void IGDstartelt(void * d, const char * name, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	memcpy( datas->cureltname, name, l);
+	datas->cureltname[l] = '\0';
+	datas->level++;
+	if( (l==7) && !memcmp(name, "service", l) ) {
+		datas->tmp.controlurl[0] = '\0';
+		datas->tmp.eventsuburl[0] = '\0';
+		datas->tmp.scpdurl[0] = '\0';
+		datas->tmp.servicetype[0] = '\0';
+	}
+}
+
+/* End element handler :
+ * update nesting level counter and update parser state if
+ * service element is parsed */
+void IGDendelt(void * d, const char * name, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	datas->level--;
+	/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
+	if( (l==7) && !memcmp(name, "service", l) )
+	{
+		/*
+		if( datas->state < 1
+			&& !strcmp(datas->servicetype,
+				//	"urn:schemas-upnp-org:service:WANIPConnection:1") )
+				"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
+			datas->state ++;
+		*/
+		if(0==strcmp(datas->tmp.servicetype,
+				"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
+			memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
+		} else if(0==strcmp(datas->tmp.servicetype,
+				"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) {
+			memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
+		} else if(0==strcmp(datas->tmp.servicetype,
+				"urn:schemas-upnp-org:service:WANIPConnection:1")
+				 || 0==strcmp(datas->tmp.servicetype,
+				"urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
+			if(datas->first.servicetype[0] == '\0') {
+				memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
+			} else {
+				memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
+			}
+		}
+	}
+}
+
+/* Data handler :
+ * copy data depending on the current element name and state */
+void IGDdata(void * d, const char * data, int l)
+{
+	struct IGDdatas * datas = (struct IGDdatas *)d;
+	char * dstmember = 0;
+	/*printf("%2d %s : %.*s\n",
+           datas->level, datas->cureltname, l, data);	*/
+	if( !strcmp(datas->cureltname, "URLBase") )
+		dstmember = datas->urlbase;
+	else if( !strcmp(datas->cureltname, "presentationURL") )
+		dstmember = datas->presentationurl;
+	else if( !strcmp(datas->cureltname, "serviceType") )
+		dstmember = datas->tmp.servicetype;
+	else if( !strcmp(datas->cureltname, "controlURL") )
+		dstmember = datas->tmp.controlurl;
+	else if( !strcmp(datas->cureltname, "eventSubURL") )
+		dstmember = datas->tmp.eventsuburl;
+	else if( !strcmp(datas->cureltname, "SCPDURL") )
+		dstmember = datas->tmp.scpdurl;
+/*	else if( !strcmp(datas->cureltname, "deviceType") )
+		dstmember = datas->devicetype_tmp;*/
+	if(dstmember)
+	{
+		if(l>=MINIUPNPC_URL_MAXSIZE)
+			l = MINIUPNPC_URL_MAXSIZE-1;
+		memcpy(dstmember, data, l);
+		dstmember[l] = '\0';
+	}
+}
+
+void printIGD(struct IGDdatas * d)
+{
+	printf("urlbase = '%s'\n", d->urlbase);
+	printf("WAN Device (Common interface config) :\n");
+	/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
+	printf(" serviceType = '%s'\n", d->CIF.servicetype);
+	printf(" controlURL = '%s'\n", d->CIF.controlurl);
+	printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
+	printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
+	printf("primary WAN Connection Device (IP or PPP Connection):\n");
+	/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
+	printf(" servicetype = '%s'\n", d->first.servicetype);
+	printf(" controlURL = '%s'\n", d->first.controlurl);
+	printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
+	printf(" SCPDURL = '%s'\n", d->first.scpdurl);
+	printf("secondary WAN Connection Device (IP or PPP Connection):\n");
+	/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
+	printf(" servicetype = '%s'\n", d->second.servicetype);
+	printf(" controlURL = '%s'\n", d->second.controlurl);
+	printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
+	printf(" SCPDURL = '%s'\n", d->second.scpdurl);
+	printf("WAN IPv6 Firewall Control :\n");
+	/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
+	printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
+	printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
+	printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
+	printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h
new file mode 100644
index 0000000..bab1fd5
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/igd_desc_parse.h
@@ -0,0 +1,48 @@
+/* $Id: igd_desc_parse.h,v 1.10 2011/04/11 09:19:24 nanard Exp $ */
+/* Project : miniupnp
+ * http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2010 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __IGD_DESC_PARSE_H__
+#define __IGD_DESC_PARSE_H__
+
+/* Structure to store the result of the parsing of UPnP
+ * descriptions of Internet Gateway Devices */
+#define MINIUPNPC_URL_MAXSIZE (128)
+struct IGDdatas_service {
+	char controlurl[MINIUPNPC_URL_MAXSIZE];
+	char eventsuburl[MINIUPNPC_URL_MAXSIZE];
+	char scpdurl[MINIUPNPC_URL_MAXSIZE];
+	char servicetype[MINIUPNPC_URL_MAXSIZE];
+	/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
+};
+
+struct IGDdatas {
+	char cureltname[MINIUPNPC_URL_MAXSIZE];
+	char urlbase[MINIUPNPC_URL_MAXSIZE];
+	char presentationurl[MINIUPNPC_URL_MAXSIZE];
+	int level;
+	/*int state;*/
+	/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
+	struct IGDdatas_service CIF;
+	/* "urn:schemas-upnp-org:service:WANIPConnection:1"
+	 * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
+	struct IGDdatas_service first;
+	/* if both WANIPConnection and WANPPPConnection are present */
+	struct IGDdatas_service second;
+	/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
+	struct IGDdatas_service IPv6FC;
+	/* tmp */
+	struct IGDdatas_service tmp;
+};
+
+void IGDstartelt(void *, const char *, int);
+void IGDendelt(void *, const char *, int);
+void IGDdata(void *, const char *, int);
+void printIGD(struct IGDdatas *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c
new file mode 100644
index 0000000..8889bf0
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.c
@@ -0,0 +1,121 @@
+/* $Id: minisoap.c,v 1.21 2011/03/22 19:15:35 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2009 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ *
+ * Minimal SOAP implementation for UPnP protocol.
+ */
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#include <io.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+#include "minisoap.h"
+#include "miniupnpcstrings.h"
+
+/* only for malloc */
+#include <stdlib.h>
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+/* httpWrite sends the headers and the body to the socket
+ * and returns the number of bytes sent */
+static int
+httpWrite(int fd, const char * body, int bodysize,
+          const char * headers, int headerssize)
+{
+	int n = 0;
+	/*n = write(fd, headers, headerssize);*/
+	/*if(bodysize>0)
+		n += write(fd, body, bodysize);*/
+	/* Note : my old linksys router only took into account
+	 * soap request that are sent into only one packet */
+	char * p;
+	/* TODO: AVOID MALLOC */
+	p = malloc(headerssize+bodysize);
+	if(!p)
+	  return 0;
+	memcpy(p, headers, headerssize);
+	memcpy(p+headerssize, body, bodysize);
+	/*n = write(fd, p, headerssize+bodysize);*/
+	n = send(fd, p, headerssize+bodysize, 0);
+	if(n<0) {
+	  PRINT_SOCKET_ERROR("send");
+	}
+	/* disable send on the socket */
+	/* draytek routers dont seems to like that... */
+#if 0
+#ifdef WIN32
+	if(shutdown(fd, SD_SEND)<0) {
+#else
+	if(shutdown(fd, SHUT_WR)<0)	{ /*SD_SEND*/
+#endif
+		PRINT_SOCKET_ERROR("shutdown");
+	}
+#endif
+	free(p);
+	return n;
+}
+
+/* self explanatory  */
+int soapPostSubmit(int fd,
+                   const char * url,
+				   const char * host,
+				   unsigned short port,
+				   const char * action,
+				   const char * body,
+				   const char * httpversion)
+{
+	int bodysize;
+	char headerbuf[512];
+	int headerssize;
+	char portstr[8];
+	bodysize = (int)strlen(body);
+	/* We are not using keep-alive HTTP connections.
+	 * HTTP/1.1 needs the header Connection: close to do that.
+	 * This is the default with HTTP/1.0
+	 * Using HTTP/1.1 means we need to support chunked transfer-encoding :
+	 * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
+	 * transfer encoding. */
+    /* Connection: Close is normally there only in HTTP/1.1 but who knows */
+	portstr[0] = '\0';
+	if(port != 80)
+		snprintf(portstr, sizeof(portstr), ":%hu", port);
+	headerssize = snprintf(headerbuf, sizeof(headerbuf),
+                       "POST %s HTTP/%s\r\n"
+	                   "Host: %s%s\r\n"
+					   "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+	                   "Content-Length: %d\r\n"
+					   "Content-Type: text/xml\r\n"
+					   "SOAPAction: \"%s\"\r\n"
+					   "Connection: Close\r\n"
+					   "Cache-Control: no-cache\r\n"	/* ??? */
+					   "Pragma: no-cache\r\n"
+					   "\r\n",
+					   url, httpversion, host, portstr, bodysize, action);
+#ifdef DEBUG
+	/*printf("SOAP request : headersize=%d bodysize=%d\n",
+	       headerssize, bodysize);
+	*/
+	printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
+	        url, httpversion, host, portstr);
+	printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
+	printf("Headers :\n%s", headerbuf);
+	printf("Body :\n%s\n", body);
+#endif
+	return httpWrite(fd, body, bodysize, headerbuf, headerssize);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h
new file mode 100644
index 0000000..696725f
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minisoap.h
@@ -0,0 +1,15 @@
+/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+#ifndef __MINISOAP_H__
+#define __MINISOAP_H__
+
+/*int httpWrite(int, const char *, int, const char *);*/
+int soapPostSubmit(int, const char *, const char *, unsigned short,
+		   const char *, const char *, const char *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c
new file mode 100644
index 0000000..e61c1cd
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.c
@@ -0,0 +1,138 @@
+/* $Id: minissdpc.c,v 1.14 2010/11/25 09:57:25 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2009 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+/*#include <syslog.h>*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#include <sys/types.h>
+#else
+#define ssize_t int
+#endif
+
+#if defined(WIN32) || defined(__amigaos__) || defined(__amigaos4__)
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <winsock.h>
+#include <stdint.h>
+#endif
+#if defined(__amigaos__) || defined(__amigaos4__)
+#include <sys/socket.h>
+#endif
+#if defined(__amigaos__)
+#define uint16_t unsigned short
+#endif
+/* Hack */
+#define UNIX_PATH_LEN   108
+struct sockaddr_un {
+  uint16_t sun_family;
+  char     sun_path[UNIX_PATH_LEN];
+};
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+#include "minissdpc.h"
+#include "miniupnpc.h"
+
+#include "codelength.h"
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath)
+{
+	struct UPNPDev * tmp;
+	struct UPNPDev * devlist = NULL;
+	unsigned char buffer[2048];
+	ssize_t n;
+	unsigned char * p;
+	unsigned char * url;
+	unsigned int i;
+	unsigned int urlsize, stsize, usnsize, l;
+	int s;
+	struct sockaddr_un addr;
+
+	s = socket(AF_UNIX, SOCK_STREAM, 0);
+	if(s < 0)
+	{
+		/*syslog(LOG_ERR, "socket(unix): %m");*/
+		perror("socket(unix)");
+		return NULL;
+	}
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
+	/* TODO : check if we need to handle the EINTR */
+	if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
+	{
+		/*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
+		close(s);
+		return NULL;
+	}
+	stsize = strlen(devtype);
+	buffer[0] = 1; /* request type 1 : request devices/services by type */
+	p = buffer + 1;
+	l = stsize;	CODELENGTH(l, p);
+	if(p + stsize > buffer + sizeof(buffer))
+	{
+		/* devtype is too long ! */
+		close(s);
+		return NULL;
+	}
+	memcpy(p, devtype, stsize);
+	p += stsize;
+	if(write(s, buffer, p - buffer) < 0)
+	{
+		/*syslog(LOG_ERR, "write(): %m");*/
+		perror("minissdpc.c: write()");
+		close(s);
+		return NULL;
+	}
+	n = read(s, buffer, sizeof(buffer));
+	if(n<=0)
+	{
+		perror("minissdpc.c: read()");
+		close(s);
+		return NULL;
+	}
+	p = buffer + 1;
+	for(i = 0; i < buffer[0]; i++)
+	{
+		if(p+2>=buffer+sizeof(buffer))
+			break;
+		DECODELENGTH(urlsize, p);
+		if(p+urlsize+2>=buffer+sizeof(buffer))
+			break;
+		url = p;
+		p += urlsize;
+		DECODELENGTH(stsize, p);
+		if(p+stsize+2>=buffer+sizeof(buffer))
+			break;
+		tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+		tmp->pNext = devlist;
+		tmp->descURL = tmp->buffer;
+		tmp->st = tmp->buffer + 1 + urlsize;
+		memcpy(tmp->buffer, url, urlsize);
+		tmp->buffer[urlsize] = '\0';
+		memcpy(tmp->buffer + urlsize + 1, p, stsize);
+		p += stsize;
+		tmp->buffer[urlsize+1+stsize] = '\0';
+		devlist = tmp;
+		/* added for compatibility with recent versions of MiniSSDPd 
+		 * >= 2007/12/19 */
+		DECODELENGTH(usnsize, p);
+		p += usnsize;
+		if(p>buffer + sizeof(buffer))
+			break;
+	}
+	close(s);
+	return devlist;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h
new file mode 100644
index 0000000..25e91ce
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minissdpc.h
@@ -0,0 +1,15 @@
+/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2007 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINISSDPC_H__
+#define __MINISSDPC_H__
+
+struct UPNPDev *
+getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c
new file mode 100644
index 0000000..bd46a24
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.c
@@ -0,0 +1,906 @@
+/* $Id: miniupnpc.c,v 1.95 2011/05/15 21:42:26 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2005-2011 Thomas Bernard
+ * This software is subjet to the conditions detailed in the
+ * provided LICENSE file. */
+#define __EXTENSIONS__ 1
+#if !defined(MACOSX) && !defined(__sun)
+#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+#ifndef __cplusplus
+#define _XOPEN_SOURCE 600
+#endif
+#endif
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE 1
+#endif
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+/* Win32 Specific includes and defines */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define snprintf _snprintf
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#define MAXHOSTNAMELEN 64
+#else /* #ifdef WIN32 */
+/* Standard POSIX includes */
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+/* Amiga OS 3 specific stuff */
+#define socklen_t int
+#else
+#include <sys/select.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <strings.h>
+#include <errno.h>
+#define closesocket close
+#endif /* #else WIN32 */
+#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#include <sys/time.h>
+#endif
+#if defined(__amigaos__) || defined(__amigaos4__)
+/* Amiga OS specific stuff */
+#define TIMEVAL struct timeval
+#endif
+
+#include "miniupnpc.h"
+#include "minissdpc.h"
+#include "miniwget.h"
+#include "minisoap.h"
+#include "minixml.h"
+#include "upnpcommands.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#define SOAPPREFIX "s"
+#define SERVICEPREFIX "u"
+#define SERVICEPREFIX2 'u'
+
+/* root description parsing */
+LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
+{
+	struct xmlparser parser;
+	/* xmlparser object */
+	parser.xmlstart = buffer;
+	parser.xmlsize = bufsize;
+	parser.data = data;
+	parser.starteltfunc = IGDstartelt;
+	parser.endeltfunc = IGDendelt;
+	parser.datafunc = IGDdata;
+	parser.attfunc = 0;
+	parsexml(&parser);
+#ifdef DEBUG
+	printIGD(data);
+#endif
+}
+
+/* simpleUPnPcommand2 :
+ * not so simple !
+ * return values :
+ *   pointer - OK
+ *   NULL - error */
+char * simpleUPnPcommand2(int s, const char * url, const char * service,
+		       const char * action, struct UPNParg * args,
+		       int * bufsize, const char * httpversion)
+{
+	char hostname[MAXHOSTNAMELEN+1];
+	unsigned short port = 0;
+	char * path;
+	char soapact[128];
+	char soapbody[2048];
+	char * buf;
+    int n;
+
+	*bufsize = 0;
+	snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
+	if(args==NULL)
+	{
+		/*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
+						"<?xml version=\"1.0\"?>\r\n"
+	    	              "<" SOAPPREFIX ":Envelope "
+						  "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+						  SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+						  "<" SOAPPREFIX ":Body>"
+						  "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
+						  "</" SERVICEPREFIX ":%s>"
+						  "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
+					 	  "\r\n", action, service, action);
+	}
+	else
+	{
+		char * p;
+		const char * pe, * pv;
+		int soapbodylen;
+		soapbodylen = snprintf(soapbody, sizeof(soapbody),
+						"<?xml version=\"1.0\"?>\r\n"
+	    	            "<" SOAPPREFIX ":Envelope "
+						"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+						SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+						"<" SOAPPREFIX ":Body>"
+						"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
+						action, service);
+		p = soapbody + soapbodylen;
+		while(args->elt)
+		{
+			/* check that we are never overflowing the string... */
+			if(soapbody + sizeof(soapbody) <= p + 100)
+			{
+				/* we keep a margin of at least 100 bytes */
+				return NULL;
+			}
+			*(p++) = '<';
+			pe = args->elt;
+			while(*pe)
+				*(p++) = *(pe++);
+			*(p++) = '>';
+			if((pv = args->val))
+			{
+				while(*pv)
+					*(p++) = *(pv++);
+			}
+			*(p++) = '<';
+			*(p++) = '/';
+			pe = args->elt;
+			while(*pe)
+				*(p++) = *(pe++);
+			*(p++) = '>';
+			args++;
+		}
+		*(p++) = '<';
+		*(p++) = '/';
+		*(p++) = SERVICEPREFIX2;
+		*(p++) = ':';
+		pe = action;
+		while(*pe)
+			*(p++) = *(pe++);
+		strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
+		        soapbody + sizeof(soapbody) - p);
+	}
+	if(!parseURL(url, hostname, &port, &path)) return NULL;
+	if(s<0)
+	{
+		s = connecthostport(hostname, port);
+		if(s < 0)
+		{
+			return NULL;
+		}
+	}
+
+	n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
+	if(n<=0) {
+#ifdef DEBUG
+		printf("Error sending SOAP request\n");
+#endif
+		closesocket(s);
+		return NULL;
+	}
+
+	buf = getHTTPResponse(s, bufsize);
+#ifdef DEBUG
+	if(*bufsize > 0 && buf)
+	{
+		printf("SOAP Response :\n%.*s\n", *bufsize, buf);
+	}
+#endif
+	closesocket(s);
+	return buf;
+}
+
+/* simpleUPnPcommand :
+ * not so simple !
+ * return values :
+ *   pointer - OK
+ *   NULL    - error */
+char * simpleUPnPcommand(int s, const char * url, const char * service,
+		       const char * action, struct UPNParg * args,
+		       int * bufsize)
+{
+	char * buf;
+
+	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+/*
+	buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
+	if (!buf || *bufsize == 0)
+	{
+#if DEBUG
+	    printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
+#endif
+		buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
+	}
+*/
+	return buf;
+}
+
+/* parseMSEARCHReply()
+ * the last 4 arguments are filled during the parsing :
+ *    - location/locationsize : "location:" field of the SSDP reply packet
+ *    - st/stsize : "st:" field of the SSDP reply packet.
+ * The strings are NOT null terminated */
+static void
+parseMSEARCHReply(const char * reply, int size,
+                  const char * * location, int * locationsize,
+			      const char * * st, int * stsize)
+{
+	int a, b, i;
+	i = 0;
+	a = i;	/* start of the line */
+	b = 0;	/* end of the "header" (position of the colon) */
+	while(i<size)
+	{
+		switch(reply[i])
+		{
+		case ':':
+				if(b==0)
+				{
+					b = i; /* end of the "header" */
+					/*for(j=a; j<b; j++)
+					{
+						putchar(reply[j]);
+					}
+					*/
+				}
+				break;
+		case '\x0a':
+		case '\x0d':
+				if(b!=0)
+				{
+					/*for(j=b+1; j<i; j++)
+					{
+						putchar(reply[j]);
+					}
+					putchar('\n');*/
+					/* skip the colon and white spaces */
+					do { b++; } while(reply[b]==' ');
+					if(0==strncasecmp(reply+a, "location", 8))
+					{
+						*location = reply+b;
+						*locationsize = i-b;
+					}
+					else if(0==strncasecmp(reply+a, "st", 2))
+					{
+						*st = reply+b;
+						*stsize = i-b;
+					}
+					b = 0;
+				}
+				a = i+1;
+				break;
+		default:
+				break;
+		}
+		i++;
+	}
+}
+
+/* port upnp discover : SSDP protocol */
+#define PORT 1900
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define UPNP_MCAST_ADDR "239.255.255.250"
+/* for IPv6 */
+#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
+
+/* upnpDiscover() :
+ * return a chained list of all devices found or NULL if
+ * no devices was found.
+ * It is up to the caller to free the chained list
+ * delay is in millisecond (poll) */
+LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+             const char * minissdpdsock, int sameport,
+             int ipv6,
+             int * error)
+{
+	struct UPNPDev * tmp;
+	struct UPNPDev * devlist = 0;
+	int opt = 1;
+	static const char MSearchMsgFmt[] = 
+	"M-SEARCH * HTTP/1.1\r\n"
+	"HOST: %s:" XSTR(PORT) "\r\n"
+	"ST: %s\r\n"
+	"MAN: \"ssdp:discover\"\r\n"
+	"MX: %u\r\n"
+	"\r\n";
+	static const char * const deviceList[] = {
+#if 0
+		"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
+		"urn:schemas-upnp-org:service:WANIPConnection:2",
+#endif
+		"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+		"urn:schemas-upnp-org:service:WANIPConnection:1",
+		"urn:schemas-upnp-org:service:WANPPPConnection:1",
+		"upnp:rootdevice",
+		0
+	};
+	int deviceIndex = 0;
+	char bufr[1536];	/* reception and emission buffer */
+	int sudp;
+	int n;
+	struct sockaddr_storage sockudp_r;
+	unsigned int mx;
+#ifdef NO_GETADDRINFO
+	struct sockaddr_storage sockudp_w;
+#else
+	int rv;
+	struct addrinfo hints, *servinfo, *p;
+#endif
+#ifdef WIN32
+	MIB_IPFORWARDROW ip_forward;
+#endif
+	int linklocal = 1;
+
+	if(error)
+		*error = UPNPDISCOVER_UNKNOWN_ERROR;
+#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+	/* first try to get infos from minissdpd ! */
+	if(!minissdpdsock)
+		minissdpdsock = "/var/run/minissdpd.sock";
+	while(!devlist && deviceList[deviceIndex]) {
+		devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
+		                                  minissdpdsock);
+		/* We return what we have found if it was not only a rootdevice */
+		if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
+			if(error)
+				*error = UPNPDISCOVER_SUCCESS;
+			return devlist;
+		}
+		deviceIndex++;
+	}
+	deviceIndex = 0;
+#endif
+	/* fallback to direct discovery */
+#ifdef WIN32
+	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else
+	sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
+#endif
+	if(sudp < 0)
+	{
+		if(error)
+			*error = UPNPDISCOVER_SOCKET_ERROR;
+		PRINT_SOCKET_ERROR("socket");
+		return NULL;
+	}
+	/* reception */
+	memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
+	if(ipv6) {
+		struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
+		p->sin6_family = AF_INET6;
+		if(sameport)
+			p->sin6_port = htons(PORT);
+		p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
+	} else {
+		struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
+		p->sin_family = AF_INET;
+		if(sameport)
+			p->sin_port = htons(PORT);
+		p->sin_addr.s_addr = INADDR_ANY;
+	}
+#ifdef WIN32
+/* This code could help us to use the right Network interface for 
+ * SSDP multicast traffic */
+/* Get IP associated with the index given in the ip_forward struct
+ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
+	if(!ipv6
+	   && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
+		DWORD dwRetVal = 0;
+		PMIB_IPADDRTABLE pIPAddrTable;
+		DWORD dwSize = 0;
+#ifdef DEBUG
+		IN_ADDR IPAddr;
+#endif
+		int i;
+#ifdef DEBUG
+		printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
+#endif
+		pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
+		if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
+			free(pIPAddrTable);
+			pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
+		}
+		if(pIPAddrTable) {
+			dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+#ifdef DEBUG
+			printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
+#endif
+			for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
+#ifdef DEBUG
+				printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
+				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+				printf("\tIP Address[%d]:     \t%s\n", i, inet_ntoa(IPAddr) );
+				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
+				printf("\tSubnet Mask[%d]:    \t%s\n", i, inet_ntoa(IPAddr) );
+				IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
+				printf("\tBroadCast[%d]:      \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
+				printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
+				printf("\tType and State[%d]:", i);
+				printf("\n");
+#endif
+				if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
+					/* Set the address of this interface to be used */
+					struct in_addr mc_if;
+					memset(&mc_if, 0, sizeof(mc_if));
+					mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
+					if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
+						PRINT_SOCKET_ERROR("setsockopt");
+					}
+					((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
+#ifndef DEBUG
+					break;
+#endif
+				}
+			}
+			free(pIPAddrTable);
+			pIPAddrTable = NULL;
+		}
+	}
+#endif
+
+#ifdef WIN32
+	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
+#else
+	if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
+#endif
+	{
+		if(error)
+			*error = UPNPDISCOVER_SOCKET_ERROR;
+		PRINT_SOCKET_ERROR("setsockopt");
+		return NULL;
+	}
+
+	if(multicastif)
+	{
+		if(ipv6) {
+#if !defined(WIN32)
+			/* according to MSDN, if_nametoindex() is supported since
+			 * MS Windows Vista and MS Windows Server 2008.
+			 * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
+			unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
+			if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
+			{
+				PRINT_SOCKET_ERROR("setsockopt");
+			}
+#else
+#ifdef DEBUG
+			printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
+#endif
+#endif
+		} else {
+			struct in_addr mc_if;
+			mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
+			((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
+			if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
+			{
+				PRINT_SOCKET_ERROR("setsockopt");
+			}
+		}
+	}
+
+	/* Avant d'envoyer le paquet on bind pour recevoir la reponse */
+    if (bind(sudp, (const struct sockaddr *)&sockudp_r,
+	         ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
+	{
+		if(error)
+			*error = UPNPDISCOVER_SOCKET_ERROR;
+        PRINT_SOCKET_ERROR("bind");
+		closesocket(sudp);
+		return NULL;
+    }
+
+	if(error)
+		*error = UPNPDISCOVER_SUCCESS;
+	/* Calculating maximum response time in seconds */
+	mx = ((unsigned int)delay) / 1000u;
+	/* receiving SSDP response packet */
+	for(n = 0; deviceList[deviceIndex]; deviceIndex++)
+	{
+	if(n == 0)
+	{
+		/* sending the SSDP M-SEARCH packet */
+		n = snprintf(bufr, sizeof(bufr),
+		             MSearchMsgFmt,
+		             ipv6 ?
+		             (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
+		             : UPNP_MCAST_ADDR,
+		             deviceList[deviceIndex], mx);
+#ifdef DEBUG
+		printf("Sending %s", bufr);
+#endif
+#ifdef NO_GETADDRINFO
+		/* the following code is not using getaddrinfo */
+		/* emission */
+		memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
+		if(ipv6) {
+			struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
+			p->sin6_family = AF_INET6;
+			p->sin6_port = htons(PORT);
+			inet_pton(AF_INET6,
+			          linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
+			          &(p->sin6_addr));
+		} else {
+			struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
+			p->sin_family = AF_INET;
+			p->sin_port = htons(PORT);
+			p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
+		}
+		n = sendto(sudp, bufr, n, 0,
+		           &sockudp_w,
+		           ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+		if (n < 0) {
+			if(error)
+				*error = UPNPDISCOVER_SOCKET_ERROR;
+			PRINT_SOCKET_ERROR("sendto");
+			break;
+		}
+#else /* #ifdef NO_GETADDRINFO */
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
+		hints.ai_socktype = SOCK_DGRAM;
+		/*hints.ai_flags = */
+		if ((rv = getaddrinfo(ipv6
+		                      ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
+		                      : UPNP_MCAST_ADDR,
+		                      XSTR(PORT), &hints, &servinfo)) != 0) {
+			if(error)
+				*error = UPNPDISCOVER_SOCKET_ERROR;
+#ifdef WIN32
+		    fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
+#else
+		    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+#endif
+			break;
+		}
+		for(p = servinfo; p; p = p->ai_next) {
+			n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
+			if (n < 0) {
+				PRINT_SOCKET_ERROR("sendto");
+				continue;
+			}
+		}
+		freeaddrinfo(servinfo);
+		if(n < 0) {
+			if(error)
+				*error = UPNPDISCOVER_SOCKET_ERROR;
+			break;
+		}
+#endif /* #ifdef NO_GETADDRINFO */
+	}
+	/* Waiting for SSDP REPLY packet to M-SEARCH */
+	n = receivedata(sudp, bufr, sizeof(bufr), delay);
+	if (n < 0) {
+		/* error */
+		if(error)
+			*error = UPNPDISCOVER_SOCKET_ERROR;
+		break;
+	} else if (n == 0) {
+		/* no data or Time Out */
+		if (devlist) {
+			/* no more device type to look for... */
+			if(error)
+				*error = UPNPDISCOVER_SUCCESS;
+			break;
+		}
+		if(ipv6) {
+			if(linklocal) {
+				linklocal = 0;
+				--deviceIndex;
+			} else {
+				linklocal = 1;
+			}
+		}
+	} else {
+		const char * descURL=NULL;
+		int urlsize=0;
+		const char * st=NULL;
+		int stsize=0;
+        /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
+		parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
+		if(st&&descURL)
+		{
+#ifdef DEBUG
+			printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
+			       stsize, st, urlsize, descURL);
+#endif
+			for(tmp=devlist; tmp; tmp = tmp->pNext) {
+				if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
+				   tmp->descURL[urlsize] == '\0' &&
+				   memcmp(tmp->st, st, stsize) == 0 &&
+				   tmp->st[stsize] == '\0')
+					break;
+			}
+			/* at the exit of the loop above, tmp is null if
+			 * no duplicate device was found */
+			if(tmp)
+				continue;
+			tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
+			if(!tmp) {
+				/* memory allocation error */
+				if(error)
+					*error = UPNPDISCOVER_MEMORY_ERROR;
+				break;
+			}
+			tmp->pNext = devlist;
+			tmp->descURL = tmp->buffer;
+			tmp->st = tmp->buffer + 1 + urlsize;
+			memcpy(tmp->buffer, descURL, urlsize);
+			tmp->buffer[urlsize] = '\0';
+			memcpy(tmp->buffer + urlsize + 1, st, stsize);
+			tmp->buffer[urlsize+1+stsize] = '\0';
+			devlist = tmp;
+		}
+	}
+	}
+	closesocket(sudp);
+	return devlist;
+}
+
+/* freeUPNPDevlist() should be used to
+ * free the chained list returned by upnpDiscover() */
+LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
+{
+	struct UPNPDev * next;
+	while(devlist)
+	{
+		next = devlist->pNext;
+		free(devlist);
+		devlist = next;
+	}
+}
+
+static void
+url_cpy_or_cat(char * dst, const char * src, int n)
+{
+	if(  (src[0] == 'h')
+	   &&(src[1] == 't')
+	   &&(src[2] == 't')
+	   &&(src[3] == 'p')
+	   &&(src[4] == ':')
+	   &&(src[5] == '/')
+	   &&(src[6] == '/'))
+	{
+		strncpy(dst, src, n);
+	}
+	else
+	{
+		int l = strlen(dst);
+		if(src[0] != '/')
+			dst[l++] = '/';
+		if(l<=n)
+			strncpy(dst + l, src, n - l);
+	}
+}
+
+/* Prepare the Urls for usage...
+ */
+LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
+                 const char * descURL)
+{
+	char * p;
+	int n1, n2, n3, n4;
+	n1 = strlen(data->urlbase);
+	if(n1==0)
+		n1 = strlen(descURL);
+	n1 += 2;	/* 1 byte more for Null terminator, 1 byte for '/' if needed */
+	n2 = n1; n3 = n1; n4 = n1;
+	n1 += strlen(data->first.scpdurl);
+	n2 += strlen(data->first.controlurl);
+	n3 += strlen(data->CIF.controlurl);
+	n4 += strlen(data->IPv6FC.controlurl);
+
+	urls->ipcondescURL = (char *)malloc(n1);
+	urls->controlURL = (char *)malloc(n2);
+	urls->controlURL_CIF = (char *)malloc(n3);
+	urls->controlURL_6FC = (char *)malloc(n4);
+	/* maintenant on chope la desc du WANIPConnection */
+	if(data->urlbase[0] != '\0')
+		strncpy(urls->ipcondescURL, data->urlbase, n1);
+	else
+		strncpy(urls->ipcondescURL, descURL, n1);
+	p = strchr(urls->ipcondescURL+7, '/');
+	if(p) p[0] = '\0';
+	strncpy(urls->controlURL, urls->ipcondescURL, n2);
+	strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
+	strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
+	
+	url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
+
+	url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
+
+	url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
+
+	url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
+
+#ifdef DEBUG
+	printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
+	       (unsigned)strlen(urls->ipcondescURL), n1);
+	printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
+	       (unsigned)strlen(urls->controlURL), n2);
+	printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
+	       (unsigned)strlen(urls->controlURL_CIF), n3);
+	printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
+	       (unsigned)strlen(urls->controlURL_6FC), n4);
+#endif
+}
+
+LIBSPEC void
+FreeUPNPUrls(struct UPNPUrls * urls)
+{
+	if(!urls)
+		return;
+	free(urls->controlURL);
+	urls->controlURL = 0;
+	free(urls->ipcondescURL);
+	urls->ipcondescURL = 0;
+	free(urls->controlURL_CIF);
+	urls->controlURL_CIF = 0;
+	free(urls->controlURL_6FC);
+	urls->controlURL_6FC = 0;
+}
+
+int
+UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+	char status[64];
+	unsigned int uptime;
+	status[0] = '\0';
+	UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+	                   status, &uptime, NULL);
+	if(0 == strcmp("Connected", status))
+	{
+		return 1;
+	}
+	else
+		return 0;
+}
+
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ *     0 = NO IGD found
+ *     1 = A valid connected IGD has been found
+ *     2 = A valid IGD has been found but it reported as
+ *         not connected
+ *     3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+                 struct UPNPUrls * urls,
+				 struct IGDdatas * data,
+				 char * lanaddr, int lanaddrlen)
+{
+	char * descXML;
+	int descXMLsize = 0;
+	struct UPNPDev * dev;
+	int ndev = 0;
+	int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
+	if(!devlist)
+	{
+#ifdef DEBUG
+		printf("Empty devlist\n");
+#endif
+		return 0;
+	}
+	for(state = 1; state <= 3; state++)
+	{
+		for(dev = devlist; dev; dev = dev->pNext)
+		{
+			/* we should choose an internet gateway device.
+		 	* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
+			descXML = miniwget_getaddr(dev->descURL, &descXMLsize,
+			   	                        lanaddr, lanaddrlen);
+			if(descXML)
+			{
+				ndev++;
+				memset(data, 0, sizeof(struct IGDdatas));
+				memset(urls, 0, sizeof(struct UPNPUrls));
+				parserootdesc(descXML, descXMLsize, data);
+				free(descXML);
+				descXML = NULL;
+				if(0==strcmp(data->CIF.servicetype,
+				   "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
+				   || state >= 3 )
+				{
+				  GetUPNPUrls(urls, data, dev->descURL);
+
+#ifdef DEBUG
+				  printf("UPNPIGD_IsConnected(%s) = %d\n",
+				     urls->controlURL,
+			         UPNPIGD_IsConnected(urls, data));
+#endif
+				  if((state >= 2) || UPNPIGD_IsConnected(urls, data))
+					return state;
+				  FreeUPNPUrls(urls);
+				  if(data->second.servicetype[0] != '\0') {
+#ifdef DEBUG
+				    printf("We tried %s, now we try %s !\n",
+				           data->first.servicetype, data->second.servicetype);
+#endif
+				    /* swaping WANPPPConnection and WANIPConnection ! */
+				    memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
+				    memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
+				    memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
+				    GetUPNPUrls(urls, data, dev->descURL);
+#ifdef DEBUG
+				    printf("UPNPIGD_IsConnected(%s) = %d\n",
+				       urls->controlURL,
+			           UPNPIGD_IsConnected(urls, data));
+#endif
+				    if((state >= 2) || UPNPIGD_IsConnected(urls, data))
+					  return state;
+				    FreeUPNPUrls(urls);
+				  }
+				}
+				memset(data, 0, sizeof(struct IGDdatas));
+			}
+#ifdef DEBUG
+			else
+			{
+				printf("error getting XML description %s\n", dev->descURL);
+			}
+#endif
+		}
+	}
+	return 0;
+}
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ *   0 - Not ok
+ *   1 - OK */
+int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+                   struct UPNPUrls * urls,
+                   struct IGDdatas * data,
+                   char * lanaddr, int lanaddrlen)
+{
+	char * descXML;
+	int descXMLsize = 0;
+	descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
+	   	                       lanaddr, lanaddrlen);
+	if(descXML) {
+		memset(data, 0, sizeof(struct IGDdatas));
+		memset(urls, 0, sizeof(struct UPNPUrls));
+		parserootdesc(descXML, descXMLsize, data);
+		free(descXML);
+		descXML = NULL;
+		GetUPNPUrls(urls, data, rootdescurl);
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h
new file mode 100644
index 0000000..50df017
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpc.h
@@ -0,0 +1,121 @@
+/* $Id: miniupnpc.h,v 1.23 2011/04/11 08:21:46 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/
+ * Author: Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __MINIUPNPC_H__
+#define __MINIUPNPC_H__
+
+#include "declspec.h"
+#include "igd_desc_parse.h"
+
+/* error codes : */
+#define UPNPDISCOVER_SUCCESS (0)
+#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
+#define UPNPDISCOVER_SOCKET_ERROR (-101)
+#define UPNPDISCOVER_MEMORY_ERROR (-102)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Structures definitions : */
+struct UPNParg { const char * elt; const char * val; };
+
+char *
+simpleUPnPcommand(int, const char *, const char *,
+                  const char *, struct UPNParg *,
+                  int *);
+
+struct UPNPDev {
+	struct UPNPDev * pNext;
+	char * descURL;
+	char * st;
+	char buffer[2];
+};
+
+/* upnpDiscover()
+ * discover UPnP devices on the network.
+ * The discovered devices are returned as a chained list.
+ * It is up to the caller to free the list with freeUPNPDevlist().
+ * delay (in millisecond) is the maximum time for waiting any device
+ * response.
+ * If available, device list will be obtained from MiniSSDPd.
+ * Default path for minissdpd socket will be used if minissdpdsock argument
+ * is NULL.
+ * If multicastif is not NULL, it will be used instead of the default
+ * multicast interface for sending SSDP discover packets.
+ * If sameport is not null, SSDP packets will be sent from the source port
+ * 1900 (same as destination port) otherwise system assign a source port. */
+LIBSPEC struct UPNPDev *
+upnpDiscover(int delay, const char * multicastif,
+             const char * minissdpdsock, int sameport,
+             int ipv6,
+             int * error);
+/* freeUPNPDevlist()
+ * free list returned by upnpDiscover() */
+LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
+
+/* parserootdesc() :
+ * parse root XML description of a UPnP device and fill the IGDdatas
+ * structure. */
+LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
+
+/* structure used to get fast access to urls
+ * controlURL: controlURL of the WANIPConnection
+ * ipcondescURL: url of the description of the WANIPConnection
+ * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
+ * controlURL_6FC: controlURL of the WANIPv6FirewallControl
+ */
+struct UPNPUrls {
+	char * controlURL;
+	char * ipcondescURL;
+	char * controlURL_CIF;
+	char * controlURL_6FC;
+};
+
+/* UPNP_GetValidIGD() :
+ * return values :
+ *     0 = NO IGD found
+ *     1 = A valid connected IGD has been found
+ *     2 = A valid IGD has been found but it reported as
+ *         not connected
+ *     3 = an UPnP device has been found but was not recognized as an IGD
+ *
+ * In any non zero return case, the urls and data structures
+ * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * free allocated memory.
+ */
+LIBSPEC int
+UPNP_GetValidIGD(struct UPNPDev * devlist,
+                 struct UPNPUrls * urls,
+				 struct IGDdatas * data,
+				 char * lanaddr, int lanaddrlen);
+
+/* UPNP_GetIGDFromUrl()
+ * Used when skipping the discovery process.
+ * return value :
+ *   0 - Not ok
+ *   1 - OK */
+LIBSPEC int
+UPNP_GetIGDFromUrl(const char * rootdescurl,
+                   struct UPNPUrls * urls,
+                   struct IGDdatas * data,
+                   char * lanaddr, int lanaddrlen);
+
+LIBSPEC void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);
+
+LIBSPEC void FreeUPNPUrls(struct UPNPUrls *);
+
+/* return 0 or 1 */
+LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h
new file mode 100644
index 0000000..86081c3
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniupnpctypes.h
@@ -0,0 +1,19 @@
+/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef __MINIUPNPCTYPES_H__
+#define __MINIUPNPCTYPES_H__
+
+#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+#define UNSIGNED_INTEGER unsigned long long
+#define STRTOUI	strtoull
+#else
+#define UNSIGNED_INTEGER unsigned int
+#define STRTOUI	strtoul
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c
new file mode 100644
index 0000000..87f6155
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.c
@@ -0,0 +1,522 @@
+/* $Id: miniwget.c,v 1.52 2011/06/17 22:59:42 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define MAXHOSTNAMELEN 64
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#define snprintf _snprintf
+#define socklen_t int
+#ifndef strncasecmp
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)
+#define strncasecmp _memicmp
+#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#define strncasecmp memicmp
+#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
+#endif /* #ifndef strncasecmp */
+#else /* #ifdef WIN32 */
+#include <unistd.h>
+#include <sys/param.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#define closesocket close
+/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
+ * during the connect() call */
+#define MINIUPNPC_IGNORE_EINTR
+#endif /* #else WIN32 */
+#if defined(__sun) || defined(sun)
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#include "miniupnpcstrings.h"
+#include "miniwget.h"
+#include "connecthostport.h"
+#include "receivedata.h"
+
+/*
+ * Read a HTTP response from a socket.
+ * Process Content-Length and Transfer-encoding headers.
+ * return a pointer to the content buffer, which length is saved
+ * to the length parameter.
+ */
+void *
+getHTTPResponse(int s, int * size)
+{
+	char buf[2048];
+	int n;
+	int endofheaders = 0;
+	int chunked = 0;
+	int content_length = -1;
+	unsigned int chunksize = 0;
+	unsigned int bytestocopy = 0;
+	/* buffers : */
+	char * header_buf;
+	int header_buf_len = 2048;
+	int header_buf_used = 0;
+	char * content_buf;
+	int content_buf_len = 2048;
+	int content_buf_used = 0;
+	char chunksize_buf[32];
+	int chunksize_buf_index;
+
+	header_buf = malloc(header_buf_len);
+	content_buf = malloc(content_buf_len);
+	chunksize_buf[0] = '\0';
+	chunksize_buf_index = 0;
+
+	while((n = receivedata(s, buf, 2048, 5000)) > 0)
+	{
+		if(endofheaders == 0)
+		{
+			int i;
+			int linestart=0;
+			int colon=0;
+			int valuestart=0;
+			if(header_buf_used + n > header_buf_len) {
+				header_buf = realloc(header_buf, header_buf_used + n);
+				header_buf_len = header_buf_used + n;
+			}
+			memcpy(header_buf + header_buf_used, buf, n);
+			header_buf_used += n;
+			/* search for CR LF CR LF (end of headers)
+			 * recognize also LF LF */
+			i = 0;
+			while(i < (header_buf_used-1) && (endofheaders == 0)) {
+				if(header_buf[i] == '\r') {
+					i++;
+					if(header_buf[i] == '\n') {
+						i++;
+						if(i < header_buf_used && header_buf[i] == '\r') {
+							i++;
+							if(i < header_buf_used && header_buf[i] == '\n') {
+								endofheaders = i+1;
+							}
+						}
+					}
+				} else if(header_buf[i] == '\n') {
+					i++;
+					if(header_buf[i] == '\n') {
+						endofheaders = i+1;
+					}
+				}
+				i++;
+			}
+			if(endofheaders == 0)
+				continue;
+			/* parse header lines */
+			for(i = 0; i < endofheaders - 1; i++) {
+				if(colon <= linestart && header_buf[i]==':')
+				{
+					colon = i;
+					while(i < (endofheaders-1)
+					      && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
+						i++;
+					valuestart = i + 1;
+				}
+				/* detecting end of line */
+				else if(header_buf[i]=='\r' || header_buf[i]=='\n')
+				{
+					if(colon > linestart && valuestart > colon)
+					{
+#ifdef DEBUG
+						printf("header='%.*s', value='%.*s'\n",
+						       colon-linestart, header_buf+linestart,
+						       i-valuestart, header_buf+valuestart);
+#endif
+						if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
+						{
+							content_length = atoi(header_buf+valuestart);
+#ifdef DEBUG
+							printf("Content-Length: %d\n", content_length);
+#endif
+						}
+						else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
+						   && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
+						{
+#ifdef DEBUG
+							printf("chunked transfer-encoding!\n");
+#endif
+							chunked = 1;
+						}
+					}
+					while(header_buf[i]=='\r' || header_buf[i] == '\n')
+						i++;
+					linestart = i;
+					colon = linestart;
+					valuestart = 0;
+				} 
+			}
+			/* copy the remaining of the received data back to buf */
+			n = header_buf_used - endofheaders;
+			memcpy(buf, header_buf + endofheaders, n);
+			/* if(headers) */
+		}
+		if(endofheaders)
+		{
+			/* content */
+			if(chunked)
+			{
+				int i = 0;
+				while(i < n)
+				{
+					if(chunksize == 0)
+					{
+						/* reading chunk size */
+						if(chunksize_buf_index == 0) {
+							/* skipping any leading CR LF */
+							if(i<n && buf[i] == '\r') i++;
+							if(i<n && buf[i] == '\n') i++;
+						}
+						while(i<n && isxdigit(buf[i])
+						     && chunksize_buf_index < (sizeof(chunksize_buf)-1))
+						{
+							chunksize_buf[chunksize_buf_index++] = buf[i];
+							chunksize_buf[chunksize_buf_index] = '\0';
+							i++;
+						}
+						while(i<n && buf[i] != '\r' && buf[i] != '\n')
+							i++; /* discarding chunk-extension */
+						if(i<n && buf[i] == '\r') i++;
+						if(i<n && buf[i] == '\n') {
+							int j;
+							for(j = 0; j < chunksize_buf_index; j++) {
+							if(chunksize_buf[j] >= '0'
+							   && chunksize_buf[j] <= '9')
+								chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
+							else
+								chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
+							}
+							chunksize_buf[0] = '\0';
+							chunksize_buf_index = 0;
+							i++;
+						} else {
+							/* not finished to get chunksize */
+							continue;
+						}
+#ifdef DEBUG
+						printf("chunksize = %u (%x)\n", chunksize, chunksize);
+#endif
+						if(chunksize == 0)
+						{
+#ifdef DEBUG
+							printf("end of HTTP content - %d %d\n", i, n);
+							/*printf("'%.*s'\n", n-i, buf+i);*/
+#endif
+							goto end_of_stream;
+						}
+					}
+					bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i);
+					if((int)(content_buf_used + bytestocopy) > content_buf_len)
+					{
+						if(content_length >= content_buf_used + (int)bytestocopy) {
+							content_buf_len = content_length;
+						} else {
+							content_buf_len = content_buf_used + (int)bytestocopy;
+						}
+						content_buf = (char *)realloc((void *)content_buf, 
+						                              content_buf_len);
+					}
+					memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
+					content_buf_used += bytestocopy;
+					i += bytestocopy;
+					chunksize -= bytestocopy;
+				}
+			}
+			else
+			{
+				/* not chunked */
+				if(content_length > 0
+				   && (content_buf_used + n) > content_length) {
+					/* skipping additional bytes */
+					n = content_length - content_buf_used;
+				}
+				if(content_buf_used + n > content_buf_len)
+				{
+					if(content_length >= content_buf_used + n) {
+						content_buf_len = content_length;
+					} else {
+						content_buf_len = content_buf_used + n;
+					}
+					content_buf = (char *)realloc((void *)content_buf, 
+					                              content_buf_len);
+				}
+				memcpy(content_buf + content_buf_used, buf, n);
+				content_buf_used += n;
+			}
+		}
+		/* use the Content-Length header value if available */
+		if(content_length > 0 && content_buf_used >= content_length)
+		{
+#ifdef DEBUG
+			printf("End of HTTP content\n");
+#endif
+			break;
+		}
+	}
+end_of_stream:
+	free(header_buf); header_buf = NULL;
+	*size = content_buf_used;
+	if(content_buf_used == 0)
+	{
+		free(content_buf);
+		content_buf = NULL;
+	}
+	return content_buf;
+}
+
+/* miniwget3() :
+ * do all the work.
+ * Return NULL if something failed. */
+static void *
+miniwget3(const char * url, const char * host,
+          unsigned short port, const char * path,
+          int * size, char * addr_str, int addr_str_len,
+          const char * httpversion)
+{
+	char buf[2048];
+    int s;
+	int n;
+	int len;
+	int sent;
+	void * content;
+
+	*size = 0;
+	s = connecthostport(host, port);
+	if(s < 0)
+		return NULL;
+
+	/* get address for caller ! */
+	if(addr_str)
+	{
+		struct sockaddr_storage saddr;
+		socklen_t saddrlen;
+
+		saddrlen = sizeof(saddr);
+		if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
+		{
+			perror("getsockname");
+		}
+		else
+		{
+#if defined(__amigaos__) && !defined(__amigaos4__)
+	/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
+     * But his function make a string with the port :  nn.nn.nn.nn:port */
+/*		if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
+                            NULL, addr_str, (DWORD *)&addr_str_len))
+		{
+		    printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
+		}*/
+			/* the following code is only compatible with ip v4 addresses */
+			strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
+#else
+#if 0
+			if(saddr.sa_family == AF_INET6) {
+				inet_ntop(AF_INET6,
+				          &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
+				          addr_str, addr_str_len);
+			} else {
+				inet_ntop(AF_INET,
+				          &(((struct sockaddr_in *)&saddr)->sin_addr),
+				          addr_str, addr_str_len);
+			}
+#endif
+			/* getnameinfo return ip v6 address with the scope identifier
+			 * such as : 2a01:e35:8b2b:7330::%4281128194 */
+			n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
+			                addr_str, addr_str_len,
+			                NULL, 0,
+			                NI_NUMERICHOST | NI_NUMERICSERV);
+			if(n != 0) {
+#ifdef WIN32
+				fprintf(stderr, "getnameinfo() failed : %d\n", n);
+#else
+				fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
+#endif
+			}
+#endif
+		}
+#ifdef DEBUG
+		printf("address miniwget : %s\n", addr_str);
+#endif
+	}
+
+	len = snprintf(buf, sizeof(buf),
+                 "GET %s HTTP/%s\r\n"
+			     "Host: %s:%d\r\n"
+				 "Connection: Close\r\n"
+				 "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
+
+				 "\r\n",
+			   path, httpversion, host, port);
+	sent = 0;
+	/* sending the HTTP request */
+	while(sent < len)
+	{
+		n = send(s, buf+sent, len-sent, 0);
+		if(n < 0)
+		{
+			perror("send");
+			closesocket(s);
+			return NULL;
+		}
+		else
+		{
+			sent += n;
+		}
+	}
+	content = getHTTPResponse(s, size);
+	closesocket(s);
+	return content;
+}
+
+/* miniwget2() :
+ * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
+static void *
+miniwget2(const char * url, const char * host,
+		  unsigned short port, const char * path,
+		  int * size, char * addr_str, int addr_str_len)
+{
+	char * respbuffer;
+
+	respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
+/*
+	respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0");
+	if (*size == 0)
+	{
+#ifdef DEBUG
+		printf("Retrying with HTTP/1.1\n");
+#endif
+		free(respbuffer);
+		respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
+	}
+*/
+	return respbuffer;
+}
+
+
+
+
+/* parseURL()
+ * arguments :
+ *   url :		source string not modified
+ *   hostname :	hostname destination string (size of MAXHOSTNAMELEN+1)
+ *   port :		port (destination)
+ *   path :		pointer to the path part of the URL 
+ *
+ * Return values :
+ *    0 - Failure
+ *    1 - Success         */
+int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
+{
+	char * p1, *p2, *p3;
+	if(!url)
+		return 0;
+	p1 = strstr(url, "://");
+	if(!p1)
+		return 0;
+	p1 += 3;
+	if(  (url[0]!='h') || (url[1]!='t')
+	   ||(url[2]!='t') || (url[3]!='p'))
+		return 0;
+	memset(hostname, 0, MAXHOSTNAMELEN + 1);
+	if(*p1 == '[')
+	{
+		/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
+		p2 = strchr(p1, ']');
+		p3 = strchr(p1, '/');
+		if(p2 && p3)
+		{
+			p2++;
+			strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+			if(*p2 == ':')
+			{
+				*port = 0;
+				p2++;
+				while( (*p2 >= '0') && (*p2 <= '9'))
+				{
+					*port *= 10;
+					*port += (unsigned short)(*p2 - '0');
+					p2++;
+				}
+			}
+			else
+			{
+				*port = 80;
+			}
+			*path = p3;
+			return 1;
+		}
+	}
+	p2 = strchr(p1, ':');
+	p3 = strchr(p1, '/');
+	if(!p3)
+		return 0;
+	if(!p2 || (p2>p3))
+	{
+		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
+		*port = 80;
+	}
+	else
+	{
+		strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
+		*port = 0;
+		p2++;
+		while( (*p2 >= '0') && (*p2 <= '9'))
+		{
+			*port *= 10;
+			*port += (unsigned short)(*p2 - '0');
+			p2++;
+		}
+	}
+	*path = p3;
+	return 1;
+}
+
+void * miniwget(const char * url, int * size)
+{
+	unsigned short port;
+	char * path;
+	/* protocol://host:port/chemin */
+	char hostname[MAXHOSTNAMELEN+1];
+	*size = 0;
+	if(!parseURL(url, hostname, &port, &path))
+		return NULL;
+#ifdef DEBUG
+	printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
+#endif
+	return miniwget2(url, hostname, port, path, size, 0, 0);
+}
+
+void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen)
+{
+	unsigned short port;
+	char * path;
+	/* protocol://host:port/chemin */
+	char hostname[MAXHOSTNAMELEN+1];
+	*size = 0;
+	if(addr)
+		addr[0] = '\0';
+	if(!parseURL(url, hostname, &port, &path))
+		return NULL;
+#ifdef DEBUG
+	printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path);
+#endif
+	return miniwget2(url, hostname, port, path, size, addr, addrlen);
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h
new file mode 100644
index 0000000..8314b40
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/miniwget.h
@@ -0,0 +1,30 @@
+/* $Id: miniwget.h,v 1.6 2010/12/09 16:11:33 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIWGET_H__
+#define __MINIWGET_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC void * getHTTPResponse(int s, int * size);
+
+LIBSPEC void * miniwget(const char *, int *);
+
+LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int);
+
+int parseURL(const char *, char *, unsigned short *, char * *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c
new file mode 100644
index 0000000..8b5594c
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.c
@@ -0,0 +1,216 @@
+/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */
+/* minixml.c : the minimum size a xml parser can be ! */
+/* Project : miniupnp
+ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+
+Copyright (c) 2005-2011, Thomas BERNARD 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name of the author may not be used to endorse or promote products
+	  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <string.h>
+#include "minixml.h"
+
+/* parseatt : used to parse the argument list
+ * return 0 (false) in case of success and -1 (true) if the end
+ * of the xmlbuffer is reached. */
+static int parseatt(struct xmlparser * p)
+{
+	const char * attname;
+	int attnamelen;
+	const char * attvalue;
+	int attvaluelen;
+	while(p->xml < p->xmlend)
+	{
+		if(*p->xml=='/' || *p->xml=='>')
+			return 0;
+		if( !IS_WHITE_SPACE(*p->xml) )
+		{
+			char sep;
+			attname = p->xml;
+			attnamelen = 0;
+			while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
+			{
+				attnamelen++; p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			while(*(p->xml++) != '=')
+			{
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			while(IS_WHITE_SPACE(*p->xml))
+			{
+				p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+			}
+			sep = *p->xml;
+			if(sep=='\'' || sep=='\"')
+			{
+				p->xml++;
+				if(p->xml >= p->xmlend)
+					return -1;
+				attvalue = p->xml;
+				attvaluelen = 0;
+				while(*p->xml != sep)
+				{
+					attvaluelen++; p->xml++;
+					if(p->xml >= p->xmlend)
+						return -1;
+				}
+			}
+			else
+			{
+				attvalue = p->xml;
+				attvaluelen = 0;
+				while(   !IS_WHITE_SPACE(*p->xml)
+					  && *p->xml != '>' && *p->xml != '/')
+				{
+					attvaluelen++; p->xml++;
+					if(p->xml >= p->xmlend)
+						return -1;
+				}
+			}
+			/*printf("%.*s='%.*s'\n",
+			       attnamelen, attname, attvaluelen, attvalue);*/
+			if(p->attfunc)
+				p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
+		}
+		p->xml++;
+	}
+	return -1;
+}
+
+/* parseelt parse the xml stream and
+ * call the callback functions when needed... */
+static void parseelt(struct xmlparser * p)
+{
+	int i;
+	const char * elementname;
+	while(p->xml < (p->xmlend - 1))
+	{
+		if((p->xml)[0]=='<' && (p->xml)[1]!='?')
+		{
+			i = 0; elementname = ++p->xml;
+			while( !IS_WHITE_SPACE(*p->xml)
+				  && (*p->xml!='>') && (*p->xml!='/')
+				 )
+			{
+				i++; p->xml++;
+				if (p->xml >= p->xmlend)
+					return;
+				/* to ignore namespace : */
+				if(*p->xml==':')
+				{
+					i = 0;
+					elementname = ++p->xml;
+				}
+			}
+			if(i>0)
+			{
+				if(p->starteltfunc)
+					p->starteltfunc(p->data, elementname, i);
+				if(parseatt(p))
+					return;
+				if(*p->xml!='/')
+				{
+					const char * data;
+					i = 0; data = ++p->xml;
+					if (p->xml >= p->xmlend)
+						return;
+					while( IS_WHITE_SPACE(*p->xml) )
+					{
+						i++; p->xml++;
+						if (p->xml >= p->xmlend)
+							return;
+					}
+					if(memcmp(p->xml, "<![CDATA[", 9) == 0)
+					{ 
+						/* CDATA handling */
+						p->xml += 9;
+						data = p->xml;
+						i = 0;
+						while(memcmp(p->xml, "]]>", 3) != 0)
+						{
+							i++; p->xml++;
+							if ((p->xml + 3) >= p->xmlend)
+								return;
+						}
+						if(i>0 && p->datafunc)
+							p->datafunc(p->data, data, i);
+						while(*p->xml!='<')
+						{
+							p->xml++;
+							if (p->xml >= p->xmlend)
+								return;
+						}
+					}
+					else
+					{
+						while(*p->xml!='<')
+						{
+							i++; p->xml++;
+							if ((p->xml + 1) >= p->xmlend)
+								return;
+						}
+						if(i>0 && p->datafunc && *(p->xml + 1) == '/')
+							p->datafunc(p->data, data, i);
+					}
+				}
+			}
+			else if(*p->xml == '/')
+			{
+				i = 0; elementname = ++p->xml;
+				if (p->xml >= p->xmlend)
+					return;
+				while((*p->xml != '>'))
+				{
+					i++; p->xml++;
+					if (p->xml >= p->xmlend)
+						return;
+				}
+				if(p->endeltfunc)
+					p->endeltfunc(p->data, elementname, i);
+				p->xml++;
+			}
+		}
+		else
+		{
+			p->xml++;
+		}
+	}
+}
+
+/* the parser must be initialized before calling this function */
+void parsexml(struct xmlparser * parser)
+{
+	parser->xml = parser->xmlstart;
+	parser->xmlend = parser->xmlstart + parser->xmlsize;
+	parseelt(parser);
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h
new file mode 100644
index 0000000..857c70e
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixml.h
@@ -0,0 +1,37 @@
+/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
+/* minimal xml parser
+ *
+ * Project : miniupnp
+ * Website : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#ifndef __MINIXML_H__
+#define __MINIXML_H__
+#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
+
+/* if a callback function pointer is set to NULL,
+ * the function is not called */
+struct xmlparser {
+	const char *xmlstart;
+	const char *xmlend;
+	const char *xml;	/* pointer to current character */
+	int xmlsize;
+	void * data;
+	void (*starteltfunc) (void *, const char *, int);
+	void (*endeltfunc) (void *, const char *, int);
+	void (*datafunc) (void *, const char *, int);
+	void (*attfunc) (void *, const char *, int, const char *, int);
+};
+
+/* parsexml()
+ * the xmlparser structure must be initialized before the call
+ * the following structure members have to be initialized :
+ * xmlstart, xmlsize, data, *func
+ * xml is for internal usage, xmlend is computed automatically */
+void parsexml(struct xmlparser *);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c
new file mode 100644
index 0000000..766211b
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/minixmlvalid.c
@@ -0,0 +1,156 @@
+/* $Id: minixmlvalid.c,v 1.4 2011/02/07 13:44:57 nanard Exp $ */
+/* MiniUPnP Project
+ * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
+ * minixmlvalid.c :
+ * validation program for the minixml parser
+ *
+ * (c) 2006-2011 Thomas Bernard */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "minixml.h"
+
+/* xml event structure */
+struct event {
+	enum { ELTSTART, ELTEND, ATT, CHARDATA } type;
+	const char * data;
+	int len;
+};
+
+struct eventlist {
+	int n;
+	struct event * events;
+};
+
+/* compare 2 xml event lists
+ * return 0 if the two lists are equals */
+int evtlistcmp(struct eventlist * a, struct eventlist * b)
+{
+	int i;
+	struct event * ae, * be;
+	if(a->n != b->n)
+	{
+		printf("event number not matching : %d != %d\n", a->n, b->n);
+		//return 1;
+	}
+	for(i=0; i<a->n; i++)
+	{
+		ae = a->events + i;
+		be = b->events + i;
+		if(  (ae->type != be->type)
+		   ||(ae->len != be->len)
+		   ||memcmp(ae->data, be->data, ae->len))
+		{
+			printf("Found a difference : %d '%.*s' != %d '%.*s'\n",
+			       ae->type, ae->len, ae->data,
+			       be->type, be->len, be->data);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Test data */
+static const char xmldata[] =
+"<xmlroot>\n"
+" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
+"character data"
+"</elt1> \n \t"
+"<elt1b/>"
+"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
+"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
+"</xmlroot>";
+
+static const struct event evtref[] =
+{
+	{ELTSTART, "xmlroot", 7},
+	{ELTSTART, "elt1", 4},
+	/* attributes */
+	{CHARDATA, "character data", 14},
+	{ELTEND, "elt1", 4},
+	{ELTSTART, "elt1b", 5},
+	{ELTSTART, "elt1", 4},
+	{CHARDATA, " <html>stuff !\n ", 16},
+	{ELTEND, "elt1", 4},
+	{ELTSTART, "elt2a", 5},
+	{ELTSTART, "elt2b", 5},
+	{CHARDATA, "chardata1", 9},
+	{ELTEND, "elt2b", 5},
+	{ELTSTART, "elt2b", 5},
+	{CHARDATA, " chardata2 ", 11},
+	{ELTEND, "elt2b", 5},
+	{ELTEND, "elt2a", 5},
+	{ELTEND, "xmlroot", 7}
+};	
+
+void startelt(void * data, const char * p, int l)
+{
+	struct eventlist * evtlist = data;
+	struct event * evt;
+	evt = evtlist->events + evtlist->n;
+	/*printf("startelt : %.*s\n", l, p);*/
+	evt->type = ELTSTART;
+	evt->data = p;
+	evt->len = l;
+	evtlist->n++;
+}
+
+void endelt(void * data, const char * p, int l)
+{
+	struct eventlist * evtlist = data;
+	struct event * evt;
+	evt = evtlist->events + evtlist->n;
+	/*printf("endelt : %.*s\n", l, p);*/
+	evt->type = ELTEND;
+	evt->data = p;
+	evt->len = l;
+	evtlist->n++;
+}
+
+void chardata(void * data, const char * p, int l)
+{
+	struct eventlist * evtlist = data;
+	struct event * evt;
+	evt = evtlist->events + evtlist->n;
+	/*printf("chardata : '%.*s'\n", l, p);*/
+	evt->type = CHARDATA;
+	evt->data = p;
+	evt->len = l;
+	evtlist->n++;
+}
+
+int testxmlparser(const char * xml, int size)
+{
+	int r;
+	struct eventlist evtlist;
+	struct eventlist evtlistref;
+	struct xmlparser parser;
+	evtlist.n = 0;
+	evtlist.events = malloc(sizeof(struct event)*100);
+	memset(&parser, 0, sizeof(parser));
+	parser.xmlstart = xml;
+	parser.xmlsize = size;
+	parser.data = &evtlist;
+	parser.starteltfunc = startelt;
+	parser.endeltfunc = endelt;
+	parser.datafunc = chardata;
+	parsexml(&parser);
+	printf("%d events\n", evtlist.n);
+	/* compare */
+	evtlistref.n = sizeof(evtref)/sizeof(struct event);
+	evtlistref.events = (struct event *)evtref;
+	r = evtlistcmp(&evtlistref, &evtlist);
+	free(evtlist.events);
+	return r;
+}
+
+int main(int argc, char * * argv)
+{
+	int r;
+	r = testxmlparser(xmldata, sizeof(xmldata)-1);
+	if(r)
+		printf("minixml validation test failed\n");
+	return r;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c
new file mode 100644
index 0000000..e09e80f
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.c
@@ -0,0 +1,157 @@
+/* $Id: portlistingparse.c,v 1.4 2011/03/18 11:02:17 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#include <string.h>
+#include <stdlib.h>
+#include "portlistingparse.h"
+#include "minixml.h"
+
+/* list of the elements */
+static const struct {
+	const portMappingElt code;
+	const char * const str;
+} elements[] = {
+	{ PortMappingEntry, "PortMappingEntry"},
+	{ NewRemoteHost, "NewRemoteHost"},
+	{ NewExternalPort, "NewExternalPort"},
+	{ NewProtocol, "NewProtocol"},
+	{ NewInternalPort, "NewInternalPort"},
+	{ NewInternalClient, "NewInternalClient"},
+	{ NewEnabled, "NewEnabled"},
+	{ NewDescription, "NewDescription"},
+	{ NewLeaseTime, "NewLeaseTime"},
+	{ PortMappingEltNone, NULL}
+};
+
+/* Helper function */
+static UNSIGNED_INTEGER
+atoui(const char * p, int l)
+{
+	UNSIGNED_INTEGER r = 0;
+	while(l > 0 && *p)
+	{
+		if(*p >= '0' && *p <= '9')
+			r = r*10 + (*p - '0');
+		else
+			break;
+		p++;
+		l--;
+	}
+	return r;
+}
+
+/* Start element handler */
+static void
+startelt(void * d, const char * name, int l)
+{
+	int i;
+	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+	pdata->curelt = PortMappingEltNone;
+	for(i = 0; elements[i].str; i++)
+	{
+		if(memcmp(name, elements[i].str, l) == 0)
+		{
+			pdata->curelt = elements[i].code;
+			break;
+		}
+	}
+	if(pdata->curelt == PortMappingEntry)
+	{
+		struct PortMapping * pm;
+		pm = calloc(1, sizeof(struct PortMapping));
+		LIST_INSERT_HEAD( &(pdata->head), pm, entries);
+	}
+}
+
+/* End element handler */
+static void
+endelt(void * d, const char * name, int l)
+{
+	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+	pdata->curelt = PortMappingEltNone;
+}
+
+/* Data handler */
+static void
+data(void * d, const char * data, int l)
+{
+	struct PortMapping * pm;
+	struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
+	pm = pdata->head.lh_first;
+	if(!pm)
+		return;
+	if(l > 63)
+		l = 63;
+	switch(pdata->curelt)
+	{
+	case NewRemoteHost:
+		memcpy(pm->remoteHost, data, l);
+		pm->remoteHost[l] = '\0';
+		break;
+	case NewExternalPort:
+		pm->externalPort = (unsigned short)atoui(data, l);
+		break;
+	case NewProtocol:
+		if(l > 3)
+			l = 3;
+		memcpy(pm->protocol, data, l);
+		pm->protocol[l] = '\0';
+		break;
+	case NewInternalPort:
+		pm->internalPort = (unsigned short)atoui(data, l);
+		break;
+	case NewInternalClient:
+		memcpy(pm->internalClient, data, l);
+		pm->internalClient[l] = '\0';
+		break;
+	case NewEnabled:
+		pm->enabled = (unsigned char)atoui(data, l);
+		break;
+	case NewDescription:
+		memcpy(pm->description, data, l);
+		pm->description[l] = '\0';
+		break;
+	case NewLeaseTime:
+		pm->leaseTime = atoui(data, l);
+		break;
+	default:
+		break;
+	}
+}
+
+
+/* Parse the PortMappingList XML document for IGD version 2
+ */
+void
+ParsePortListing(const char * buffer, int bufsize,
+                 struct PortMappingParserData * pdata)
+{
+	struct xmlparser parser;
+
+	memset(pdata, 0, sizeof(struct PortMappingParserData));
+	LIST_INIT(&(pdata->head));
+	/* init xmlparser */
+	parser.xmlstart = buffer;
+	parser.xmlsize = bufsize;
+	parser.data = pdata;
+	parser.starteltfunc = startelt;
+	parser.endeltfunc = endelt;
+	parser.datafunc = data;
+	parser.attfunc = 0;
+	parsexml(&parser);
+}
+
+void
+FreePortListing(struct PortMappingParserData * pdata)
+{
+	struct PortMapping * pm;
+	while((pm = pdata->head.lh_first) != NULL)
+	{
+		LIST_REMOVE(pm, entries);
+		free(pm);
+	}
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h
new file mode 100644
index 0000000..1852478
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/portlistingparse.h
@@ -0,0 +1,71 @@
+/* $Id: portlistingparse.h,v 1.4 2011/02/15 23:03:56 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2011 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef __PORTLISTINGPARSE_H__
+#define __PORTLISTINGPARSE_H__
+
+#include "declspec.h"
+/* for the definition of UNSIGNED_INTEGER */
+#include "miniupnpctypes.h"
+
+#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__) 
+#include "bsdqueue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* sample of PortMappingEntry :
+  <p:PortMappingEntry>
+    <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
+    <p:NewExternalPort>2345</p:NewExternalPort>
+    <p:NewProtocol>TCP</p:NewProtocol>
+    <p:NewInternalPort>2345</p:NewInternalPort>
+    <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
+    <p:NewEnabled>1</p:NewEnabled>
+    <p:NewDescription>dooom</p:NewDescription>
+    <p:NewLeaseTime>345</p:NewLeaseTime>
+  </p:PortMappingEntry>
+ */
+typedef enum { PortMappingEltNone,
+       PortMappingEntry, NewRemoteHost,
+       NewExternalPort, NewProtocol,
+       NewInternalPort, NewInternalClient,
+       NewEnabled, NewDescription, 
+       NewLeaseTime } portMappingElt;
+
+struct PortMapping {
+	LIST_ENTRY(PortMapping) entries;
+	UNSIGNED_INTEGER leaseTime;
+	unsigned short externalPort;
+	unsigned short internalPort;
+	char remoteHost[64];
+	char internalClient[64];
+	char description[64];
+	char protocol[4];
+	unsigned char enabled;
+};
+
+struct PortMappingParserData {
+	LIST_HEAD(portmappinglisthead, PortMapping) head;
+	portMappingElt curelt;
+};
+
+LIBSPEC void
+ParsePortListing(const char * buffer, int bufsize,
+                 struct PortMappingParserData * pdata);
+
+LIBSPEC void
+FreePortListing(struct PortMappingParserData * pdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c
new file mode 100644
index 0000000..a1eadfc
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.c
@@ -0,0 +1,81 @@
+/* $Id: receivedata.c,v 1.1 2011/04/11 08:21:47 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <unistd.h>
+#if defined(__amigaos__) && !defined(__amigaos4__)
+#define socklen_t int
+#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/select.h>
+#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
+#include <sys/socket.h>
+#if !defined(__amigaos__) && !defined(__amigaos4__)
+#include <poll.h>
+#endif
+#include <errno.h>
+#define MINIUPNPC_IGNORE_EINTR
+#endif
+
+#ifdef WIN32
+#define PRINT_SOCKET_ERROR(x)    printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#else
+#define PRINT_SOCKET_ERROR(x) perror(x)
+#endif
+
+#include "receivedata.h"
+
+int
+receivedata(int socket, char * data, int length, int timeout)
+{
+    int n;
+#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
+	/* using poll */
+    struct pollfd fds[1]; /* for the poll */
+#ifdef MINIUPNPC_IGNORE_EINTR
+    do {
+#endif
+        fds[0].fd = socket;
+        fds[0].events = POLLIN;
+        n = poll(fds, 1, timeout);
+#ifdef MINIUPNPC_IGNORE_EINTR
+    } while(n < 0 && errno == EINTR);
+#endif
+    if(n < 0) {
+        PRINT_SOCKET_ERROR("poll");
+        return -1;
+    } else if(n == 0) {
+		/* timeout */
+        return 0;
+    }
+#else /* !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
+	/* using select under WIN32 and amigaos */
+    fd_set socketSet;
+    TIMEVAL timeval;
+    FD_ZERO(&socketSet);
+    FD_SET(socket, &socketSet);
+    timeval.tv_sec = timeout / 1000;
+    timeval.tv_usec = (timeout % 1000) * 1000;
+    n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
+    if(n < 0) {
+        PRINT_SOCKET_ERROR("select");
+        return -1;
+    } else if(n == 0) {
+        return 0;
+    }    
+#endif
+	n = recv(socket, data, length, 0);
+	if(n<0) {
+		PRINT_SOCKET_ERROR("recv");
+	}
+	return n;
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h
new file mode 100644
index 0000000..7a551b9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/receivedata.h
@@ -0,0 +1,17 @@
+/* $Id: receivedata.h,v 1.1 2011/04/11 08:21:47 nanard Exp $ */
+/* Project: miniupnp
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author: Thomas Bernard
+ * Copyright (c) 2011 Thomas Bernard
+ * This software is subjects to the conditions detailed
+ * in the LICENCE file provided within this distribution */
+#ifndef __RECEIVEDATA_H__
+#define __RECEIVEDATA_H__
+
+/* Reads data from the specified socket. 
+ * Returns the number of bytes read if successful, zero if no bytes were 
+ * read or if we timed out. Returns negative if there was an error. */
+int receivedata(int socket, char * data, int length, int timeout);
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c
new file mode 100644
index 0000000..b136d9d
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpc.c
@@ -0,0 +1,683 @@
+/* $Id: upnpc.c,v 1.88 2011/06/17 23:31:01 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef WIN32
+#include <winsock2.h>
+#define snprintf _snprintf
+#endif
+#include "miniwget.h"
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#include "upnperrors.h"
+
+/* protofix() checks if protocol is "UDP" or "TCP" 
+ * returns NULL if not */
+const char * protofix(const char * proto)
+{
+	static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
+	static const char proto_udp[4] = { 'U', 'D', 'P', 0};
+	int i, b;
+	for(i=0, b=1; i<4; i++)
+		b = b && (   (proto[i] == proto_tcp[i]) 
+		          || (proto[i] == (proto_tcp[i] | 32)) );
+	if(b)
+		return proto_tcp;
+	for(i=0, b=1; i<4; i++)
+		b = b && (   (proto[i] == proto_udp[i])
+		          || (proto[i] == (proto_udp[i] | 32)) );
+	if(b)
+		return proto_udp;
+	return 0;
+}
+
+static void DisplayInfos(struct UPNPUrls * urls,
+                         struct IGDdatas * data)
+{
+	char externalIPAddress[40];
+	char connectionType[64];
+	char status[64];
+	char lastconnerr[64];
+	unsigned int uptime;
+	unsigned int brUp, brDown;
+	time_t timenow, timestarted;
+	int r;
+	UPNP_GetConnectionTypeInfo(urls->controlURL,
+	                           data->first.servicetype,
+							   connectionType);
+	if(connectionType[0])
+		printf("Connection Type : %s\n", connectionType);
+	else
+		printf("GetConnectionTypeInfo failed.\n");
+	UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
+	                   status, &uptime, lastconnerr);
+	printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
+	       status, uptime, lastconnerr);
+	timenow = time(NULL);
+	timestarted = timenow - uptime;
+	printf("  Time started : %s", ctime(&timestarted));
+	UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
+			&brDown, &brUp);
+	printf("MaxBitRateDown : %u bps", brDown);
+	if(brDown >= 1000000) {
+		printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
+	} else if(brDown >= 1000) {
+		printf(" (%u Kbps)", brDown / 1000);
+	}
+	printf("   MaxBitRateUp %u bps", brUp);
+	if(brUp >= 1000000) {
+		printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
+	} else if(brUp >= 1000) {
+		printf(" (%u Kbps)", brUp / 1000);
+	}
+	printf("\n");
+	r = UPNP_GetExternalIPAddress(urls->controlURL,
+	                          data->first.servicetype,
+							  externalIPAddress);
+	if(r != UPNPCOMMAND_SUCCESS)
+		printf("GetExternalIPAddress() returned %d\n", r);
+	if(externalIPAddress[0])
+		printf("ExternalIPAddress = %s\n", externalIPAddress);
+	else
+		printf("GetExternalIPAddress failed.\n");
+}
+
+static void GetConnectionStatus(struct UPNPUrls * urls,
+                               struct IGDdatas * data)
+{
+	unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+	DisplayInfos(urls, data);
+	bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+	bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+	packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+	packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+	printf("Bytes:   Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+	printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+static void ListRedirections(struct UPNPUrls * urls,
+                             struct IGDdatas * data)
+{
+	int r;
+	int i = 0;
+	char index[6];
+	char intClient[40];
+	char intPort[6];
+	char extPort[6];
+	char protocol[4];
+	char desc[80];
+	char enabled[6];
+	char rHost[64];
+	char duration[16];
+	/*unsigned int num=0;
+	UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
+	printf("PortMappingNumberOfEntries : %u\n", num);*/
+	do {
+		snprintf(index, 6, "%d", i);
+		rHost[0] = '\0'; enabled[0] = '\0';
+		duration[0] = '\0'; desc[0] = '\0';
+		extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
+		r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
+		                               data->first.servicetype,
+		                               index,
+		                               extPort, intClient, intPort,
+									   protocol, desc, enabled,
+									   rHost, duration);
+		if(r==0)
+		/*
+			printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
+			       "     desc='%s' rHost='%s'\n",
+			       i, protocol, extPort, intClient, intPort,
+				   enabled, duration,
+				   desc, rHost);
+				   */
+			printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
+			       i, protocol, extPort, intClient, intPort,
+			       desc, rHost, duration);
+		else
+			printf("GetGenericPortMappingEntry() returned %d (%s)\n",
+			       r, strupnperror(r));
+		i++;
+	} while(r==0);
+}
+
+static void NewListRedirections(struct UPNPUrls * urls,
+                                struct IGDdatas * data)
+{
+	int r;
+	int i = 0;
+	struct PortMappingParserData pdata;
+	struct PortMapping * pm;
+
+	memset(&pdata, 0, sizeof(struct PortMappingParserData));
+	r = UPNP_GetListOfPortMappings(urls->controlURL,
+                                   data->first.servicetype,
+	                               "0",
+	                               "65535",
+	                               "TCP",
+	                               "1000",
+	                               &pdata);
+	if(r == UPNPCOMMAND_SUCCESS)
+	{
+		for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
+		{
+			printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+			       i, pm->protocol, pm->externalPort, pm->internalClient,
+			       pm->internalPort,
+			       pm->description, pm->remoteHost,
+			       (unsigned)pm->leaseTime);
+			i++;
+		}
+		FreePortListing(&pdata);
+	}
+	else
+	{
+		printf("GetListOfPortMappings() returned %d (%s)\n",
+		       r, strupnperror(r));
+	}
+	r = UPNP_GetListOfPortMappings(urls->controlURL,
+                                   data->first.servicetype,
+	                               "0",
+	                               "65535",
+	                               "UDP",
+	                               "1000",
+	                               &pdata);
+	if(r == UPNPCOMMAND_SUCCESS)
+	{
+		for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next)
+		{
+			printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
+			       i, pm->protocol, pm->externalPort, pm->internalClient,
+			       pm->internalPort,
+			       pm->description, pm->remoteHost,
+			       (unsigned)pm->leaseTime);
+			i++;
+		}
+		FreePortListing(&pdata);
+	}
+	else
+	{
+		printf("GetListOfPortMappings() returned %d (%s)\n",
+		       r, strupnperror(r));
+	}
+}
+
+/* Test function 
+ * 1 - get connection type
+ * 2 - get extenal ip address
+ * 3 - Add port mapping
+ * 4 - get this port mapping from the IGD */
+static void SetRedirectAndTest(struct UPNPUrls * urls,
+                               struct IGDdatas * data,
+							   const char * iaddr,
+							   const char * iport,
+							   const char * eport,
+                               const char * proto,
+                               const char * leaseDuration)
+{
+	char externalIPAddress[40];
+	char intClient[40];
+	char intPort[6];
+	char duration[16];
+	int r;
+
+	if(!iaddr || !iport || !eport || !proto)
+	{
+		fprintf(stderr, "Wrong arguments\n");
+		return;
+	}
+	proto = protofix(proto);
+	if(!proto)
+	{
+		fprintf(stderr, "invalid protocol\n");
+		return;
+	}
+	
+	UPNP_GetExternalIPAddress(urls->controlURL,
+	                          data->first.servicetype,
+							  externalIPAddress);
+	if(externalIPAddress[0])
+		printf("ExternalIPAddress = %s\n", externalIPAddress);
+	else
+		printf("GetExternalIPAddress failed.\n");
+	
+	r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
+	                        eport, iport, iaddr, 0, proto, 0, leaseDuration);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
+		       eport, iport, iaddr, r, strupnperror(r));
+
+	r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
+	                                 data->first.servicetype,
+    	                             eport, proto,
+									 intClient, intPort, NULL/*desc*/,
+	                                 NULL/*enabled*/, duration);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
+		       r, strupnperror(r));
+	
+	if(intClient[0]) {
+		printf("InternalIP:Port = %s:%s\n", intClient, intPort);
+		printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
+		       externalIPAddress, eport, proto, intClient, intPort, duration);
+	}
+}
+
+static void
+RemoveRedirect(struct UPNPUrls * urls,
+               struct IGDdatas * data,
+			   const char * eport,
+			   const char * proto)
+{
+	int r;
+	if(!proto || !eport)
+	{
+		fprintf(stderr, "invalid arguments\n");
+		return;
+	}
+	proto = protofix(proto);
+	if(!proto)
+	{
+		fprintf(stderr, "protocol invalid\n");
+		return;
+	}
+	r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, 0);
+	printf("UPNP_DeletePortMapping() returned : %d\n", r);
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */
+static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
+{
+	unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
+	int firewallEnabled = 0, inboundPinholeAllowed = 0;
+
+	UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
+	printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
+	printf("GetFirewallStatus:\n   Firewall Enabled: %s\n   Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
+	
+	bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
+	bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
+	packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
+	packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
+	printf("Bytes:   Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
+	printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
+}
+
+/* Test function 
+ * 1 - Add pinhole
+ * 2 - Check if pinhole is working from the IGD side */
+static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
+					const char * remoteaddr, const char * eport,
+					const char * intaddr, const char * iport,
+					const char * proto, const char * lease_time)
+{
+	char uniqueID[8];
+	//int isWorking = 0;
+	int r;
+
+	if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
+	{
+		fprintf(stderr, "Wrong arguments\n");
+		return;
+	}
+	/*proto = protofix(proto);
+	if(!proto)
+	{
+		fprintf(stderr, "invalid protocol\n");
+		return;
+	}*/
+	r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+		       intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+	else
+	{
+		printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n", intaddr, iport, remoteaddr, eport, uniqueID);
+		/*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
+		if(r!=UPNPCOMMAND_SUCCESS)
+			printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+		printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
+	}
+}
+
+/* Test function
+ * 1 - Check if pinhole is working from the IGD side
+ * 2 - Update pinhole */
+static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
+					const char * uniqueID, const char * lease_time)
+{
+	int isWorking = 0;
+	int r;
+
+	if(!uniqueID || !lease_time)
+	{
+		fprintf(stderr, "Wrong arguments\n");
+		return;
+	}
+	r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+	printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+	if(isWorking || r==709)
+	{
+		r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
+		printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
+		if(r!=UPNPCOMMAND_SUCCESS)
+			printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
+	}
+}
+
+/* Test function 
+ * Get pinhole timeout
+ */
+static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
+					const char * remoteaddr, const char * eport,
+					const char * intaddr, const char * iport,
+					const char * proto)
+{
+	int timeout = 0;
+	int r;
+
+	if(!intaddr || !remoteaddr || !iport || !eport || !proto)
+	{
+		fprintf(stderr, "Wrong arguments\n");
+		return;
+	}
+
+	r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
+		       intaddr, iport, remoteaddr, eport, r, strupnperror(r));
+	else
+		printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
+}
+
+static void
+GetPinholePackets(struct UPNPUrls * urls,
+               struct IGDdatas * data, const char * uniqueID)
+{
+	int r, pinholePackets = 0;
+	if(!uniqueID)
+	{
+		fprintf(stderr, "invalid arguments\n");
+		return;
+	}
+	r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
+	else
+		printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
+}
+
+static void
+CheckPinhole(struct UPNPUrls * urls,
+               struct IGDdatas * data, const char * uniqueID)
+{
+	int r, isWorking = 0;
+	if(!uniqueID)
+	{
+		fprintf(stderr, "invalid arguments\n");
+		return;
+	}
+	r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
+	if(r!=UPNPCOMMAND_SUCCESS)
+		printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
+	else
+		printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
+}
+
+static void
+RemovePinhole(struct UPNPUrls * urls,
+               struct IGDdatas * data, const char * uniqueID)
+{
+	int r;
+	if(!uniqueID)
+	{
+		fprintf(stderr, "invalid arguments\n");
+		return;
+	}
+	r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
+	printf("UPNP_DeletePinhole() returned : %d\n", r);
+}
+
+
+/* sample upnp client program */
+int main(int argc, char ** argv)
+{
+	char command = 0;
+	char ** commandargv = 0;
+	int commandargc = 0;
+	struct UPNPDev * devlist = 0;
+	char lanaddr[64];	/* my ip address on the LAN */
+	int i;
+	const char * rootdescurl = 0;
+	const char * multicastif = 0;
+	const char * minissdpdpath = 0;
+	int retcode = 0;
+	int error = 0;
+	int ipv6 = 0;
+
+#ifdef WIN32
+	WSADATA wsaData;
+	int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+	if(nResult != NO_ERROR)
+	{
+		fprintf(stderr, "WSAStartup() failed.\n");
+		return -1;
+	}
+#endif
+    printf("upnpc : miniupnpc library test client. (c) 2006-2011 Thomas Bernard\n");
+    printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+	       "for more information.\n");
+	/* command line processing */
+	for(i=1; i<argc; i++)
+	{
+		if(argv[i][0] == '-')
+		{
+			if(argv[i][1] == 'u')
+				rootdescurl = argv[++i];
+			else if(argv[i][1] == 'm')
+				multicastif = argv[++i];
+			else if(argv[i][1] == 'p')
+				minissdpdpath = argv[++i];
+			else if(argv[i][1] == '6')
+				ipv6 = 1;
+			else
+			{
+				command = argv[i][1];
+				i++;
+				commandargv = argv + i;
+				commandargc = argc - i;
+				break;
+			}
+		}
+		else
+		{
+			fprintf(stderr, "option '%s' invalid\n", argv[i]);
+		}
+	}
+
+	if(!command || (command == 'a' && commandargc<4)
+	   || (command == 'd' && argc<2)
+	   || (command == 'r' && argc<2)
+	   || (command == 'A' && commandargc<6)
+	   || (command == 'U' && commandargc<2)
+	   || (command == 'D' && commandargc<1))
+	{
+		fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -d external_port protocol [port2 protocol2] [...]\n\t\tDelete port redirection\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -l\n\t\tList redirections\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings, IGD v2)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -r port1 protocol1 [port2 protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
+		fprintf(stderr, "       \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
+		fprintf(stderr, "\nprotocol is UDP or TCP\n");
+		fprintf(stderr, "Options:\n");
+		fprintf(stderr, "  -6 : use ip v6 instead of ip v4.\n");
+		fprintf(stderr, "  -u url : bypass discovery process by providing the XML root description url.\n");
+		fprintf(stderr, "  -m address/interface : provide ip address (ip v4) or interface name (ip v6) to use for sending SSDP multicast packets.\n");
+		fprintf(stderr, "  -p path : use this path for MiniSSDPd socket.\n");
+		return 1;
+	}
+
+	if( rootdescurl
+	  || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
+	                             0/*sameport*/, ipv6, &error)))
+	{
+		struct UPNPDev * device;
+		struct UPNPUrls urls;
+		struct IGDdatas data;
+		if(devlist)
+		{
+			printf("List of UPNP devices found on the network :\n");
+			for(device = devlist; device; device = device->pNext)
+			{
+				printf(" desc: %s\n st: %s\n\n",
+					   device->descURL, device->st);
+			}
+		}
+		else
+		{
+			printf("upnpDiscover() error code=%d\n", error);
+		}
+		i = 1;
+		if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
+		  || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
+		{
+			switch(i) {
+			case 1:
+				printf("Found valid IGD : %s\n", urls.controlURL);
+				break;
+			case 2:
+				printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
+				printf("Trying to continue anyway\n");
+				break;
+			case 3:
+				printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
+				printf("Trying to continue anyway\n");
+				break;
+			default:
+				printf("Found device (igd ?) : %s\n", urls.controlURL);
+				printf("Trying to continue anyway\n");
+			}
+			printf("Local LAN ip address : %s\n", lanaddr);
+			#if 0
+			printf("getting \"%s\"\n", urls.ipcondescURL);
+			descXML = miniwget(urls.ipcondescURL, &descXMLsize);
+			if(descXML)
+			{
+				/*fwrite(descXML, 1, descXMLsize, stdout);*/
+				free(descXML); descXML = NULL;
+			}
+			#endif
+
+			switch(command)
+			{
+			case 'l':
+				DisplayInfos(&urls, &data);
+				ListRedirections(&urls, &data);
+				break;
+			case 'L':
+				NewListRedirections(&urls, &data);
+				break;
+			case 'a':
+				SetRedirectAndTest(&urls, &data,
+				                   commandargv[0], commandargv[1],
+				                   commandargv[2], commandargv[3],
+				                   (commandargc > 4)?commandargv[4]:"0");
+				break;
+			case 'd':
+				for(i=0; i<commandargc; i+=2)
+				{
+					RemoveRedirect(&urls, &data, commandargv[i], commandargv[i+1]);
+				}
+				break;
+			case 's':
+				GetConnectionStatus(&urls, &data);
+				break;
+			case 'r':
+				for(i=0; i<commandargc; i+=2)
+				{
+					/*printf("port %s protocol %s\n", argv[i], argv[i+1]);*/
+					SetRedirectAndTest(&urls, &data,
+					                   lanaddr, commandargv[i],
+									   commandargv[i], commandargv[i+1], "0");
+				}
+				break;
+			case 'A':
+				SetPinholeAndTest(&urls, &data,
+				                  commandargv[0], commandargv[1],
+				                  commandargv[2], commandargv[3],
+				                  commandargv[4], commandargv[5]);
+				break;
+			case 'U':
+				GetPinholeAndUpdate(&urls, &data,
+				                   commandargv[0], commandargv[1]);
+				break;
+			case 'C':
+				for(i=0; i<commandargc; i++)
+				{
+					CheckPinhole(&urls, &data, commandargv[i]);
+				}
+				break;
+			case 'K':
+				for(i=0; i<commandargc; i++)
+				{
+					GetPinholePackets(&urls, &data, commandargv[i]);
+				}
+				break;
+			case 'D':
+				for(i=0; i<commandargc; i++)
+				{
+					RemovePinhole(&urls, &data, commandargv[i]);
+				}
+				break;
+			case 'S':
+				GetFirewallStatus(&urls, &data);
+				break;
+			case 'G':
+				GetPinholeOutboundTimeout(&urls, &data,
+							commandargv[0], commandargv[1],
+							commandargv[2], commandargv[3],
+							commandargv[4]);
+				break;
+			case 'P':
+				printf("Presentation URL found:\n");
+				printf("            %s\n", data.presentationurl);
+				break;
+			default:
+				fprintf(stderr, "Unknown switch -%c\n", command);
+				retcode = 1;
+			}
+
+			FreeUPNPUrls(&urls);
+		}
+		else
+		{
+			fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
+			retcode = 1;
+		}
+		freeUPNPDevlist(devlist); devlist = 0;
+	}
+	else
+	{
+		fprintf(stderr, "No IGD UPnP Device found on the network !\n");
+		retcode = 1;
+	}
+	return retcode;
+}
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c
new file mode 100644
index 0000000..1114759
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.c
@@ -0,0 +1,1097 @@
+/* $Id: upnpcommands.c,v 1.37 2011/06/04 15:56:23 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+#include "portlistingparse.h"
+
+static UNSIGNED_INTEGER
+my_atoui(const char * s)
+{
+	return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+					const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	unsigned int r = 0;
+	char * p;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetTotalBytesSent", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	unsigned int r = 0;
+	char * p;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetTotalBytesReceived", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	unsigned int r = 0;
+	char * p;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetTotalPacketsSent", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/*
+ * */
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+						const char * servicetype)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	unsigned int r = 0;
+	char * p;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetTotalPacketsReceived", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
+	r = my_atoui(p);
+	ClearNameValueList(&pdata);
+	return r;
+}
+
+/* UPNP_GetStatusInfo() call the corresponding UPNP method
+ * returns the current status and uptime */
+LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+				const char * servicetype,
+				char * status, 
+				unsigned int * uptime,
+				char * lastconnerror)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	char * p;
+	char * up;
+	char * err;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!status && !uptime)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetStatusInfo", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	/*DisplayNameValueList(buffer, bufsize);*/
+	free(buffer); buffer = NULL;
+	up = GetValueFromNameValueList(&pdata, "NewUptime");
+	p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
+	err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
+	if(p && up)
+	  ret = UPNPCOMMAND_SUCCESS;
+
+	if(status) {
+		if(p){
+			strncpy(status, p, 64 );
+			status[63] = '\0';
+		}else
+			status[0]= '\0';
+	}
+
+	if(uptime) {
+		if(up)
+			sscanf(up,"%u",uptime);
+		else
+			uptime = 0;
+	}
+
+	if(lastconnerror) {
+		if(err) {
+			strncpy(lastconnerror, err, 64 );
+			lastconnerror[63] = '\0';
+		} else
+			lastconnerror[0] = '\0';
+	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
+ * returns the connection type */
+LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+                           const char * servicetype,
+                           char * connectionType)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!connectionType)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetConnectionTypeInfo", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "NewConnectionType");
+	/*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
+	/* PossibleConnectionTypes will have several values.... */
+	if(p) {
+		strncpy(connectionType, p, 64 );
+		connectionType[63] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		connectionType[0] = '\0';
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
+ * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
+ * One of the values can be null 
+ * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only 
+ * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
+LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
+                             const char * servicetype,
+                             unsigned int * bitrateDown,
+                             unsigned int * bitrateUp)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+	char * down;
+	char * up;
+	char * p;
+
+	if(!bitrateDown && !bitrateUp)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	/* shouldn't we use GetCommonLinkProperties ? */
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetCommonLinkProperties", 0, &bufsize))) {
+	                              /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	/*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
+	/*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
+	down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
+	up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
+	/*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
+	/*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
+	if(down && up)
+		ret = UPNPCOMMAND_SUCCESS;
+
+	if(bitrateDown) {
+		if(down)
+			sscanf(down,"%u",bitrateDown);
+		else
+			*bitrateDown = 0;
+	}
+
+	if(bitrateUp) {
+		if(up)
+			sscanf(up,"%u",bitrateUp);
+		else
+			*bitrateUp = 0;
+	}
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available
+ * 
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ *
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ */
+LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+                          const char * servicetype,
+                          char * extIpAdd)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!extIpAdd || !controlURL || !servicetype)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetExternalIPAddress", 0, &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	/*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
+	p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
+	if(p) {
+		strncpy(extIpAdd, p, 16 );
+		extIpAdd[15] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		extIpAdd[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+                    const char * extPort,
+					const char * inPort,
+					const char * inClient,
+					const char * desc,
+					const char * proto,
+                    const char * remoteHost,
+                    const char * leaseDuration)
+{
+	struct UPNParg * AddPortMappingArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!inPort || !inClient || !proto || !extPort)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
+	AddPortMappingArgs[0].elt = "NewRemoteHost";
+	AddPortMappingArgs[0].val = remoteHost;
+	AddPortMappingArgs[1].elt = "NewExternalPort";
+	AddPortMappingArgs[1].val = extPort;
+	AddPortMappingArgs[2].elt = "NewProtocol";
+	AddPortMappingArgs[2].val = proto;
+	AddPortMappingArgs[3].elt = "NewInternalPort";
+	AddPortMappingArgs[3].val = inPort;
+	AddPortMappingArgs[4].elt = "NewInternalClient";
+	AddPortMappingArgs[4].val = inClient;
+	AddPortMappingArgs[5].elt = "NewEnabled";
+	AddPortMappingArgs[5].val = "1";
+	AddPortMappingArgs[6].elt = "NewPortMappingDescription";
+	AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
+	AddPortMappingArgs[7].elt = "NewLeaseDuration";
+	AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "AddPortMapping", AddPortMappingArgs,
+	                                &bufsize))) {
+		free(AddPortMappingArgs);
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	/*DisplayNameValueList(buffer, bufsize);*/
+	/*buffer[bufsize] = '\0';*/
+	/*puts(buffer);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal) {
+		/*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	} else {
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(AddPortMappingArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+                       const char * extPort, const char * proto,
+                       const char * remoteHost)
+{
+	/*struct NameValueParserData pdata;*/
+	struct UPNParg * DeletePortMappingArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!extPort || !proto)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
+	DeletePortMappingArgs[0].elt = "NewRemoteHost";
+	DeletePortMappingArgs[0].val = remoteHost;
+	DeletePortMappingArgs[1].elt = "NewExternalPort";
+	DeletePortMappingArgs[1].val = extPort;
+	DeletePortMappingArgs[2].elt = "NewProtocol";
+	DeletePortMappingArgs[2].val = proto;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                               "DeletePortMapping",
+	                               DeletePortMappingArgs, &bufsize))) {
+		free(DeletePortMappingArgs);
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	} else {
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(DeletePortMappingArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+                                const char * servicetype,
+							 const char * index,
+							 char * extPort,
+							 char * intClient,
+							 char * intPort,
+							 char * protocol,
+							 char * desc,
+							 char * enabled,
+							 char * rHost,
+							 char * duration)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetPortMappingArgs;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int r = UPNPCOMMAND_UNKNOWN_ERROR;
+	if(!index)
+		return UPNPCOMMAND_INVALID_ARGS;
+	intClient[0] = '\0';
+	intPort[0] = '\0';
+	GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
+	GetPortMappingArgs[0].elt = "NewPortMappingIndex";
+	GetPortMappingArgs[0].val = index;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                               "GetGenericPortMappingEntry",
+	                               GetPortMappingArgs, &bufsize))) {
+		free(GetPortMappingArgs);
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+	p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
+	if(p && rHost)
+	{
+		strncpy(rHost, p, 64);
+		rHost[63] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewExternalPort");
+	if(p && extPort)
+	{
+		strncpy(extPort, p, 6);
+		extPort[5] = '\0';
+		r = UPNPCOMMAND_SUCCESS;
+	}
+	p = GetValueFromNameValueList(&pdata, "NewProtocol");
+	if(p && protocol)
+	{
+		strncpy(protocol, p, 4);
+		protocol[3] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+	if(p && intClient)
+	{
+		strncpy(intClient, p, 16);
+		intClient[15] = '\0';
+		r = 0;
+	}
+	p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+	if(p && intPort)
+	{
+		strncpy(intPort, p, 6);
+		intPort[5] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewEnabled");
+	if(p && enabled)
+	{
+		strncpy(enabled, p, 4);
+		enabled[3] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+	if(p && desc)
+	{
+		strncpy(desc, p, 80);
+		desc[79] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+	if(p && duration)
+	{
+		strncpy(duration, p, 16);
+		duration[15] = '\0';
+	}
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		r = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &r);
+	}
+	ClearNameValueList(&pdata);
+	free(GetPortMappingArgs);
+	return r;
+}
+
+LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
+                                   const char * servicetype,
+                                   unsigned int * numEntries)
+{
+ 	struct NameValueParserData pdata;
+ 	char * buffer;
+ 	int bufsize;
+ 	char* p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+ 	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetPortMappingNumberOfEntries", 0,
+	                                &bufsize))) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+#ifdef DEBUG
+	DisplayNameValueList(buffer, bufsize);
+#endif
+ 	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+ 	p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
+ 	if(numEntries && p) {
+		*numEntries = 0;
+ 		sscanf(p, "%u", numEntries);
+		ret = UPNPCOMMAND_SUCCESS;
+ 	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+ 	ClearNameValueList(&pdata);
+	return ret;
+}
+
+/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
+ * the result is returned in the intClient and intPort strings
+ * please provide 16 and 6 bytes of data */
+LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+                                 const char * servicetype,
+                                 const char * extPort,
+							     const char * proto,
+                                 char * intClient,
+                                 char * intPort,
+                                 char * desc,
+                                 char * enabled,
+                                 char * leaseDuration)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetPortMappingArgs;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!intPort || !intClient || !extPort || !proto)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
+	GetPortMappingArgs[0].elt = "NewRemoteHost";
+	/* TODO : add remote host ? */
+	GetPortMappingArgs[1].elt = "NewExternalPort";
+	GetPortMappingArgs[1].val = extPort;
+	GetPortMappingArgs[2].elt = "NewProtocol";
+	GetPortMappingArgs[2].val = proto;
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetSpecificPortMappingEntry",
+	                                GetPortMappingArgs, &bufsize))) {
+		free(GetPortMappingArgs);
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+	p = GetValueFromNameValueList(&pdata, "NewInternalClient");
+	if(p) {
+		strncpy(intClient, p, 16);
+		intClient[15] = '\0';
+		ret = UPNPCOMMAND_SUCCESS;
+	} else
+		intClient[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "NewInternalPort");
+	if(p) {
+		strncpy(intPort, p, 6);
+		intPort[5] = '\0';
+	} else
+		intPort[0] = '\0';
+
+	p = GetValueFromNameValueList(&pdata, "NewEnabled");
+	if(p && enabled) {
+		strncpy(enabled, p, 4);
+		enabled[3] = '\0';
+	}
+
+	p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
+	if(p && desc) {
+		strncpy(desc, p, 80);
+		desc[79] = '\0';
+	}
+
+	p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
+	if(p && leaseDuration)
+	{
+		strncpy(leaseDuration, p, 16);
+		leaseDuration[15] = '\0';
+	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	free(GetPortMappingArgs);
+	return ret;
+}
+
+/* UPNP_GetListOfPortMappings()
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ *                              consistent.
+ */
+LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+                           const char * servicetype,
+                           const char * startPort,
+                           const char * endPort,
+                           const char * protocol,
+                           const char * numberOfPorts,
+                           struct PortMappingParserData * data)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetListOfPortMappingsArgs;
+	const char * p;
+	char * buffer;
+	int bufsize;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!startPort || !endPort || !protocol)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
+	GetListOfPortMappingsArgs[0].elt = "NewStartPort";
+	GetListOfPortMappingsArgs[0].val = startPort;
+	GetListOfPortMappingsArgs[1].elt = "NewEndPort";
+	GetListOfPortMappingsArgs[1].val = endPort;
+	GetListOfPortMappingsArgs[2].elt = "NewProtocol";
+	GetListOfPortMappingsArgs[2].val = protocol;
+	GetListOfPortMappingsArgs[3].elt = "NewManage";
+	GetListOfPortMappingsArgs[3].val = "1";
+	GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
+	GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
+
+	if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                                "GetListOfPortMappings",
+	                                GetListOfPortMappingsArgs, &bufsize))) {
+		free(GetListOfPortMappingsArgs);
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	free(GetListOfPortMappingsArgs);
+
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+	/*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
+	/*if(p) {
+		printf("NewPortListing : %s\n", p);
+	}*/
+	/*printf("NewPortListing(%d chars) : %s\n",
+	       pdata.portListingLength, pdata.portListing);*/
+	if(pdata.portListing)
+	{
+		/*struct PortMapping * pm;
+		int i = 0;*/
+		ParsePortListing(pdata.portListing, pdata.portListingLength,
+		                 data);
+		ret = UPNPCOMMAND_SUCCESS;
+		/*
+		for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
+		{
+			printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
+			       i, pm->protocol, pm->externalPort, pm->internalClient,
+			       pm->internalPort,
+			       pm->description, pm->remoteHost);
+			i++;
+		}
+		*/
+		/*FreePortListing(&data);*/
+	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p) {
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+
+	//printf("%.*s", bufsize, buffer);
+
+	return ret;
+}
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */ 
+LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+				const char * servicetype,
+				int * firewallEnabled, 
+				int * inboundPinholeAllowed)
+{
+	struct NameValueParserData pdata;
+	char * buffer;
+	int bufsize;
+	char * fe, *ipa, *p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!firewallEnabled && !inboundPinholeAllowed)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "GetFirewallStatus", 0, &bufsize);
+	if(!buffer) {
+		return UPNPCOMMAND_HTTP_ERROR;
+	}
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
+	ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
+	if(ipa && fe)
+		ret = UPNPCOMMAND_SUCCESS;
+	if(fe)
+		*firewallEnabled = my_atoui(fe);
+	/*else
+		*firewallEnabled = 0;*/
+	if(ipa)
+		*inboundPinholeAllowed = my_atoui(ipa);
+	/*else
+		*inboundPinholeAllowed = 0;*/
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p)
+	{
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+	ClearNameValueList(&pdata);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+                    const char * remoteHost,
+                    const char * remotePort,
+                    const char * intClient,
+                    const char * intPort,
+                    const char * proto,
+                    int * opTimeout)
+{
+	struct UPNParg * GetOutboundPinholeTimeoutArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	char * p;
+	int ret;
+
+	if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
+	GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
+	GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
+	GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
+	GetOutboundPinholeTimeoutArgs[1].val = remotePort;
+	GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
+	GetOutboundPinholeTimeoutArgs[2].val = proto;
+	GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
+	GetOutboundPinholeTimeoutArgs[3].val = intPort;
+	GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
+	GetOutboundPinholeTimeoutArgs[4].val = intClient;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal)
+	{
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	}
+	else
+	{
+		ret = UPNPCOMMAND_SUCCESS;
+		p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
+		if(p)
+			*opTimeout = my_atoui(p);
+	}
+	ClearNameValueList(&pdata);
+	free(GetOutboundPinholeTimeoutArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+                    const char * remoteHost,
+                    const char * remotePort,
+                    const char * intClient,
+                    const char * intPort,
+                    const char * proto,
+                    const char * leaseTime,
+                    char * uniqueID)
+{
+	struct UPNParg * AddPinholeArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	char * p;
+	int ret;
+
+	if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
+	// RemoteHost can be wilcarded
+	if(strncmp(remoteHost, "empty", 5)==0)
+	{
+		AddPinholeArgs[0].elt = "RemoteHost";
+		AddPinholeArgs[0].val = "";
+	}
+	else
+	{
+		AddPinholeArgs[0].elt = "RemoteHost";
+		AddPinholeArgs[0].val = remoteHost;
+	}
+	AddPinholeArgs[1].elt = "RemotePort";
+	AddPinholeArgs[1].val = remotePort;
+	AddPinholeArgs[2].elt = "Protocol";
+	AddPinholeArgs[2].val = proto;
+	AddPinholeArgs[3].elt = "InternalPort";
+	AddPinholeArgs[3].val = intPort;
+	if(strncmp(intClient, "empty", 5)==0)
+	{
+		AddPinholeArgs[4].elt = "InternalClient";
+		AddPinholeArgs[4].val = "";
+	}
+	else
+	{
+		AddPinholeArgs[4].elt = "InternalClient";
+		AddPinholeArgs[4].val = intClient;
+	}
+	AddPinholeArgs[5].elt = "LeaseTime";
+	AddPinholeArgs[5].val = leaseTime;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "AddPinhole", AddPinholeArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	p = GetValueFromNameValueList(&pdata, "UniqueID");
+	if(p)
+	{
+		strncpy(uniqueID, p, 8);
+		uniqueID[7] = '\0';
+	}
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal)
+	{
+		//printf("AddPortMapping errorCode = '%s'\n", resVal); 
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	}
+	else
+	{
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(AddPinholeArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+                    const char * uniqueID,
+                    const char * leaseTime)
+{
+	struct UPNParg * UpdatePinholeArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!uniqueID || !leaseTime)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
+	UpdatePinholeArgs[0].elt = "UniqueID";
+	UpdatePinholeArgs[0].val = uniqueID;
+	UpdatePinholeArgs[1].elt = "NewLeaseTime";
+	UpdatePinholeArgs[1].val = leaseTime;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "UpdatePinhole", UpdatePinholeArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal)
+	{
+		/*printf("AddPortMapping errorCode = '%s'\n", resVal); */
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	}
+	else
+	{
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(UpdatePinholeArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
+{
+	/*struct NameValueParserData pdata;*/
+	struct UPNParg * DeletePinholeArgs;
+	char * buffer;
+	int bufsize;
+	struct NameValueParserData pdata;
+	const char * resVal;
+	int ret;
+
+	if(!uniqueID)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
+	DeletePinholeArgs[0].elt = "UniqueID";
+	DeletePinholeArgs[0].val = uniqueID;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "DeletePinhole", DeletePinholeArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	/*DisplayNameValueList(buffer, bufsize);*/
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+	resVal = GetValueFromNameValueList(&pdata, "errorCode");
+	if(resVal)
+	{
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(resVal, "%d", &ret);
+	}
+	else
+	{
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	ClearNameValueList(&pdata);
+	free(DeletePinholeArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+                                 const char * uniqueID, int * isWorking)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * CheckPinholeWorkingArgs;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!uniqueID)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
+	CheckPinholeWorkingArgs[0].elt = "UniqueID";
+	CheckPinholeWorkingArgs[0].val = uniqueID;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+	p = GetValueFromNameValueList(&pdata, "IsWorking");
+	if(p)
+	{
+		*isWorking=my_atoui(p);
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+	else
+		*isWorking = 0;
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p)
+	{
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	free(CheckPinholeWorkingArgs);
+	return ret;
+}
+
+LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+                                 const char * uniqueID, int * packets)
+{
+	struct NameValueParserData pdata;
+	struct UPNParg * GetPinholePacketsArgs;
+	char * buffer;
+	int bufsize;
+	char * p;
+	int ret = UPNPCOMMAND_UNKNOWN_ERROR;
+
+	if(!uniqueID)
+		return UPNPCOMMAND_INVALID_ARGS;
+
+	GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
+	GetPinholePacketsArgs[0].elt = "UniqueID";
+	GetPinholePacketsArgs[0].val = uniqueID;
+	buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+	                           "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+	if(!buffer)
+		return UPNPCOMMAND_HTTP_ERROR;
+	ParseNameValue(buffer, bufsize, &pdata);
+	free(buffer); buffer = NULL;
+
+	p = GetValueFromNameValueList(&pdata, "PinholePackets");
+	if(p)
+	{
+		*packets=my_atoui(p);
+		ret = UPNPCOMMAND_SUCCESS;
+	}
+
+	p = GetValueFromNameValueList(&pdata, "errorCode");
+	if(p)
+	{
+		ret = UPNPCOMMAND_UNKNOWN_ERROR;
+		sscanf(p, "%d", &ret);
+	}
+
+	ClearNameValueList(&pdata);
+	free(GetPinholePacketsArgs);
+	return ret;
+}
+
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h
new file mode 100644
index 0000000..66d95e0
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpcommands.h
@@ -0,0 +1,271 @@
+/* $Id: upnpcommands.h,v 1.23 2011/04/11 09:14:00 nanard Exp $ */
+/* Miniupnp project : http://miniupnp.free.fr/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2011 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided within this distribution */
+#ifndef __UPNPCOMMANDS_H__
+#define __UPNPCOMMANDS_H__
+
+#include "upnpreplyparse.h"
+#include "portlistingparse.h"
+#include "declspec.h"
+#include "miniupnpctypes.h"
+
+/* MiniUPnPc return codes : */
+#define UPNPCOMMAND_SUCCESS (0)
+#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
+#define UPNPCOMMAND_INVALID_ARGS (-2)
+#define UPNPCOMMAND_HTTP_ERROR (-3)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesSent(const char * controlURL,
+					const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalBytesReceived(const char * controlURL,
+						const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsSent(const char * controlURL,
+					const char * servicetype);
+
+LIBSPEC UNSIGNED_INTEGER
+UPNP_GetTotalPacketsReceived(const char * controlURL,
+					const char * servicetype);
+
+/* UPNP_GetStatusInfo()
+ * status and lastconnerror are 64 byte buffers
+ * Return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetStatusInfo(const char * controlURL,
+			       const char * servicetype,
+				   char * status,
+				   unsigned int * uptime,
+                   char * lastconnerror);
+
+/* UPNP_GetConnectionTypeInfo()
+ * argument connectionType is a 64 character buffer
+ * Return Values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error code */
+LIBSPEC int
+UPNP_GetConnectionTypeInfo(const char * controlURL,
+                           const char * servicetype,
+						   char * connectionType);
+
+/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
+ * if the third arg is not null the value is copied to it.
+ * at least 16 bytes must be available 
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR Either an UPnP error code or an unknown error.
+ * 
+ * possible UPnP Errors :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control. */
+LIBSPEC int
+UPNP_GetExternalIPAddress(const char * controlURL,
+                          const char * servicetype,
+                          char * extIpAdd);
+
+/* UPNP_GetLinkLayerMaxBitRates()
+ * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
+ *
+ * return values :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
+							const char* servicetype,
+							unsigned int * bitrateDown,
+							unsigned int * bitrateUp);
+
+/* UPNP_AddPortMapping()
+ * if desc is NULL, it will be defaulted to "libminiupnpc"
+ * remoteHost is usually NULL because IGD don't support it.
+ *
+ * Return values :
+ * 0 : SUCCESS
+ * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
+ * 
+ * List of possible UPnP errors for AddPortMapping :
+ * errorCode errorDescription (short) - Description (long)
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 501 Action Failed - See UPnP Device Architecture section on Control.
+ * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
+ *                                   wild-carded
+ * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
+ * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
+ *                     with a mapping assigned previously to another client
+ * 724 SamePortValuesRequired - Internal and External port values
+ *                              must be the same 
+ * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
+ *                  permanent lease times on port mappings
+ * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
+ *                             and cannot be a specific IP address or DNS name
+ * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
+ *                                        cannot be a specific port value */
+LIBSPEC int
+UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
+                    const char * extPort,
+				    const char * inPort,
+					const char * inClient,
+					const char * desc,
+                    const char * proto,
+                    const char * remoteHost,
+                    const char * leaseDuration);
+
+/* UPNP_DeletePortMapping()
+ * Use same argument values as what was used for AddPortMapping().
+ * remoteHost is usually NULL because IGD don't support it.
+ * Return Values :
+ * 0 : SUCCESS
+ * NON ZERO : error. Either an UPnP error code or an undefined error.
+ *
+ * List of possible UPnP errors for DeletePortMapping :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 714 NoSuchEntryInArray - The specified value does not exist in the array */
+LIBSPEC int
+UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
+                       const char * extPort, const char * proto,
+                       const char * remoteHost);
+
+/* UPNP_GetPortMappingNumberOfEntries()
+ * not supported by all routers */
+LIBSPEC int
+UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
+                                   const char* servicetype,
+                                   unsigned int * num);
+
+/* UPNP_GetSpecificPortMappingEntry()
+ *    retrieves an existing port mapping
+ * params :
+ *  in   extPort
+ *  in   proto
+ *  out  intClient (16 bytes)
+ *  out  intPort (6 bytes)
+ *  out  desc (80 bytes)
+ *  out  enabled (4 bytes)
+ *  out  leaseDuration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code. */
+LIBSPEC int
+UPNP_GetSpecificPortMappingEntry(const char * controlURL,
+                                 const char * servicetype,
+                                 const char * extPort,
+                                 const char * proto,
+                                 char * intClient,
+                                 char * intPort,
+                                 char * desc,
+                                 char * enabled,
+                                 char * leaseDuration);
+
+/* UPNP_GetGenericPortMappingEntry()
+ * params :
+ *  in   index
+ *  out  extPort (6 bytes)
+ *  out  intClient (16 bytes)
+ *  out  intPort (6 bytes)
+ *  out  protocol (4 bytes)
+ *  out  desc (80 bytes)
+ *  out  enabled (4 bytes)
+ *  out  rHost (64 bytes)
+ *  out  duration (16 bytes)
+ *
+ * return value :
+ * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
+ * or a UPnP Error Code.
+ *
+ * Possible UPNP Error codes :
+ * 402 Invalid Args - See UPnP Device Architecture section on Control.
+ * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
+ */
+LIBSPEC int
+UPNP_GetGenericPortMappingEntry(const char * controlURL,
+                                const char * servicetype,
+								const char * index,
+								char * extPort,
+								char * intClient,
+								char * intPort,
+								char * protocol,
+								char * desc,
+								char * enabled,
+								char * rHost,
+								char * duration);
+
+/* UPNP_GetListOfPortMappings()      Available in IGD v2
+ *
+ *
+ * Possible UPNP Error codes :
+ * 606 Action not Authorized
+ * 730 PortMappingNotFound - no port mapping is found in the specified range.
+ * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
+ *                              consistent.
+ */
+LIBSPEC int
+UPNP_GetListOfPortMappings(const char * controlURL,
+                           const char * servicetype,
+                           const char * startPort,
+                           const char * endPort,
+                           const char * protocol,
+                           const char * numberOfPorts,
+                           struct PortMappingParserData * data);
+
+/* IGD:2, functions for service WANIPv6FirewallControl:1 */ 
+LIBSPEC int
+UPNP_GetFirewallStatus(const char * controlURL,
+				const char * servicetype,
+				int * firewallEnabled, 
+				int * inboundPinholeAllowed);
+
+LIBSPEC int
+UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
+                    const char * remoteHost,
+                    const char * remotePort,
+                    const char * intClient,
+                    const char * intPort,
+                    const char * proto,
+                    int * opTimeout);
+
+LIBSPEC int
+UPNP_AddPinhole(const char * controlURL, const char * servicetype,
+                    const char * remoteHost,
+                    const char * remotePort,
+                    const char * intClient,
+                    const char * intPort,
+                    const char * proto,
+                    const char * leaseTime,
+                    char * uniqueID);
+
+LIBSPEC int
+UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
+                    const char * uniqueID,
+                    const char * leaseTime);
+
+LIBSPEC int
+UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
+
+LIBSPEC int
+UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
+                                 const char * uniqueID, int * isWorking);
+
+LIBSPEC int
+UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
+                                 const char * uniqueID, int * packets);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c
new file mode 100644
index 0000000..a48ae10
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.c
@@ -0,0 +1,103 @@
+/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */
+/* Project : miniupnp
+ * Author : Thomas BERNARD
+ * copyright (c) 2007 Thomas Bernard
+ * All Right reserved.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#include <string.h>
+#include "upnperrors.h"
+#include "upnpcommands.h"
+#include "miniupnpc.h"
+
+const char * strupnperror(int err)
+{
+	const char * s = NULL;
+	switch(err) {
+	case UPNPCOMMAND_SUCCESS:
+		s = "Success";
+		break;
+	case UPNPCOMMAND_UNKNOWN_ERROR:
+		s = "Miniupnpc Unknown Error";
+		break;
+	case UPNPCOMMAND_INVALID_ARGS:
+		s = "Miniupnpc Invalid Arguments";
+		break;
+	case UPNPDISCOVER_SOCKET_ERROR:
+		s = "Miniupnpc Socket error";
+		break;
+	case UPNPDISCOVER_MEMORY_ERROR:
+		s = "Miniupnpc Memory allocation error";
+		break;
+	case 401:
+		s = "Invalid Action";
+		break;
+	case 402:
+		s = "Invalid Args";
+		break;
+	case 501:
+		s = "Action Failed";
+		break;
+	case 606:
+		s = "Action not authorized";
+		break;
+	case 701:
+		s = "PinholeSpaceExhausted";
+		break;
+	case 702:
+		s = "FirewallDisabled";
+		break;
+	case 703:
+		s = "InboundPinholeNotAllowed";
+		break;
+	case 704:
+		s = "NoSuchEntry";
+		break;
+	case 705:
+		s = "ProtocolNotSupported";
+		break;
+	case 706:
+		s = "InternalPortWildcardingNotAllowed";
+		break;
+	case 707:
+		s = "ProtocolWildcardingNotAllowed";
+		break;
+	case 708:
+		s = "WildcardNotPermittedInSrcIP";
+		break;
+	case 709:
+		s = "NoPacketSent";
+		break;
+	case 713:
+		s = "SpecifiedArrayIndexInvalid";
+		break;
+	case 714:
+		s = "NoSuchEntryInArray";
+		break;
+	case 715:
+		s = "WildCardNotPermittedInSrcIP";
+		break;
+	case 716:
+		s = "WildCardNotPermittedInExtPort";
+		break;
+	case 718:
+		s = "ConflictInMappingEntry";
+		break;
+	case 724:
+		s = "SamePortValuesRequired";
+		break;
+	case 725:
+		s = "OnlyPermanentLeasesSupported";
+		break;
+	case 726:
+		s = "RemoteHostOnlySupportsWildcard";
+		break;
+	case 727:
+		s = "ExternalPortOnlySupportsWildcard";
+		break;
+	default:
+		s = NULL;
+	}
+	return s;
+}
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h
new file mode 100644
index 0000000..2c544c9
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnperrors.h
@@ -0,0 +1,26 @@
+/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
+/* (c) 2007 Thomas Bernard
+ * All rights reserved.
+ * MiniUPnP Project.
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subjet to the conditions detailed in the
+ * provided LICENCE file. */
+#ifndef __UPNPERRORS_H__
+#define __UPNPERRORS_H__
+
+#include "declspec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* strupnperror()
+ * Return a string description of the UPnP error code 
+ * or NULL for undefinded errors */
+LIBSPEC const char * strupnperror(int err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c
new file mode 100644
index 0000000..482030b
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.c
@@ -0,0 +1,152 @@
+/* $Id: upnpreplyparse.c,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2011 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "upnpreplyparse.h"
+#include "minixml.h"
+
+static void
+NameValueParserStartElt(void * d, const char * name, int l)
+{
+    struct NameValueParserData * data = (struct NameValueParserData *)d;
+    if(l>63)
+        l = 63;
+    memcpy(data->curelt, name, l);
+    data->curelt[l] = '\0';
+}
+
+static void
+NameValueParserGetData(void * d, const char * datas, int l)
+{
+    struct NameValueParserData * data = (struct NameValueParserData *)d;
+    struct NameValue * nv;
+	if(strcmp(data->curelt, "NewPortListing") == 0)
+	{
+		/* specific case for NewPortListing which is a XML Document */
+		data->portListing = malloc(l + 1);
+		if(!data->portListing)
+		{
+			/* malloc error */
+			return;
+		}
+		memcpy(data->portListing, datas, l);
+		data->portListing[l] = '\0';
+		data->portListingLength = l;
+	}
+	else
+	{
+		/* standard case. Limited to 63 chars strings */
+	    nv = malloc(sizeof(struct NameValue));
+	    if(l>63)
+	        l = 63;
+	    strncpy(nv->name, data->curelt, 64);
+		nv->name[63] = '\0';
+	    memcpy(nv->value, datas, l);
+	    nv->value[l] = '\0';
+	    LIST_INSERT_HEAD( &(data->head), nv, entries);
+	}
+}
+
+void
+ParseNameValue(const char * buffer, int bufsize,
+               struct NameValueParserData * data)
+{
+    struct xmlparser parser;
+    LIST_INIT(&(data->head));
+	data->portListing = NULL;
+	data->portListingLength = 0;
+    /* init xmlparser object */
+    parser.xmlstart = buffer;
+    parser.xmlsize = bufsize;
+    parser.data = data;
+    parser.starteltfunc = NameValueParserStartElt;
+    parser.endeltfunc = 0;
+    parser.datafunc = NameValueParserGetData;
+	parser.attfunc = 0;
+    parsexml(&parser);
+}
+
+void
+ClearNameValueList(struct NameValueParserData * pdata)
+{
+    struct NameValue * nv;
+	if(pdata->portListing)
+	{
+		free(pdata->portListing);
+		pdata->portListing = NULL;
+		pdata->portListingLength = 0;
+	}
+    while((nv = pdata->head.lh_first) != NULL)
+    {
+        LIST_REMOVE(nv, entries);
+        free(nv);
+    }
+}
+
+char * 
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+                          const char * Name)
+{
+    struct NameValue * nv;
+    char * p = NULL;
+    for(nv = pdata->head.lh_first;
+        (nv != NULL) && (p == NULL);
+        nv = nv->entries.le_next)
+    {
+        if(strcmp(nv->name, Name) == 0)
+            p = nv->value;
+    }
+    return p;
+}
+
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+                                  const char * Name)
+{
+	struct NameValue * nv;
+	char * p = NULL;
+	char * pname;
+	for(nv = pdata->head.lh_first;
+	    (nv != NULL) && (p == NULL);
+		nv = nv->entries.le_next)
+	{
+		pname = strrchr(nv->name, ':');
+		if(pname)
+			pname++;
+		else
+			pname = nv->name;
+		if(strcmp(pname, Name)==0)
+			p = nv->value;
+	}
+	return p;
+}
+#endif
+
+/* debug all-in-one function 
+ * do parsing then display to stdout */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize)
+{
+    struct NameValueParserData pdata;
+    struct NameValue * nv;
+    ParseNameValue(buffer, bufsize, &pdata);
+    for(nv = pdata.head.lh_first;
+        nv != NULL;
+        nv = nv->entries.le_next)
+    {
+        printf("%s = %s\n", nv->name, nv->value);
+    }
+    ClearNameValueList(&pdata);
+}
+#endif
+
diff --git a/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h
new file mode 100644
index 0000000..267ea87
--- /dev/null
+++ b/3rdParty/LibMiniUPnPc/src/miniupnpc/upnpreplyparse.h
@@ -0,0 +1,64 @@
+/* $Id: upnpreplyparse.h,v 1.11 2011/02/07 16:17:06 nanard Exp $ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2011 Thomas Bernard 
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef __UPNPREPLYPARSE_H__
+#define __UPNPREPLYPARSE_H__
+
+#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__) 
+#include "bsdqueue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct NameValue {
+    LIST_ENTRY(NameValue) entries;
+    char name[64];
+    char value[64];
+};
+
+struct NameValueParserData {
+    LIST_HEAD(listhead, NameValue) head;
+    char curelt[64];
+	char * portListing;
+	int portListingLength;
+};
+
+/* ParseNameValue() */
+void
+ParseNameValue(const char * buffer, int bufsize,
+               struct NameValueParserData * data);
+
+/* ClearNameValueList() */
+void
+ClearNameValueList(struct NameValueParserData * pdata);
+
+/* GetValueFromNameValueList() */
+char *
+GetValueFromNameValueList(struct NameValueParserData * pdata,
+                          const char * Name);
+
+/* GetValueFromNameValueListIgnoreNS() */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+                                  const char * Name);
+
+/* DisplayNameValueList() */
+#ifdef DEBUG
+void
+DisplayNameValueList(char * buffer, int bufsize);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/3rdParty/LibNATPMP/SConscript b/3rdParty/LibNATPMP/SConscript
new file mode 100644
index 0000000..60e59a0
--- /dev/null
+++ b/3rdParty/LibNATPMP/SConscript
@@ -0,0 +1,50 @@
+Import(["env", "conf_env"])
+
+if env.get("LIBNATPMP_BUNDLED", False) :
+
+################################################################################
+# Module flags
+################################################################################
+
+	if env["SCONS_STAGE"] == "flags" :
+		env["LIBNATPMP_FLAGS"] = {
+				"CPPPATH": [Dir("src/libnatpmp")],
+				"LIBPATH": [Dir(".")],
+				"LIBS": ["Swiften_NATPMP"],
+			}
+		#if env["PLATFORM"] == "win32" :
+		#	env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32")]
+		#	if env["MSVC_VERSION"][:3] == "9.0" :
+		#		env["LIBIDN_FLAGS"]["CPPPATH"] += [Dir("stubs/win32/VC2008")]
+
+################################################################################
+# Build
+################################################################################
+
+	if env["SCONS_STAGE"] == "build" :
+		myenv = env.Clone()
+		myenv.Append(CPPPATH = ["src"])
+		# Remove warn flags
+		myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]])
+
+		myenv.Append(CCFLAGS = ["-DNDEBUG", "-DSTATICLIB"])
+		
+		#if myenv["PLATFORM"] != "win32":
+		#	myenv.Append(CCFLAGS = ["-DMINIUPNPC_SET_SOCKET_TIMEOUT"])
+		
+		if myenv["PLATFORM"] == "darwin":
+			myenv.Append(CCFLAGS = ["-DMACOSX", "-D_DARWIN_C_SOURCE"])
+		
+		if myenv["PLATFORM"] == "win32":
+			myenv.Append(CCFLAGS = ["-DWIN32"])
+
+		src_files = [
+				"src/libnatpmp/getgateway.c",
+				"src/libnatpmp/natpmp.c",
+				"src/libnatpmp/natpmpc.c",
+			]	
+		
+		if myenv["PLATFORM"] == "win32":
+			src_files += ["src/libnatpmp/wingettimeofday.c"]
+		
+		myenv.StaticLibrary("Swiften_NATPMP", src_files)
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/LICENSE b/3rdParty/LibNATPMP/src/libnatpmp/LICENSE
new file mode 100644
index 0000000..14db2fe
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2007-2009, Thomas BERNARD 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name of the author may not be used to endorse or promote products
+	  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/Makefile b/3rdParty/LibNATPMP/src/libnatpmp/Makefile
new file mode 100644
index 0000000..b523e53
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/Makefile
@@ -0,0 +1,97 @@
+# $Id: Makefile,v 1.16 2011/01/03 17:31:03 nanard Exp $
+# This Makefile is designed for use with GNU make
+# libnatpmp
+# (c) 2007-2011 Thomas Bernard
+# http://miniupnp.free.fr/libnatpmp.html
+
+OS = $(shell uname -s)
+CC = gcc
+INSTALL = install
+
+# APIVERSION is used in soname
+APIVERSION = 1
+LDFLAGS = --no-undefined
+CFLAGS = -O -fPIC -Wall -DENABLE_STRNATPMPERR
+
+LIBOBJS = natpmp.o getgateway.o
+
+OBJS = $(LIBOBJS) testgetgateway.o natpmpc.o
+
+STATICLIB = libnatpmp.a
+ifeq ($(OS), Darwin)
+  SHAREDLIB = libnatpmp.dynlib
+  SONAME = $(basename $(SHAREDLIB)).$(APIVERSION).dylib
+  CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS)
+else
+  SHAREDLIB = libnatpmp.so
+  SONAME = $(SHAREDLIB).$(APIVERSION)
+endif
+
+HEADERS = natpmp.h
+
+EXECUTABLES = testgetgateway natpmpc-shared natpmpc-static
+
+INSTALLPREFIX ?= $(PREFIX)/usr
+INSTALLDIRINC = $(INSTALLPREFIX)/include
+INSTALLDIRLIB = $(INSTALLPREFIX)/lib
+INSTALLDIRBIN = $(INSTALLPREFIX)/bin
+
+.PHONY:	all clean depend install cleaninstall installpythonmodule
+
+all: $(STATICLIB) $(SHAREDLIB) $(EXECUTABLES)
+
+pythonmodule: $(STATICLIB) libnatpmpmodule.c setup.py
+	python setup.py build
+	touch $@
+
+installpythonmodule: pythonmodule
+	python setup.py install
+
+clean:
+	$(RM) $(OBJS) $(EXECUTABLES) $(STATICLIB) $(SHAREDLIB)
+	$(RM) pythonmodule
+	$(RM) -r build/ dist/
+
+depend:
+	makedepend -f$(MAKEFILE_LIST) -Y $(OBJS:.o=.c) 2>/dev/null
+
+install:	$(HEADERS) $(STATICLIB) $(SHAREDLIB) natpmpc-shared
+	$(INSTALL) -d $(INSTALLDIRINC)
+	$(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC)
+	$(INSTALL) -d $(INSTALLDIRLIB)
+	$(INSTALL) -m 644 $(STATICLIB) $(INSTALLDIRLIB)
+	$(INSTALL) -m 644 $(SHAREDLIB) $(INSTALLDIRLIB)/$(SONAME)
+	$(INSTALL) -d $(INSTALLDIRBIN)
+	$(INSTALL) -m 755 natpmpc-shared $(INSTALLDIRBIN)/natpmpc
+	ln -s -f $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIB)
+
+cleaninstall:
+	$(RM) $(addprefix $(INSTALLDIRINC), $(HEADERS))
+	$(RM) $(INSTALLDIRLIB)/$(SONAME)
+	$(RM) $(INSTALLDIRLIB)/$(SHAREDLIB)
+	$(RM) $(INSTALLDIRLIB)/$(STATICLIB)
+
+testgetgateway:	testgetgateway.o getgateway.o
+
+natpmpc-static:	natpmpc.o $(STATICLIB)
+	$(CC) -o $@ $^
+
+natpmpc-shared:	natpmpc.o $(SHAREDLIB)
+	$(CC) -o $@ $^
+
+$(STATICLIB):	$(LIBOBJS)
+	$(AR) crs $@ $?
+
+$(SHAREDLIB):	$(LIBOBJS)
+ifeq ($(OS), Darwin)
+	$(CC) -dynamiclib -Wl,-install_name,$(SONAME) -o $@ $^
+else
+	$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^
+endif
+
+# DO NOT DELETE
+
+natpmp.o: natpmp.h getgateway.h declspec.h
+getgateway.o: getgateway.h declspec.h
+testgetgateway.o: getgateway.h declspec.h
+natpmpc.o: natpmp.h
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/declspec.h b/3rdParty/LibNATPMP/src/libnatpmp/declspec.h
new file mode 100644
index 0000000..ea479d1
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/declspec.h
@@ -0,0 +1,15 @@
+#ifndef __DECLSPEC_H__
+#define __DECLSPEC_H__
+
+#if defined(WIN32) && !defined(STATICLIB)
+	#ifdef NATPMP_EXPORTS
+		#define LIBSPEC __declspec(dllexport)
+	#else
+		#define LIBSPEC __declspec(dllimport)
+	#endif
+#else
+	#define LIBSPEC
+#endif
+
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c
new file mode 100644
index 0000000..bcde3ad
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.c
@@ -0,0 +1,554 @@
+/* $Id: getgateway.c,v 1.19 2009/12/19 15:20:45 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2009, Thomas BERNARD <miniupnp@free.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <stdio.h>
+#include <ctype.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#endif
+#if !defined(_MSC_VER)
+#include <sys/param.h>
+#endif
+/* There is no portable method to get the default route gateway.
+ * So below are four (or five ?) differents functions implementing this.
+ * Parsing /proc/net/route is for linux.
+ * sysctl is the way to access such informations on BSD systems.
+ * Many systems should provide route information through raw PF_ROUTE
+ * sockets.
+ * In MS Windows, default gateway is found by looking into the registry
+ * or by using GetBestRoute(). */
+#ifdef __linux__
+#define USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef BSD
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef __APPLE__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#define USE_SYSCTL_NET_ROUTE
+#endif
+
+#if (defined(sun) && defined(__SVR4))
+#undef USE_PROC_NET_ROUTE
+#define USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#endif
+
+#ifdef WIN32
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+//#define USE_WIN32_CODE
+#define USE_WIN32_CODE_2
+#endif
+
+#ifdef __CYGWIN__
+#undef USE_PROC_NET_ROUTE
+#undef USE_SOCKET_ROUTE
+#undef USE_SYSCTL_NET_ROUTE
+#define USE_WIN32_CODE
+#include <stdarg.h>
+#include <w32api/windef.h>
+#include <w32api/winbase.h>
+#include <w32api/winreg.h>
+#endif 
+
+#ifdef __HAIKU__
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <sys/sockio.h>
+#define USE_HAIKU_CODE
+#endif 
+
+#ifdef USE_SYSCTL_NET_ROUTE
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#endif
+#ifdef USE_SOCKET_ROUTE
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#endif
+
+#ifdef USE_WIN32_CODE
+#include <unknwn.h>
+#include <winreg.h>
+#define MAX_KEY_LENGTH 255
+#define MAX_VALUE_LENGTH 16383
+#endif
+
+#ifdef USE_WIN32_CODE_2
+#include <windows.h>
+#include <iphlpapi.h>
+#endif
+
+#include "getgateway.h"
+
+#ifndef WIN32
+#define SUCCESS (0)
+#define FAILED  (-1)
+#endif
+
+#ifdef USE_PROC_NET_ROUTE
+/*
+ parse /proc/net/route which is as follow :
+
+Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT           
+wlan0   0001A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0              
+eth0    0000FEA9        00000000        0001    0       0       0       0000FFFF        0       0       0              
+wlan0   00000000        0101A8C0        0003    0       0       0       00000000        0       0       0              
+eth0    00000000        00000000        0001    0       0       1000    00000000        0       0       0              
+
+ One header line, and then one line by route by route table entry.
+*/
+int getdefaultgateway(in_addr_t * addr)
+{
+	unsigned long d, g;
+	char buf[256];
+	int line = 0;
+	FILE * f;
+	char * p;
+	f = fopen("/proc/net/route", "r");
+	if(!f)
+		return FAILED;
+	while(fgets(buf, sizeof(buf), f)) {
+		if(line > 0) {	/* skip the first line */
+			p = buf;
+			/* skip the interface name */
+			while(*p && !isspace(*p))
+				p++;
+			while(*p && isspace(*p))
+				p++;
+			if(sscanf(p, "%lx%lx", &d, &g)==2) {
+				if(d == 0 && g != 0) { /* default */
+					*addr = g;
+					fclose(f);
+					return SUCCESS;
+				}
+			}
+		}
+		line++;
+	}
+	/* default route not found ! */
+	if(f)
+		fclose(f);
+	return FAILED;
+}
+#endif /* #ifdef USE_PROC_NET_ROUTE */
+
+
+#ifdef USE_SYSCTL_NET_ROUTE
+
+#define ROUNDUP(a) \
+	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+int getdefaultgateway(in_addr_t * addr)
+{
+#if 0
+	/* net.route.0.inet.dump.0.0 ? */
+	int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+	             NET_RT_DUMP, 0, 0/*tableid*/};
+#endif
+	/* net.route.0.inet.flags.gateway */
+	int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
+	             NET_RT_FLAGS, RTF_GATEWAY};
+	size_t l;
+	char * buf, * p;
+	struct rt_msghdr * rt;
+	struct sockaddr * sa;
+	struct sockaddr * sa_tab[RTAX_MAX];
+	int i;
+	int r = FAILED;
+	if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
+		return FAILED;
+	}
+	if(l>0) {
+		buf = malloc(l);
+		if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
+			free(buf);
+			return FAILED;
+		}
+		for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
+			rt = (struct rt_msghdr *)p;
+			sa = (struct sockaddr *)(rt + 1);
+			for(i=0; i<RTAX_MAX; i++) {
+				if(rt->rtm_addrs & (1 << i)) {
+					sa_tab[i] = sa;
+					sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
+				} else {
+					sa_tab[i] = NULL;
+				}
+			}
+			if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
+              && sa_tab[RTAX_DST]->sa_family == AF_INET
+              && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
+				if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
+					*addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
+					r = SUCCESS;
+				}
+			}
+		}
+		free(buf);
+	}
+	return r;
+}
+#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
+
+
+#ifdef USE_SOCKET_ROUTE
+/* Thanks to Darren Kenny for this code */
+#define NEXTADDR(w, u) \
+        if (rtm_addrs & (w)) {\
+            l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
+        }
+
+#define rtm m_rtmsg.m_rtm
+
+struct {
+  struct rt_msghdr m_rtm;
+  char       m_space[512];
+} m_rtmsg;
+
+int getdefaultgateway(in_addr_t *addr)
+{
+  int s, seq, l, rtm_addrs, i;
+  pid_t pid;
+  struct sockaddr so_dst, so_mask;
+  char *cp = m_rtmsg.m_space; 
+  struct sockaddr *gate = NULL, *sa;
+  struct rt_msghdr *msg_hdr;
+
+  pid = getpid();
+  seq = 0;
+  rtm_addrs = RTA_DST | RTA_NETMASK;
+
+  memset(&so_dst, 0, sizeof(so_dst));
+  memset(&so_mask, 0, sizeof(so_mask));
+  memset(&rtm, 0, sizeof(struct rt_msghdr));
+
+  rtm.rtm_type = RTM_GET;
+  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+  rtm.rtm_version = RTM_VERSION;
+  rtm.rtm_seq = ++seq;
+  rtm.rtm_addrs = rtm_addrs; 
+
+  so_dst.sa_family = AF_INET;
+  so_mask.sa_family = AF_INET;
+
+  NEXTADDR(RTA_DST, so_dst);
+  NEXTADDR(RTA_NETMASK, so_mask);
+
+  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+  s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+  if (write(s, (char *)&m_rtmsg, l) < 0) {
+      close(s);
+      return FAILED;
+  }
+
+  do {
+    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+                        
+  close(s);
+
+  msg_hdr = &rtm;
+
+  cp = ((char *)(msg_hdr + 1));
+  if (msg_hdr->rtm_addrs) {
+    for (i = 1; i; i <<= 1)
+      if (i & msg_hdr->rtm_addrs) {
+        sa = (struct sockaddr *)cp;
+        if (i == RTA_GATEWAY )
+          gate = sa;
+
+        cp += sizeof(struct sockaddr);
+      }
+  } else {
+      return FAILED;
+  }
+
+
+  if (gate != NULL ) {
+      *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr;
+      return SUCCESS;
+  } else {
+      return FAILED;
+  }
+}
+#endif /* #ifdef USE_SOCKET_ROUTE */
+
+#ifdef USE_WIN32_CODE
+LIBSPEC int getdefaultgateway(in_addr_t * addr)
+{
+	HKEY networkCardsKey;
+	HKEY networkCardKey;
+	HKEY interfacesKey;
+	HKEY interfaceKey;
+	DWORD i = 0;
+	DWORD numSubKeys = 0;
+	TCHAR keyName[MAX_KEY_LENGTH];
+	DWORD keyNameLength = MAX_KEY_LENGTH;
+	TCHAR keyValue[MAX_VALUE_LENGTH];
+	DWORD keyValueLength = MAX_VALUE_LENGTH;
+	DWORD keyValueType = REG_SZ;
+	TCHAR gatewayValue[MAX_VALUE_LENGTH];
+	DWORD gatewayValueLength = MAX_VALUE_LENGTH;
+	DWORD gatewayValueType = REG_MULTI_SZ;
+	int done = 0;
+	
+	//const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	//const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#ifdef UNICODE
+	LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME	 L"ServiceName"
+#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY	L"DefaultGateway"
+#else
+	LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
+	LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
+#define STR_SERVICENAME	 "ServiceName"
+#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway"
+#define STR_DEFAULTGATEWAY	"DefaultGateway"
+#endif
+	// The windows registry lists its primary network devices in the following location:
+	// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
+	// 
+	// Each network device has its own subfolder, named with an index, with various properties:
+	// -NetworkCards
+	//   -5
+	//     -Description = Broadcom 802.11n Network Adapter
+	//     -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
+	//   -8
+	//     -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
+	//     -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
+	// 
+	// The above service name is the name of a subfolder within:
+	// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
+	// 
+	// There may be more subfolders in this interfaces path than listed in the network cards path above:
+	// -Interfaces
+	//   -{3a539854-6a70-11db-887c-806e6f6e6963}
+	//     -DhcpIPAddress = 0.0.0.0
+	//     -[more]
+	//   -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
+	//     -DhcpIPAddress = 10.0.1.4
+	//     -DhcpDefaultGateway = 10.0.1.1
+	//     -[more]
+	//   -{86226414-5545-4335-A9D1-5BD7120119AD}
+	//     -DhcpIpAddress = 10.0.1.5
+	//     -DhcpDefaultGateay = 10.0.1.1
+	//     -[more]
+	// 
+	// In order to extract this information, we enumerate each network card, and extract the ServiceName value.
+	// This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
+	// Once one is found, we're done.
+	// 
+	// It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
+	// However, the technique used is the technique most cited on the web, and we assume it to be more correct.
+	
+	if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key 
+	                                 networkCardsPath,   // Name of registry subkey to open
+	                                 0,                  // Reserved - must be zero
+	                                 KEY_READ,           // Mask - desired access rights
+	                                 &networkCardsKey))  // Pointer to output key
+	{
+		// Unable to open network cards keys
+		return -1;
+	}
+	
+	if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key
+	                                 interfacesPath,     // Name of registry subkey to open
+	                                 0,                  // Reserved - must be zero
+	                                 KEY_READ,           // Mask - desired access rights
+	                                 &interfacesKey))    // Pointer to output key
+	{
+		// Unable to open interfaces key
+		RegCloseKey(networkCardsKey);
+		return -1;
+	}
+	
+	// Figure out how many subfolders are within the NetworkCards folder
+	RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+	
+	//printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
+	
+	// Enumrate through each subfolder within the NetworkCards folder
+	for(i = 0; i < numSubKeys && !done; i++)
+	{
+		keyNameLength = MAX_KEY_LENGTH;
+		if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key
+		                                 i,               // Index of subkey to retrieve
+		                                 keyName,         // Buffer that receives the name of the subkey
+		                                 &keyNameLength,  // Variable that receives the size of the above buffer
+		                                 NULL,            // Reserved - must be NULL
+		                                 NULL,            // Buffer that receives the class string
+		                                 NULL,            // Variable that receives the size of the above buffer
+		                                 NULL))           // Variable that receives the last write time of subkey
+		{
+			if(RegOpenKeyEx(networkCardsKey,  keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS)
+			{
+				keyValueLength = MAX_VALUE_LENGTH;
+				if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey,   // Open registry key
+				                                    STR_SERVICENAME,    // Name of key to query
+				                                    NULL,             // Reserved - must be NULL
+				                                    &keyValueType,    // Receives value type
+				                                    (LPBYTE)keyValue, // Receives value
+				                                    &keyValueLength)) // Receives value length in bytes
+				{
+//					printf("keyValue: %s\n", keyValue);				
+					if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS)
+					{
+						gatewayValueLength = MAX_VALUE_LENGTH;
+						if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey,         // Open registry key
+						                                    STR_DHCPDEFAULTGATEWAY, // Name of key to query
+						                                    NULL,                 // Reserved - must be NULL
+						                                    &gatewayValueType,    // Receives value type
+						                                    (LPBYTE)gatewayValue, // Receives value
+						                                    &gatewayValueLength)) // Receives value length in bytes
+						{
+							// Check to make sure it's a string
+							if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+							{
+								//printf("gatewayValue: %s\n", gatewayValue);
+								done = 1;
+							}
+						}
+						else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey,         // Open registry key
+						                                    STR_DEFAULTGATEWAY, // Name of key to query
+						                                    NULL,                 // Reserved - must be NULL
+						                                    &gatewayValueType,    // Receives value type
+						                                    (LPBYTE)gatewayValue,// Receives value
+						                                    &gatewayValueLength)) // Receives value length in bytes
+						{
+							// Check to make sure it's a string
+							if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
+							{
+								//printf("gatewayValue: %s\n", gatewayValue);
+								done = 1;
+							}
+						}
+						RegCloseKey(interfaceKey);
+					}
+				}
+				RegCloseKey(networkCardKey);
+			}
+		}
+	}
+	
+	RegCloseKey(interfacesKey);
+	RegCloseKey(networkCardsKey);
+	
+	if(done)
+	{
+#if UNICODE
+		char tmp[32];
+		for(i = 0; i < 32; i++) {
+			tmp[i] = (char)gatewayValue[i];
+			if(!tmp[i])
+				break;
+		}
+		tmp[31] = '\0';
+		*addr = inet_addr(tmp);
+#else
+		*addr = inet_addr(gatewayValue);
+#endif
+		return 0;
+	}
+	
+	return -1;
+}
+#endif /* #ifdef USE_WIN32_CODE */
+
+#ifdef USE_WIN32_CODE_2
+int getdefaultgateway(in_addr_t *addr)
+{
+	MIB_IPFORWARDROW ip_forward;
+	memset(&ip_forward, 0, sizeof(ip_forward));
+	if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR)
+		return -1;
+	*addr = ip_forward.dwForwardNextHop;
+	return 0;
+}
+#endif /* #ifdef USE_WIN32_CODE_2 */
+
+#ifdef USE_HAIKU_CODE
+int getdefaultgateway(in_addr_t *addr)
+{
+    int fd, ret = -1;
+    struct ifconf config;
+    void *buffer = NULL;
+    struct ifreq *interface;
+
+    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        return -1;
+    }
+    if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) {
+        goto fail;
+    }
+    if (config.ifc_value < 1) {
+        goto fail; /* No routes */
+    }
+    if ((buffer = malloc(config.ifc_value)) == NULL) {
+        goto fail;
+    }
+    config.ifc_len = config.ifc_value;
+    config.ifc_buf = buffer;
+    if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) {
+        goto fail;
+    }
+    for (interface = buffer;
+      (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) {
+        struct route_entry route = interface->ifr_route;
+        int intfSize;
+        if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) {
+            *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr;
+            ret = 0;
+            break;
+        }
+        intfSize = sizeof(route) + IF_NAMESIZE;
+        if (route.destination != NULL) {
+            intfSize += route.destination->sa_len;
+        }
+        if (route.mask != NULL) {
+            intfSize += route.mask->sa_len;
+        }
+        if (route.gateway != NULL) {
+            intfSize += route.gateway->sa_len;
+        }
+        interface = (struct ifreq *)((uint8_t *)interface + intfSize);
+    }
+fail:
+    free(buffer);
+    close(fd);
+    return ret;
+}
+#endif /* #ifdef USE_HAIKU_CODE */
+
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h
new file mode 100644
index 0000000..9432528
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/getgateway.h
@@ -0,0 +1,36 @@
+/* $Id: getgateway.h,v 1.4 2009/12/19 12:00:00 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007, Thomas BERNARD <miniupnp@free.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __GETGATEWAY_H__
+#define __GETGATEWAY_H__
+
+#ifdef WIN32
+#if !defined(_MSC_VER)
+#include <stdint.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+#define in_addr_t uint32_t
+#endif
+#include "declspec.h"
+
+/* getdefaultgateway() :
+ * return value :
+ *    0 : success
+ *   -1 : failure    */
+LIBSPEC int getdefaultgateway(in_addr_t * addr);
+
+#endif
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c
new file mode 100644
index 0000000..53869c3
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.c
@@ -0,0 +1,350 @@
+/* $Id: natpmp.c,v 1.13 2011/01/03 17:31:03 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifdef __linux__
+#define _BSD_SOURCE 1
+#endif
+#include <string.h>
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <errno.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define ECONNREFUSED WSAECONNREFUSED
+#include "wingettimeofday.h"
+#else
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#define closesocket close
+#endif
+#include "natpmp.h"
+#include "getgateway.h"
+
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
+{
+#ifdef WIN32
+	u_long ioctlArg = 1;
+#else
+	int flags; 
+#endif
+	struct sockaddr_in addr;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	memset(p, 0, sizeof(natpmp_t));
+	p->s = socket(PF_INET, SOCK_DGRAM, 0);
+	if(p->s < 0)
+		return NATPMP_ERR_SOCKETERROR;
+#ifdef WIN32
+	if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
+		return NATPMP_ERR_FCNTLERROR;
+#else
+	if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
+		return NATPMP_ERR_FCNTLERROR;
+	if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
+		return NATPMP_ERR_FCNTLERROR;
+#endif
+
+	if(forcegw) {
+		p->gateway = forcedgw;
+	} else {
+		if(getdefaultgateway(&(p->gateway)) < 0)
+			return NATPMP_ERR_CANNOTGETGATEWAY;
+	}
+	
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(NATPMP_PORT);
+	addr.sin_addr.s_addr = p->gateway;
+	if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+		return NATPMP_ERR_CONNECTERR;
+	return 0;
+}
+
+LIBSPEC int closenatpmp(natpmp_t * p)
+{
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	if(closesocket(p->s) < 0)
+		return NATPMP_ERR_CLOSEERR;
+	return 0;
+}
+
+int sendpendingrequest(natpmp_t * p)
+{
+	int r;
+/*	struct sockaddr_in addr;*/
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+/*	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(NATPMP_PORT);
+	addr.sin_addr.s_addr = p->gateway;
+	r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
+	                   (struct sockaddr *)&addr, sizeof(addr));*/
+	r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
+	return (r<0) ? NATPMP_ERR_SENDERR : r;
+}
+
+int sendnatpmprequest(natpmp_t * p)
+{
+	int n;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	/* TODO : check if no request is allready pending */
+	p->has_pending_request = 1;
+	p->try_number = 1;
+	n = sendpendingrequest(p);
+	gettimeofday(&p->retry_time, NULL);	// check errors !
+	p->retry_time.tv_usec += 250000;	/* add 250ms */
+	if(p->retry_time.tv_usec >= 1000000) {
+		p->retry_time.tv_usec -= 1000000;
+		p->retry_time.tv_sec++;
+	}
+	return n;
+}
+
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
+{
+	struct timeval now;
+	if(!p || !timeout)
+		return NATPMP_ERR_INVALIDARGS;
+	if(!p->has_pending_request)
+		return NATPMP_ERR_NOPENDINGREQ;
+	if(gettimeofday(&now, NULL) < 0)
+		return NATPMP_ERR_GETTIMEOFDAYERR;
+	timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
+	timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
+	if(timeout->tv_usec < 0) {
+		timeout->tv_usec += 1000000;
+		timeout->tv_sec--;
+	}
+	return 0;
+}
+
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
+{
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	//static const unsigned char request[] = { 0, 0 };
+	p->pending_request[0] = 0;
+	p->pending_request[1] = 0;
+	p->pending_request_len = 2;
+	// TODO: return 0 instead of sizeof(request) ??
+	return sendnatpmprequest(p);
+}
+
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+                              uint16_t privateport, uint16_t publicport,
+							  uint32_t lifetime)
+{
+	if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
+		return NATPMP_ERR_INVALIDARGS;
+	p->pending_request[0] = 0;
+	p->pending_request[1] = protocol;
+	p->pending_request[2] = 0;
+	p->pending_request[3] = 0;
+	*((uint16_t *)(p->pending_request + 4)) = htons(privateport);
+	*((uint16_t *)(p->pending_request + 6)) = htons(publicport);
+	*((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
+	p->pending_request_len = 12;
+	return sendnatpmprequest(p);
+}
+
+LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
+{
+	unsigned char buf[16];
+	struct sockaddr_in addr;
+	socklen_t addrlen = sizeof(addr);
+	int n;
+	if(!p)
+		return NATPMP_ERR_INVALIDARGS;
+	n = recvfrom(p->s, buf, sizeof(buf), 0,
+	             (struct sockaddr *)&addr, &addrlen);
+	if(n<0)
+		switch(errno) {
+		/*case EAGAIN:*/
+		case EWOULDBLOCK:
+			n = NATPMP_TRYAGAIN;
+			break;
+		case ECONNREFUSED:
+			n = NATPMP_ERR_NOGATEWAYSUPPORT;
+			break;
+		default:
+			n = NATPMP_ERR_RECVFROM;
+		}
+	/* check that addr is correct (= gateway) */
+	else if(addr.sin_addr.s_addr != p->gateway)
+		n = NATPMP_ERR_WRONGPACKETSOURCE;
+	else {
+		response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
+		response->epoch = ntohl(*((uint32_t *)(buf + 4)));
+		if(buf[0] != 0)
+			n = NATPMP_ERR_UNSUPPORTEDVERSION;
+		else if(buf[1] < 128 || buf[1] > 130)
+			n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+		else if(response->resultcode != 0) {
+			switch(response->resultcode) {
+			case 1:
+				n = NATPMP_ERR_UNSUPPORTEDVERSION;
+				break;
+			case 2:
+				n = NATPMP_ERR_NOTAUTHORIZED;
+				break;
+			case 3:
+				n = NATPMP_ERR_NETWORKFAILURE;
+				break;
+			case 4:
+				n = NATPMP_ERR_OUTOFRESOURCES;
+				break;
+			case 5:
+				n = NATPMP_ERR_UNSUPPORTEDOPCODE;
+				break;
+			default:
+				n = NATPMP_ERR_UNDEFINEDERROR;
+			}
+		} else {
+			response->type = buf[1] & 0x7f;
+			if(buf[1] == 128)
+				//response->publicaddress.addr = *((uint32_t *)(buf + 8));
+				response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
+			else {
+				response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
+				response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
+				response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
+			}
+			n = 0;
+		}
+	}
+	return n;
+}
+
+int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
+{
+	int n;
+	if(!p || !response)
+		return NATPMP_ERR_INVALIDARGS;
+	if(!p->has_pending_request)
+		return NATPMP_ERR_NOPENDINGREQ;
+	n = readnatpmpresponse(p, response);
+	if(n<0) {
+		if(n==NATPMP_TRYAGAIN) {
+			struct timeval now;
+			gettimeofday(&now, NULL);	// check errors !
+			if(timercmp(&now, &p->retry_time, >=)) {
+				int delay, r;
+				if(p->try_number >= 9) {
+					return NATPMP_ERR_NOGATEWAYSUPPORT;
+				}
+				/*printf("retry! %d\n", p->try_number);*/
+				delay = 250 * (1<<p->try_number);	// ms
+				/*for(i=0; i<p->try_number; i++)
+					delay += delay;*/
+				p->retry_time.tv_sec += (delay / 1000);
+				p->retry_time.tv_usec += (delay % 1000) * 1000;
+				if(p->retry_time.tv_usec >= 1000000) {
+					p->retry_time.tv_usec -= 1000000;
+					p->retry_time.tv_sec++;
+				}
+				p->try_number++;
+				r = sendpendingrequest(p);
+				if(r<0)
+					return r;
+			}
+		}
+	} else {
+		p->has_pending_request = 0;
+	}
+	return n;
+}
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int r)
+{
+	const char * s;
+	switch(r) {
+	case NATPMP_ERR_INVALIDARGS:
+		s = "invalid arguments";
+		break;
+	case NATPMP_ERR_SOCKETERROR:
+		s = "socket() failed";
+		break;
+	case NATPMP_ERR_CANNOTGETGATEWAY:
+		s = "cannot get default gateway ip address";
+		break;
+	case NATPMP_ERR_CLOSEERR:
+#ifdef WIN32
+		s = "closesocket() failed";
+#else
+		s = "close() failed";
+#endif
+		break;
+	case NATPMP_ERR_RECVFROM:
+		s = "recvfrom() failed";
+		break;
+	case NATPMP_ERR_NOPENDINGREQ:
+		s = "no pending request";
+		break;
+	case NATPMP_ERR_NOGATEWAYSUPPORT:
+		s = "the gateway does not support nat-pmp";
+		break;
+	case NATPMP_ERR_CONNECTERR:
+		s = "connect() failed";
+		break;
+	case NATPMP_ERR_WRONGPACKETSOURCE:
+		s = "packet not received from the default gateway";
+		break;
+	case NATPMP_ERR_SENDERR:
+		s = "send() failed";
+		break;
+	case NATPMP_ERR_FCNTLERROR:
+		s = "fcntl() failed";
+		break;
+	case NATPMP_ERR_GETTIMEOFDAYERR:
+		s = "gettimeofday() failed";
+		break;
+	case NATPMP_ERR_UNSUPPORTEDVERSION:
+		s = "unsupported nat-pmp version error from server";
+		break;
+	case NATPMP_ERR_UNSUPPORTEDOPCODE:
+		s = "unsupported nat-pmp opcode error from server";
+		break;
+	case NATPMP_ERR_UNDEFINEDERROR:
+		s = "undefined nat-pmp server error";
+		break;
+	case NATPMP_ERR_NOTAUTHORIZED:
+		s = "not authorized";
+		break;
+	case NATPMP_ERR_NETWORKFAILURE:
+		s = "network failure";
+		break;
+	case NATPMP_ERR_OUTOFRESOURCES:
+		s = "nat-pmp server out of resources";
+		break;
+	default:
+		s = "Unknown libnatpmp error";
+	}
+	return s;
+}
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h
new file mode 100644
index 0000000..1175b58
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmp.h
@@ -0,0 +1,203 @@
+/* $Id: natpmp.h,v 1.14 2011/01/03 17:31:03 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __NATPMP_H__
+#define __NATPMP_H__
+
+/* NAT-PMP Port as defined by the NAT-PMP draft */
+#define NATPMP_PORT (5351)
+
+#include <time.h>
+#if !defined(_MSC_VER)
+#include <sys/time.h>
+#endif
+#ifdef WIN32
+#include <winsock2.h>
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned long uint32_t;
+typedef unsigned short uint16_t;
+#endif
+#define in_addr_t uint32_t
+#include "declspec.h"
+#else
+#define LIBSPEC
+#include <netinet/in.h>
+#endif
+
+typedef struct {
+	int s;	/* socket */
+	in_addr_t gateway;	/* default gateway (IPv4) */
+	int has_pending_request;
+	unsigned char pending_request[12];
+	int pending_request_len;
+	int try_number;
+	struct timeval retry_time;
+} natpmp_t;
+
+typedef struct {
+	uint16_t type;	/* NATPMP_RESPTYPE_* */
+	uint16_t resultcode;	/* NAT-PMP response code */
+	uint32_t epoch;	/* Seconds since start of epoch */
+	union {
+		struct {
+			//in_addr_t addr;
+			struct in_addr addr;
+		} publicaddress;
+		struct {
+			uint16_t privateport;
+			uint16_t mappedpublicport;
+			uint32_t lifetime;
+		} newportmapping;
+	} pnu;
+} natpmpresp_t;
+
+/* possible values for type field of natpmpresp_t */
+#define NATPMP_RESPTYPE_PUBLICADDRESS (0)
+#define NATPMP_RESPTYPE_UDPPORTMAPPING (1)
+#define NATPMP_RESPTYPE_TCPPORTMAPPING (2)
+
+/* Values to pass to sendnewportmappingrequest() */
+#define NATPMP_PROTOCOL_UDP (1)
+#define NATPMP_PROTOCOL_TCP (2)
+
+/* return values */
+/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */
+#define NATPMP_ERR_INVALIDARGS (-1)
+/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */
+#define NATPMP_ERR_SOCKETERROR (-2)
+/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */
+#define NATPMP_ERR_CANNOTGETGATEWAY (-3)
+/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */
+#define NATPMP_ERR_CLOSEERR (-4)
+/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */
+#define NATPMP_ERR_RECVFROM (-5)
+/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while
+ * no NAT-PMP request was pending */
+#define NATPMP_ERR_NOPENDINGREQ (-6)
+/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */
+#define NATPMP_ERR_NOGATEWAYSUPPORT (-7)
+/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */
+#define NATPMP_ERR_CONNECTERR (-8)
+/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */
+#define NATPMP_ERR_WRONGPACKETSOURCE (-9)
+/* NATPMP_ERR_SENDERR : send() failed. check errno for details */
+#define NATPMP_ERR_SENDERR (-10)
+/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */
+#define NATPMP_ERR_FCNTLERROR (-11)
+/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */
+#define NATPMP_ERR_GETTIMEOFDAYERR (-12)
+
+/* */
+#define NATPMP_ERR_UNSUPPORTEDVERSION (-14)
+#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15)
+
+/* Errors from the server : */
+#define NATPMP_ERR_UNDEFINEDERROR (-49)
+#define NATPMP_ERR_NOTAUTHORIZED (-51)
+#define NATPMP_ERR_NETWORKFAILURE (-52)
+#define NATPMP_ERR_OUTOFRESOURCES (-53)
+
+/* NATPMP_TRYAGAIN : no data available for the moment. try again later */
+#define NATPMP_TRYAGAIN (-100)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* initnatpmp()
+ * initialize a natpmp_t object
+ * With forcegw=1 the gateway is not detected automaticaly.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SOCKETERROR
+ * NATPMP_ERR_FCNTLERROR
+ * NATPMP_ERR_CANNOTGETGATEWAY
+ * NATPMP_ERR_CONNECTERR */
+LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw);
+
+/* closenatpmp()
+ * close resources associated with a natpmp_t object
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_CLOSEERR */
+LIBSPEC int closenatpmp(natpmp_t * p);
+
+/* sendpublicaddressrequest()
+ * send a public address NAT-PMP request to the network gateway
+ * Return values :
+ * 2 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendpublicaddressrequest(natpmp_t * p);
+
+/* sendnewportmappingrequest()
+ * send a new port mapping NAT-PMP request to the network gateway
+ * Arguments :
+ * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP,
+ * lifetime is in seconds.
+ * To remove a port mapping, set lifetime to zero.
+ * To remove all port mappings to the host, set lifetime and both ports
+ * to zero.
+ * Return values :
+ * 12 = OK (size of the request)
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_SENDERR */
+LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
+                              uint16_t privateport, uint16_t publicport,
+							  uint32_t lifetime);
+
+/* getnatpmprequesttimeout()
+ * fills the timeval structure with the timeout duration of the
+ * currently pending NAT-PMP request.
+ * Return values :
+ * 0 = OK
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_GETTIMEOFDAYERR
+ * NATPMP_ERR_NOPENDINGREQ */
+LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout);
+
+/* readnatpmpresponseorretry()
+ * fills the natpmpresp_t structure if possible
+ * Return values :
+ * 0 = OK
+ * NATPMP_TRYAGAIN
+ * NATPMP_ERR_INVALIDARGS
+ * NATPMP_ERR_NOPENDINGREQ
+ * NATPMP_ERR_NOGATEWAYSUPPORT
+ * NATPMP_ERR_RECVFROM
+ * NATPMP_ERR_WRONGPACKETSOURCE
+ * NATPMP_ERR_UNSUPPORTEDVERSION
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_NOTAUTHORIZED
+ * NATPMP_ERR_NETWORKFAILURE
+ * NATPMP_ERR_OUTOFRESOURCES
+ * NATPMP_ERR_UNSUPPORTEDOPCODE
+ * NATPMP_ERR_UNDEFINEDERROR */
+LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response);
+
+#ifdef ENABLE_STRNATPMPERR
+LIBSPEC const char * strnatpmperr(int t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c b/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c
new file mode 100644
index 0000000..d869572
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/natpmpc.c
@@ -0,0 +1,229 @@
+/* $Id: natpmpc.c,v 1.9 2011/04/18 18:25:21 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2011, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#if defined(_MSC_VER)
+#if _MSC_VER >= 1400
+#define strcasecmp _stricmp
+#else
+#define strcasecmp stricmp
+#endif
+#else
+#include <unistd.h>
+#endif
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "natpmp.h"
+
+void usage(FILE * out, const char * argv0)
+{
+	fprintf(out, "Usage :\n");
+	fprintf(out, "  %s [options]\n", argv0);
+	fprintf(out, "\tdisplay the public IP address.\n");
+	fprintf(out, "  %s -h\n", argv0);
+	fprintf(out, "\tdisplay this help screen.\n");
+	fprintf(out, "  %s [options] -a <public port> <private port> <protocol> [lifetime]\n", argv0);
+	fprintf(out, "\tadd a port mapping.\n");
+	fprintf(out, "\nOption available :\n");
+	fprintf(out, "  -g ipv4address\n");
+	fprintf(out, "\tforce the gateway to be used as destination for NAT-PMP commands.\n");
+	fprintf(out, "\n  In order to remove a mapping, set it with a lifetime of 0 seconds.\n");
+	fprintf(out, "  To remove all mappings for your machine, use 0 as private port and lifetime.\n");
+}
+
+/* sample code for using libnatpmp */
+int main(int argc, char * * argv)
+{
+	natpmp_t natpmp;
+	natpmpresp_t response;
+	int r;
+	int sav_errno;
+	struct timeval timeout;
+	fd_set fds;
+	int i;
+	int protocol = 0;
+	uint16_t privateport = 0;
+	uint16_t publicport = 0;
+	uint32_t lifetime = 3600;
+	int command = 0;
+	int forcegw = 0;
+	in_addr_t gateway = 0;
+
+#ifdef WIN32
+	WSADATA wsaData;
+	int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+	if(nResult != NO_ERROR)
+	{
+		fprintf(stderr, "WSAStartup() failed.\n");
+		return -1;
+	}
+#endif
+
+	/* argument parsing */
+	for(i=1; i<argc; i++) {
+		if(argv[i][0] == '-') {
+			switch(argv[i][1]) {
+			case 'h':
+				usage(stdout, argv[0]);
+				return 0;
+			case 'g':
+				forcegw = 1;
+				if(argc < i + 1) {
+					fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+					return 1;
+				}
+				gateway = inet_addr(argv[++i]);
+				break;
+			case 'a':
+				command = 'a';
+				if(argc < i + 3) {
+					fprintf(stderr, "Not enough arguments for option -%c\n", argv[i][1]);
+					return 1;
+				}
+				i++;
+				if(1 != sscanf(argv[i], "%hu", &publicport)) {
+					fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+					return 1;
+				}
+				i++;
+				if(1 != sscanf(argv[i], "%hu", &privateport)) {
+					fprintf(stderr, "%s is not a correct 16bits unsigned integer\n", argv[i]);
+					return 1;
+				}
+				i++;
+				if(0 == strcasecmp(argv[i], "tcp"))
+					protocol = NATPMP_PROTOCOL_TCP;
+				else if(0 == strcasecmp(argv[i], "udp"))
+					protocol = NATPMP_PROTOCOL_UDP;
+				else {
+					fprintf(stderr, "%s is not a valid protocol\n", argv[i]);
+					return 1;
+				}
+				if(argc >= i) {
+					i++;
+					if(1 != sscanf(argv[i], "%u", &lifetime)) {
+						fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]);
+					}
+				}
+				break;
+			default:
+				fprintf(stderr, "Unknown option %s\n", argv[i]);
+				usage(stderr, argv[0]);
+				return 1;
+			}
+		} else {
+			fprintf(stderr, "Unknown option %s\n", argv[i]);
+			usage(stderr, argv[0]);
+			return 1;
+		}
+	}
+
+	/* initnatpmp() */
+	r = initnatpmp(&natpmp, forcegw, gateway);
+	printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS");
+	if(r<0)
+		return 1;
+
+	printf("using gateway : %s\n", inet_ntoa(*((struct in_addr *)&natpmp.gateway)));
+
+	/* sendpublicaddressrequest() */
+	r = sendpublicaddressrequest(&natpmp);
+	printf("sendpublicaddressrequest returned %d (%s)\n",
+	       r, r==2?"SUCCESS":"FAILED");
+	if(r<0)
+		return 1;
+
+	do {
+		FD_ZERO(&fds);
+		FD_SET(natpmp.s, &fds);
+		getnatpmprequesttimeout(&natpmp, &timeout);
+		r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+		if(r<0) {
+			fprintf(stderr, "select()");
+			return 1;
+		}
+		r = readnatpmpresponseorretry(&natpmp, &response);
+		sav_errno = errno;
+		printf("readnatpmpresponseorretry returned %d (%s)\n",
+		       r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+		if(r<0 && r!=NATPMP_TRYAGAIN) {
+#ifdef ENABLE_STRNATPMPERR
+			fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+			        strnatpmperr(r));
+#endif
+			fprintf(stderr, "  errno=%d '%s'\n", 
+			        sav_errno, strerror(sav_errno));
+		}
+	} while(r==NATPMP_TRYAGAIN);
+	if(r<0)
+		return 1;
+
+	/* TODO : check that response.type == 0 */
+	printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr));
+	printf("epoch = %u\n", response.epoch);
+
+	if(command == 'a') {
+		/* sendnewportmappingrequest() */
+		r = sendnewportmappingrequest(&natpmp, protocol,
+        	                      privateport, publicport,
+								  lifetime);
+		printf("sendnewportmappingrequest returned %d (%s)\n",
+		       r, r==12?"SUCCESS":"FAILED");
+		if(r < 0)
+			return 1;
+
+		do {
+			FD_ZERO(&fds);
+			FD_SET(natpmp.s, &fds);
+			getnatpmprequesttimeout(&natpmp, &timeout);
+			select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+			r = readnatpmpresponseorretry(&natpmp, &response);
+			printf("readnatpmpresponseorretry returned %d (%s)\n",
+			       r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED"));
+		} while(r==NATPMP_TRYAGAIN);
+		if(r<0) {
+#ifdef ENABLE_STRNATPMPERR
+			fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n",
+			        strnatpmperr(r));
+#endif
+			return 1;
+		}
+	
+		printf("Mapped public port %hu protocol %s to local port %hu "
+		       "liftime %u\n",
+	    	   response.pnu.newportmapping.mappedpublicport,
+			   response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" :
+			    (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" :
+			     "UNKNOWN"),
+			   response.pnu.newportmapping.privateport,
+			   response.pnu.newportmapping.lifetime);
+		printf("epoch = %u\n", response.epoch);
+	}
+
+	r = closenatpmp(&natpmp);
+	printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED");
+	if(r<0)
+		return 1;
+
+	return 0;
+}
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c
new file mode 100644
index 0000000..5b1b8a6
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.c
@@ -0,0 +1,50 @@
+/* $Id: wingettimeofday.c,v 1.3 2009/12/19 12:00:00 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifdef WIN32
+#if defined(_MSC_VER)
+struct timeval {
+	long    tv_sec;
+	long    tv_usec;
+};
+#else
+#include <sys/time.h>
+#endif
+
+typedef struct _FILETIME {
+    unsigned long dwLowDateTime;
+    unsigned long dwHighDateTime;
+} FILETIME;
+
+void __stdcall GetSystemTimeAsFileTime(FILETIME*);
+  
+//int gettimeofday(struct timeval* p, void* tz /* IGNORED */);
+
+int gettimeofday(struct timeval* p, void* tz /* IGNORED */) {
+  union {
+   long long ns100; /*time since 1 Jan 1601 in 100ns units */
+   FILETIME ft;
+  } _now;
+
+	if(!p)
+		return -1;
+  GetSystemTimeAsFileTime( &(_now.ft) );
+  p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL );
+  p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL);
+  return 0;
+}
+#endif
+
diff --git a/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h
new file mode 100644
index 0000000..ed6c599
--- /dev/null
+++ b/3rdParty/LibNATPMP/src/libnatpmp/wingettimeofday.h
@@ -0,0 +1,27 @@
+/* $Id: wingettimeofday.h,v 1.1 2009/12/19 12:02:42 nanard Exp $ */
+/* libnatpmp
+ * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
+ * http://miniupnp.free.fr/libnatpmp.html
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#ifndef __WINGETTIMEOFDAY_H__
+#define __WINGETTIMEOFDAY_H__
+#ifdef WIN32
+#if defined(_MSC_VER)
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+int gettimeofday(struct timeval* p, void* tz /* IGNORED */);
+#endif
+#endif
diff --git a/BuildTools/SCons/SConscript.boot b/BuildTools/SCons/SConscript.boot
index e969def..77b1cf1 100644
--- a/BuildTools/SCons/SConscript.boot
+++ b/BuildTools/SCons/SConscript.boot
@@ -180,7 +180,7 @@ else :
 	if gccVersion >= ["4", "5", "0"] :
 		env.Append(CXXFLAGS = ["-Wlogical-op"])
 	if "clang" in env["CC"] :
-		env.Append(CXXFLAGS = ["-W#warnings", "-W-Wc++0x-compat", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnonfragile-abi2", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-attributes", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros"])
+		env.Append(CXXFLAGS = ["-W#warnings", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnonfragile-abi2", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros"])
 # To enable: 
 # "-Wheader-hygiene"
 #	"-Wnon-gcc",
@@ -193,7 +193,7 @@ if env.get("coverage", 0) :
 	env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"])
 
 if env["PLATFORM"] == "win32" :
-	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "ws2_32", "wsock32", "Advapi32"])
+	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "iphlpapi", "ws2_32", "wsock32", "Advapi32"])
 	env.Append(CCFLAGS = ["/EHsc", "/nologo"])
 	# FIXME: We should find a decent solution for MSVS 10
 	if int(env["MSVS_VERSION"].split(".")[0]) < 10 :
diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct
index 88a195a..ba2f4b7 100644
--- a/BuildTools/SCons/SConstruct
+++ b/BuildTools/SCons/SConstruct
@@ -311,6 +311,27 @@ else :
 	env["LIBIDN_BUNDLED"] = 1
 conf.Finish()
 
+# LibMiniUPnPc
+libminiupnpc_conf_env = conf_env.Clone()
+
+conf = Configure(libminiupnpc_conf_env)
+if conf.CheckCHeader("miniupnpc.h") and conf.CheckLib(env["libminiupnpc_libname"]) :
+	print "NOT IMPLEMENTED YET"
+else :
+	env["LIBMINIUPNPC_BUNDLED"] = 1
+conf.Finish()
+
+# LibNATPMP
+libnatpmp_conf_env = conf_env.Clone()
+
+conf = Configure(libnatpmp_conf_env)
+if conf.CheckCHeader("natpmp.h") and conf.CheckLib(env["libnatpmp_libname"]) :
+	print "NOT IMPLEMENTED YET"
+else :
+	env["LIBNATPMP_BUNDLED"] = 1
+conf.Finish()
+
+
 # SQLite
 #sqlite_conf_env = conf_env.Clone()
 #sqlite_flags = {}
diff --git a/QA/Checker/IO.cpp b/QA/Checker/IO.cpp
index d9eadd6..41fe5d3 100644
--- a/QA/Checker/IO.cpp
+++ b/QA/Checker/IO.cpp
@@ -6,6 +6,7 @@
 
 #include <QA/Checker/IO.h>
 
+#include <algorithm>
 #include <iostream>
 
 std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s) {
@@ -55,3 +56,10 @@ std::ostream& operator<<(std::ostream& os, const std::vector<size_t>& s) {
 	os << std::endl;
 	return os;
 }
+
+bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b) {
+	if (a.size() != b.size()) {
+		return false;
+	}
+	return std::equal(a.begin(), a.end(), b.begin());
+}
diff --git a/QA/Checker/IO.h b/QA/Checker/IO.h
index 2545d24..3e40f7e 100644
--- a/QA/Checker/IO.h
+++ b/QA/Checker/IO.h
@@ -10,6 +10,7 @@
 #include <Swiften/Base/SafeByteArray.h>
 
 std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s);
+bool operator==(const Swift::ByteArray& a, const Swift::ByteArray& b);
 std::ostream& operator<<(std::ostream& os, const Swift::SafeByteArray& s);
 std::ostream& operator<<(std::ostream& os, const std::vector<int>& s);
 std::ostream& operator<<(std::ostream& os, const std::vector<size_t>& s);
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index f4aa745..a3d9fb5 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -20,8 +20,13 @@
 #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h>
 #include <Swiften/Client/NickResolver.h>
 #include <Swift/Controllers/XMPPEvents/EventController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
 #include <Swift/Controllers/StatusUtil.h>
 #include <Swiften/Disco/EntityCapsProvider.h>
+#include <Swiften/Base/foreach.h>
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+
 
 namespace Swift {
 	
@@ -29,7 +34,7 @@ namespace Swift {
  * The controller does not gain ownership of the stanzaChannel, nor the factory.
  */
 ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsProvider* entityCapsProvider)
-	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider) {
+	: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory, entityCapsProvider), eventStream_(eventStream) {
 	isInMUC_ = isInMUC;
 	lastWasPresence_ = false;
 	chatStateNotifier_ = new ChatStateNotifier(stanzaChannel, contact, entityCapsProvider);
@@ -60,6 +65,10 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
 	chatWindow_->addSystemMessage(startMessage);
 	chatWindow_->onUserTyping.connect(boost::bind(&ChatStateNotifier::setUserIsTyping, chatStateNotifier_));
 	chatWindow_->onUserCancelsTyping.connect(boost::bind(&ChatStateNotifier::userCancelledNewMessage, chatStateNotifier_));
+	chatWindow_->onFileTransferStart.connect(boost::bind(&ChatController::handleFileTransferStart, this, _1, _2));
+	chatWindow_->onFileTransferAccept.connect(boost::bind(&ChatController::handleFileTransferAccept, this, _1, _2));
+	chatWindow_->onFileTransferCancel.connect(boost::bind(&ChatController::handleFileTransferCancel, this, _1));
+	chatWindow_->onSendFileRequest.connect(boost::bind(&ChatController::handleSendFileRequest, this, _1));
 	handleBareJIDCapsChanged(toJID_);
 
 }
@@ -171,6 +180,45 @@ void ChatController::setOnline(bool online) {
 	ChatControllerBase::setOnline(online);
 }
 
+void ChatController::handleNewFileTransferController(FileTransferController* ftc) {
+	std::string nick = senderDisplayNameFromMessage(ftc->getOtherParty());
+	std::string ftID = ftc->setChatWindow(chatWindow_, nick);
+	
+	ftControllers[ftID] = ftc;
+}
+
+void ChatController::handleFileTransferCancel(std::string id) {
+	std::cout << "handleFileTransferCancel(" << id << ")" << std::endl;
+	if (ftControllers.find(id) != ftControllers.end()) {
+		ftControllers[id]->cancel();
+	} else {
+		std::cerr << "unknown file transfer UI id" << std::endl;
+	}
+}
+
+void ChatController::handleFileTransferStart(std::string id, std::string description) {
+	std::cout << "handleFileTransferStart(" << id << ", " << description << ")" << std::endl;
+	if (ftControllers.find(id) != ftControllers.end()) {
+		ftControllers[id]->start(description);
+	} else {
+		std::cerr << "unknown file transfer UI id" << std::endl;
+	}
+}
+
+void ChatController::handleFileTransferAccept(std::string id, std::string filename) {
+	std::cout << "handleFileTransferAccept(" << id << ", " << filename << ")" << std::endl;
+	if (ftControllers.find(id) != ftControllers.end()) {
+		ftControllers[id]->accept(filename);
+	} else {
+		std::cerr << "unknown file transfer UI id" << std::endl;
+	}
+}
+
+void ChatController::handleSendFileRequest(std::string filename) {
+	std::cout << "ChatController::handleSendFileRequest(" << filename << ")" << std::endl;
+	eventStream_->send(boost::make_shared<SendFileUIEvent>(getToJID(), filename));
+}
+
 std::string ChatController::senderDisplayNameFromMessage(const JID& from) {
 	return nickResolver_->jidToNick(from);
 }
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index f6b8763..2531adb 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -8,12 +8,16 @@
 
 #include "Swift/Controllers/Chat/ChatControllerBase.h"
 
+#include <map>
+#include <string>
+
 namespace Swift {
 	class AvatarManager;
 	class ChatStateNotifier;
 	class ChatStateTracker;
 	class NickResolver;
 	class EntityCapsProvider;
+	class FileTransferController;
 
 	class ChatController : public ChatControllerBase {
 		public:
@@ -21,6 +25,7 @@ namespace Swift {
 			virtual ~ChatController();
 			virtual void setToJID(const JID& jid);
 			virtual void setOnline(bool online);
+			virtual void handleNewFileTransferController(FileTransferController* ftc);
 
 		private:
 			void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
@@ -37,6 +42,11 @@ namespace Swift {
 			void handleContactNickChanged(const JID& jid, const std::string& /*oldNick*/);
 			void handleBareJIDCapsChanged(const JID& jid);
 
+			void handleFileTransferCancel(std::string /* id */);
+			void handleFileTransferStart(std::string /* id */, std::string /* description */);
+			void handleFileTransferAccept(std::string /* id */, std::string /* filename */);
+			void handleSendFileRequest(std::string filename);
+
 		private:
 			NickResolver* nickResolver_;
 			ChatStateNotifier* chatStateNotifier_;
@@ -47,6 +57,9 @@ namespace Swift {
 			std::string lastStatusChangeString_;
 			std::map<boost::shared_ptr<Stanza>, std::string> unackedStanzas_;
 			StatusShow::Type lastShownStatus_;
+			UIEventStream* eventStream_;
+
+			std::map<std::string, FileTransferController*> ftControllers;
 	};
 }
 
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index d631494..c61479c 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -29,6 +29,8 @@
 #include <Swiften/MUC/MUCManager.h>
 #include <Swiften/Elements/ChatState.h>
 #include <Swiften/MUC/MUCBookmarkManager.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
 #include <Swift/Controllers/ProfileSettingsProvider.h>
 #include <Swiften/Avatars/AvatarManager.h>
 
@@ -56,13 +58,15 @@ ChatsManager::ChatsManager(
 		EntityCapsProvider* entityCapsProvider, 
 		MUCManager* mucManager,
 		MUCSearchWindowFactory* mucSearchWindowFactory,
-		ProfileSettingsProvider* settings) :
+		ProfileSettingsProvider* settings,
+		FileTransferOverview* ftOverview) :
 			jid_(jid), 
 			joinMUCWindowFactory_(joinMUCWindowFactory), 
 			useDelayForLatency_(useDelayForLatency), 
 			mucRegistry_(mucRegistry), 
 			entityCapsProvider_(entityCapsProvider), 
-			mucManager(mucManager)  {
+			mucManager(mucManager),
+			ftOverview_(ftOverview) {
 	timerFactory_ = timerFactory;
 	eventController_ = eventController;
 	stanzaChannel_ = stanzaChannel;
@@ -86,6 +90,7 @@ ChatsManager::ChatsManager(
 	joinMUCWindow_ = NULL;
 	mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);
 	mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));
+	ftOverview_->onNewFileTransferController.connect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1));
 	setupBookmarks();
 	loadRecents();
 }
@@ -535,6 +540,12 @@ void ChatsManager::handleMUCBookmarkActivated(const MUCBookmark& mucBookmark) {
 	uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(mucBookmark.getRoom(), mucBookmark.getNick()));
 }
 
+void ChatsManager::handleNewFileTransferController(FileTransferController* ftc) {
+	ChatController* chatController = getChatControllerOrCreate(ftc->getOtherParty());
+	chatController->handleNewFileTransferController(ftc);
+	chatController->activateChatWindow();
+}
+
 void ChatsManager::handleRecentActivated(const ChatListWindow::Chat& chat) {
 	if (chat.isMUC) {
 		uiEventStream_->send(boost::make_shared<JoinMUCUIEvent>(chat.jid, chat.nick));
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index b4db523..46c104d 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -43,10 +43,12 @@ namespace Swift {
 	class MUCSearchWindowFactory;
 	class ProfileSettingsProvider;
 	class MUCSearchController;
-
+	class FileTransferOverview;
+	class FileTransferController;
+	
 	class ChatsManager {
 		public:
-			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings);
+			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings, FileTransferOverview* ftOverview);
 			virtual ~ChatsManager();
 			void setAvatarManager(AvatarManager* avatarManager);
 			void setOnline(bool enabled);
@@ -67,6 +69,7 @@ namespace Swift {
 			void handleUserLeftMUC(MUCController* mucController);
 			void handleBookmarksReady();
 			void handleChatActivity(const JID& jid, const std::string& activity, bool isMUC);
+			void handleNewFileTransferController(FileTransferController*);
 			void appendRecent(const ChatListWindow::Chat& chat);
 			void prependRecent(const ChatListWindow::Chat& chat);
 			void setupBookmarks();
@@ -110,5 +113,6 @@ namespace Swift {
 			MUCSearchController* mucSearchController_;
 			std::list<ChatListWindow::Chat> recentChats_;
 			ProfileSettingsProvider* profileSettings_;
+			FileTransferOverview* ftOverview_;
 	};
 }
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index b8cb368..5339703 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -8,6 +8,8 @@
 #include <cppunit/extensions/TestFactoryRegistry.h>
 #include "3rdParty/hippomocks.h"
 
+#include <boost/bind.hpp>
+
 #include "Swift/Controllers/Chat/ChatsManager.h"
 
 #include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"
@@ -36,11 +38,14 @@
 #include "Swiften/Client/DummyStanzaChannel.h"
 #include "Swiften/Queries/DummyIQChannel.h"
 #include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Jingle/JingleSessionManager.h"
+#include "Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
 #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include <Swift/Controllers/ProfileSettingsProvider.h>
-
+#include "Swift/Controllers/FileTransfer/FileTransferOverview.h"
+#include <Swiften/Base/Algorithm.h>
 
 using namespace Swift;
 
@@ -86,19 +91,23 @@ public:
 		settings_ = new DummySettingsProvider();
 		profileSettings_ = new ProfileSettingsProvider("a", settings_);
 		chatListWindow_ = new MockChatListWindow();
+		ftManager_ = new DummyFileTransferManager();
+		ftOverview_ = new FileTransferOverview(ftManager_);
 		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_);
-		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_);
+		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_);
 
 		avatarManager_ = new NullAvatarManager();
 		manager_->setAvatarManager(avatarManager_);
 	};
 	
 	void tearDown() {
-		//delete chatListWindowFactory_;
+		//delete chatListWindowFactory
 		delete settings_;
 		delete profileSettings_;
 		delete avatarManager_;
 		delete manager_;
+		delete ftOverview_;
+		delete ftManager_;
 		delete directedPresenceSender_;
 		delete presenceSender_;
 		delete presenceOracle_;
@@ -354,6 +363,8 @@ private:
 	DummySettingsProvider* settings_;
 	ProfileSettingsProvider* profileSettings_;
 	ChatListWindow* chatListWindow_;
+	FileTransferOverview* ftOverview_;
+	FileTransferManager* ftManager_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/FileTransfer/FileTransferController.cpp b/Swift/Controllers/FileTransfer/FileTransferController.cpp
new file mode 100644
index 0000000..afa907d
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferController.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferController.h"
+#include "Swiften/FileTransfer/OutgoingJingleFileTransfer.h"
+#include "Swiften/FileTransfer/FileTransferManager.h"
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <boost/bind.hpp>
+#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+FileTransferController::FileTransferController(const JID& receipient, const std::string& filename, FileTransferManager* fileTransferManager) :
+	sending(true), otherParty(receipient), filename(filename), ftManager(fileTransferManager), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
+	
+}
+
+FileTransferController::FileTransferController(IncomingFileTransfer::ref transfer) :
+	sending(false), otherParty(transfer->getSender()), filename(transfer->filename), transfer(transfer), ftManager(0), ftProgressInfo(0), chatWindow(0), currentState(FileTransfer::State::WaitingForStart) {
+
+}
+
+FileTransferController::~FileTransferController() {
+	delete ftProgressInfo;
+}
+
+const JID &FileTransferController::getOtherParty() const {
+	return otherParty;
+}
+
+std::string FileTransferController::setChatWindow(ChatWindow* wnd, std::string nickname) {
+	chatWindow = wnd;
+	if (sending) {
+		uiID = wnd->addFileTransfer("me", true, filename, boost::filesystem::file_size(boost::filesystem::path(filename)));
+	} else {
+		uiID = wnd->addFileTransfer(nickname, false, filename, transfer->fileSizeInBytes);
+	}
+	return uiID;
+}
+
+void FileTransferController::setReceipient(const JID& receipient) {
+	this->otherParty = receipient;
+}
+
+bool FileTransferController::isIncoming() const {
+	return !sending;
+}
+
+FileTransfer::State FileTransferController::getState() const {
+	return currentState;
+}
+
+int FileTransferController::getProgress() const {
+	return ftProgressInfo ? ftProgressInfo->getPercentage() : 0;
+}
+
+boost::uintmax_t FileTransferController::getSize() const {
+	if (transfer) {
+		return transfer->fileSizeInBytes;
+	} else {
+		return 0;
+	}
+}
+
+void FileTransferController::start(std::string& description) {
+	std::cout << "FileTransferController::start" << std::endl;
+	fileReadStream = boost::make_shared<FileReadBytestream>(boost::filesystem::path(filename));
+	OutgoingFileTransfer::ref outgoingTransfer = ftManager->createOutgoingFileTransfer(otherParty, boost::filesystem::path(filename), description, fileReadStream);
+	if (outgoingTransfer) {
+		ftProgressInfo = new FileTransferProgressInfo(outgoingTransfer->fileSizeInBytes);
+		ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
+		outgoingTransfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+		outgoingTransfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
+		outgoingTransfer->start();
+		transfer = outgoingTransfer;
+	} else {
+		std::cerr << "File transfer not supported!" << std::endl;
+	}
+}
+
+void FileTransferController::accept(std::string& file) {
+	std::cout << "FileTransferController::accept" << std::endl;
+	IncomingFileTransfer::ref incomingTransfer = boost::dynamic_pointer_cast<IncomingFileTransfer>(transfer);
+	if (incomingTransfer) {
+		fileWriteStream = boost::make_shared<FileWriteBytestream>(boost::filesystem::path(file));
+
+		ftProgressInfo = new FileTransferProgressInfo(transfer->fileSizeInBytes);
+		ftProgressInfo->onProgressPercentage.connect(boost::bind(&FileTransferController::handleProgressPercentageChange, this, _1));
+		transfer->onStateChange.connect(boost::bind(&FileTransferController::handleFileTransferStateChange, this, _1));
+		transfer->onProcessedBytes.connect(boost::bind(&FileTransferProgressInfo::setBytesProcessed, ftProgressInfo, _1));
+		incomingTransfer->accept(fileWriteStream);
+	} else {
+		std::cerr << "Expected an incoming transfer in this situation!" << std::endl;
+	}
+}
+
+void FileTransferController::cancel() {
+	if (transfer) {
+		transfer->cancel();
+	} else {
+		chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled);
+	}
+}
+
+void FileTransferController::handleFileTransferStateChange(FileTransfer::State state) {
+	currentState = state;
+	onStateChage();
+	switch(state.state) {
+		case FileTransfer::State::Negotiating:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::Negotiating);
+			return;
+		case FileTransfer::State::Transferring:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::Transferring);
+			return;
+		case FileTransfer::State::Canceled:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::Canceled);
+			return;
+		case FileTransfer::State::Finished:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::Finished);
+			if (fileWriteStream) {
+				fileWriteStream->close();
+			}
+			return;
+		case FileTransfer::State::Failed:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::FTFailed);
+			return;
+		case FileTransfer::State::WaitingForAccept:
+			chatWindow->setFileTransferStatus(uiID, ChatWindow::WaitingForAccept);
+			return;
+		case FileTransfer::State::WaitingForStart:
+			return;
+	}
+	std::cerr << "Unhandled FileTransfer::State!" << std::endl;
+}
+
+void FileTransferController::handleProgressPercentageChange(int percentage) {
+	onProgressChange();
+	chatWindow->setFileTransferProgress(uiID, percentage);
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferController.h b/Swift/Controllers/FileTransfer/FileTransferController.h
new file mode 100644
index 0000000..5d98468
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferController.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+#include <Swiften/FileTransfer/FileReadBytestream.h>
+#include <Swiften/FileTransfer/FileWriteBytestream.h>
+#include <Swift/Controllers/FileTransfer/FileTransferProgressInfo.h>
+
+namespace Swift {
+
+class FileTransferManager;
+class ChatWindow;
+
+class FileTransferController {
+public:
+	/**
+	 * For outgoing file transfers. It'll create a file transfer via FileTransferManager as soon as the descriptive information is available.
+	 */
+	FileTransferController(const JID&, const std::string&, FileTransferManager*);
+
+	/**
+	 * For incoming file transfers.
+	 */
+	FileTransferController(IncomingFileTransfer::ref transfer);
+	~FileTransferController();
+
+	std::string setChatWindow(ChatWindow*, std::string nickname);
+	void setReceipient(const JID& otherParty);
+
+	void start(std::string& description);
+	void accept(std::string& file);
+	void cancel();
+
+	const JID &getOtherParty() const;
+	bool isIncoming() const;
+	FileTransfer::State getState() const;
+	int getProgress() const;
+	boost::uintmax_t getSize() const;
+
+	boost::signal<void ()> onStateChage;
+	boost::signal<void ()> onProgressChange;
+
+private:
+	void handleFileTransferStateChange(FileTransfer::State);
+	void handleProgressPercentageChange(int percentage);
+
+private:	
+	bool sending;
+	JID otherParty;
+	std::string filename;
+	FileTransfer::ref transfer;
+	boost::shared_ptr<FileReadBytestream> fileReadStream;
+	boost::shared_ptr<FileWriteBytestream> fileWriteStream;
+	FileTransferManager* ftManager;
+	FileTransferProgressInfo* ftProgressInfo;
+	ChatWindow* chatWindow;
+	std::string uiID;
+	FileTransfer::State currentState;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferOverview.cpp b/Swift/Controllers/FileTransfer/FileTransferOverview.cpp
new file mode 100644
index 0000000..c3ffc5c
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferOverview.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferOverview.h"
+
+#include <boost/bind.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+
+FileTransferOverview::FileTransferOverview(FileTransferManager* ftm) : fileTransferManager(ftm) {
+	fileTransferManager->onIncomingFileTransfer.connect(boost::bind(&FileTransferOverview::handleIncomingFileTransfer, this, _1));
+}
+
+FileTransferOverview::~FileTransferOverview() {
+	
+}
+	
+void FileTransferOverview::sendFile(const JID& jid, const std::string& filename) {
+	FileTransferController* controller = new FileTransferController(jid, filename, fileTransferManager);
+	fileTransfers.push_back(controller);
+	
+	onNewFileTransferController(controller);
+}
+
+void FileTransferOverview::handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
+	FileTransferController* controller = new FileTransferController(transfer);
+	fileTransfers.push_back(controller);
+	onNewFileTransferController(controller);
+}
+
+const std::vector<FileTransferController*>& FileTransferOverview::getFileTransfers() const {
+	return fileTransfers;
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferOverview.h b/Swift/Controllers/FileTransfer/FileTransferOverview.h
new file mode 100644
index 0000000..716666a
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferOverview.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "Swift/Controllers/FileTransfer/FileTransferController.h"
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class ChatsManager;
+class FileTransferManager;
+
+class FileTransferOverview {
+public:
+	FileTransferOverview(FileTransferManager*);
+	~FileTransferOverview();
+	
+	void sendFile(const JID&, const std::string&);
+	const std::vector<FileTransferController*>& getFileTransfers() const;
+
+	boost::signal<void (FileTransferController*)> onNewFileTransferController;
+
+private:
+	void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer);
+
+private:
+	std::vector<FileTransferController*> fileTransfers;
+	FileTransferManager *fileTransferManager;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp
new file mode 100644
index 0000000..6d19fa1
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "FileTransferProgressInfo.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+FileTransferProgressInfo::FileTransferProgressInfo(boost::uintmax_t completeBytes) : completeBytes(completeBytes), completedBytes(0), percentage(0) {
+	onProgressPercentage(0);
+}
+
+void FileTransferProgressInfo::setBytesProcessed(int processedBytes) {
+	int oldPercentage = int(double(completedBytes) / double(completeBytes) * 100.0);
+	completedBytes += processedBytes;
+	int newPercentage = int(double(completedBytes) / double(completeBytes) * 100.0);
+	if (oldPercentage != newPercentage) {
+		onProgressPercentage(newPercentage);
+	}
+	percentage = newPercentage;
+}
+
+int FileTransferProgressInfo::getPercentage() const {
+	return percentage;
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h
new file mode 100644
index 0000000..bb3c0fc
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/FileTransferProgressInfo.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+
+namespace Swift {
+
+class FileTransferProgressInfo {
+public:
+	FileTransferProgressInfo(boost::uintmax_t completeBytes);
+
+public:
+	void setBytesProcessed(int processedBytes);
+
+	int getPercentage() const;
+	boost::signal<void (int)> onProgressPercentage;
+
+private:
+	boost::uintmax_t completeBytes;
+	boost::uintmax_t completedBytes;
+	int percentage;
+};
+
+}
diff --git a/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
new file mode 100644
index 0000000..da0606c
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamProxyFinder.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Queries/IQRouter.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxyFinder::SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter) : iqRouter(iqRouter) {
+	serviceWalker = boost::make_shared<DiscoServiceWalker>(service, iqRouter);
+	serviceWalker->onServiceFound.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleServiceFound, this, _1, _2));
+}
+
+void SOCKS5BytestreamProxyFinder::start() {
+	serviceWalker->beginWalk();
+}
+
+void SOCKS5BytestreamProxyFinder::sendBytestreamQuery(const JID& jid) {
+	S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+	boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Get, jid, proxyRequest, iqRouter);
+	request->onResponse.connect(boost::bind(&SOCKS5BytestreamProxyFinder::handleProxyResponse, this, _1, _2));
+	request->send();
+}
+
+void SOCKS5BytestreamProxyFinder::handleServiceFound(const JID& jid, boost::shared_ptr<DiscoInfo> discoInfo) {
+	if (discoInfo->hasFeature(DiscoInfo::Bytestream)) {
+		sendBytestreamQuery(jid);
+	}
+}
+
+void SOCKS5BytestreamProxyFinder::handleProxyResponse(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error) {
+	if (error) {
+		SWIFT_LOG(debug) << "ERROR" << std::endl;
+	} else {
+		if (request) {
+			onProxyFound(request);
+		} else {
+			//assert(false);
+		}
+	}
+}
+
+}
diff --git a/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h
new file mode 100644
index 0000000..1727a63
--- /dev/null
+++ b/Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swift/Controllers/DiscoServiceWalker.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class JID;
+class IQRouter;
+
+class SOCKS5BytestreamProxyFinder {
+public:
+	SOCKS5BytestreamProxyFinder(const JID& service, IQRouter *iqRouter);
+	void start();
+
+	boost::signal<void(boost::shared_ptr<S5BProxyRequest>)> onProxyFound;
+
+private:
+	void sendBytestreamQuery(const JID&);
+
+	void handleServiceFound(const JID&, boost::shared_ptr<DiscoInfo>);
+	void handleProxyResponse(boost::shared_ptr<S5BProxyRequest>, ErrorPayload::ref);
+private:
+	boost::shared_ptr<DiscoServiceWalker> serviceWalker;
+	IQRouter* iqRouter;
+	std::vector<boost::shared_ptr<GenericRequest<S5BProxyRequest> > > requests;
+};
+
+}
diff --git a/Swift/Controllers/FileTransferListController.cpp b/Swift/Controllers/FileTransferListController.cpp
new file mode 100644
index 0000000..093a3c4
--- /dev/null
+++ b/Swift/Controllers/FileTransferListController.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "Swift/Controllers/FileTransferListController.h"
+
+#include <boost/bind.hpp>
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h"
+#include "Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h"
+
+namespace Swift {
+
+FileTransferListController::FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory) : fileTransferListWidgetFactory(fileTransferListWidgetFactory), fileTransferListWidget(NULL), fileTransferOverview(0) {
+	uiEventStream->onUIEvent.connect(boost::bind(&FileTransferListController::handleUIEvent, this, _1));
+}
+
+FileTransferListController::~FileTransferListController() {
+	delete fileTransferListWidget;
+}
+
+void FileTransferListController::setFileTransferOverview(FileTransferOverview *overview) {
+	fileTransferOverview = overview;
+	if (fileTransferListWidget) {
+		fileTransferListWidget->setFileTransferOverview(fileTransferOverview);
+	}
+}
+
+void FileTransferListController::handleUIEvent(boost::shared_ptr<UIEvent> rawEvent) {
+	boost::shared_ptr<RequestFileTransferListUIEvent> event = boost::dynamic_pointer_cast<RequestFileTransferListUIEvent>(rawEvent);
+	if (event != NULL) {
+		if (fileTransferListWidget == NULL) {
+			fileTransferListWidget = fileTransferListWidgetFactory->createFileTransferListWidget();
+			if (fileTransferOverview) {
+				fileTransferListWidget->setFileTransferOverview(fileTransferOverview);
+			}
+		}
+		fileTransferListWidget->show();
+		fileTransferListWidget->activate();
+	}
+}
+
+}
diff --git a/Swift/Controllers/FileTransferListController.h b/Swift/Controllers/FileTransferListController.h
new file mode 100644
index 0000000..c5c8893
--- /dev/null
+++ b/Swift/Controllers/FileTransferListController.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swift/Controllers/UIEvents/UIEventStream.h"
+
+namespace Swift {
+
+class FileTransferListWidgetFactory;
+class FileTransferListWidget;
+class FileTransferOverview;
+
+class FileTransferListController {
+public:
+	FileTransferListController(UIEventStream* uiEventStream, FileTransferListWidgetFactory* fileTransferListWidgetFactory);
+	~FileTransferListController();
+
+	void setFileTransferOverview(FileTransferOverview* overview);
+
+private:
+	void handleUIEvent(boost::shared_ptr<UIEvent> event);
+
+private:
+	FileTransferListWidgetFactory* fileTransferListWidgetFactory;
+	FileTransferListWidget* fileTransferListWidget;
+	FileTransferOverview* fileTransferOverview;
+};
+
+}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 24c5303..b84e7d6 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -37,6 +37,7 @@
 #include "Swift/Controllers/SystemTray.h"
 #include "Swift/Controllers/SystemTrayController.h"
 #include "Swift/Controllers/XMLConsoleController.h"
+#include "Swift/Controllers/FileTransferListController.h"
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/PresenceNotifier.h"
 #include "Swift/Controllers/EventNotifier.h"
@@ -68,6 +69,9 @@
 #include <Swift/Controllers/XMPPURIController.h>
 #include "Swift/Controllers/AdHocManager.h"
 #include <SwifTools/Idle/IdleDetector.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Client/ClientXMLTracer.h>
 
 namespace Swift {
 
@@ -101,7 +105,8 @@ MainController::MainController(
 			idleDetector_(idleDetector),
 			loginWindow_(NULL) ,
 			useDelayForLatency_(useDelayForLatency),
-			eagleMode_(eagleMode) {
+			eagleMode_(eagleMode),
+			ftOverview_(NULL) {
 	storages_ = NULL;
 	certificateStorage_ = NULL;
 	statusTracker_ = NULL;
@@ -164,6 +169,8 @@ MainController::MainController(
 
 	xmlConsoleController_ = new XMLConsoleController(uiEventStream_, uiFactory_);
 
+	fileTransferListController_ = new FileTransferListController(uiEventStream_, uiFactory_);
+
 	uiEventStream_->onUIEvent.connect(boost::bind(&MainController::handleUIEvent, this, _1));
 	bool enabled = settings_->getBoolSetting(SHOW_NOTIFICATIONS, true);
 	uiEventStream_->send(boost::shared_ptr<ToggleNotificationsUIEvent>(new ToggleNotificationsUIEvent(enabled)));
@@ -184,7 +191,7 @@ MainController::~MainController() {
 	eventController_->disconnectAll();
 
 	resetClient();
-
+	delete fileTransferListController_;
 	delete xmlConsoleController_;
 	delete xmppURIController_;
 	delete soundEventController_;
@@ -211,6 +218,10 @@ void MainController::resetClient() {
 	eventWindowController_ = NULL;
 	delete chatsManager_;
 	chatsManager_ = NULL;
+	delete s5bProxyFinder_;
+	s5bProxyFinder_ = NULL;
+	delete ftOverview_;
+	ftOverview_ = NULL;
 	delete rosterController_;
 	rosterController_ = NULL;
 	delete eventNotifier_;
@@ -270,13 +281,22 @@ void MainController::handleConnected() {
 	myStatusLooksOnline_ = true;
 	if (freshLogin) {
 		profileController_ = new ProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);
-		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_);
+		srand(time(NULL));
+		int randomPort = 10000 + rand() % 10000;
+		client_->getFileTransferManager()->startListeningOnPort(randomPort);
+		s5bProxyFinder_ = new SOCKS5BytestreamProxyFinder(client_->getJID().getDomain(), client_->getIQRouter());
+		s5bProxyFinder_->onProxyFound.connect(boost::bind(&FileTransferManager::addS5BProxy, client_->getFileTransferManager(), _1));
+		s5bProxyFinder_->start();
+		ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());
+		fileTransferListController_->setFileTransferOverview(ftOverview_);
+		rosterController_ = new RosterController(jid_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_);
 		rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
 		rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
 
 		contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_);
 
-		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_);
+		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_, ftOverview_);
+		
 		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
 		chatsManager_->setAvatarManager(client_->getAvatarManager());
 
@@ -289,6 +309,10 @@ void MainController::handleConnected() {
 		discoInfo.addFeature(DiscoInfo::ChatStatesFeature);
 		discoInfo.addFeature(DiscoInfo::SecurityLabelsFeature);
 		discoInfo.addFeature(DiscoInfo::MessageCorrectionFeature);
+		discoInfo.addFeature(DiscoInfo::JingleFeature);
+		discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+		discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+		discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
 		client_->getDiscoManager()->setCapsNode(CLIENT_NODE);
 		client_->getDiscoManager()->setDiscoInfo(discoInfo);
 
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 7ac6648..2aaa542 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -23,6 +23,8 @@
 #include "Swiften/Elements/CapsInfo.h"
 #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"
 #include "Swift/Controllers/UIEvents/UIEvent.h"
+#include "Swift/Controllers/FileTransfer/SOCKS5BytestreamProxyFinder.h"
+#include "Swiften/Client/ClientXMLTracer.h"
 
 namespace Swift {
 	class IdleDetector;
@@ -51,6 +53,7 @@ namespace Swift {
 	class SoundEventController;
 	class SoundPlayer;
 	class XMLConsoleController;
+	class FileTransferListController;
 	class UIEventStream;
 	class EventWindowFactory;
 	class EventWindowController;
@@ -65,6 +68,7 @@ namespace Swift {
 	class XMPPURIController;
 	class AdHocManager;
 	class AdHocCommandWindowFactory;
+	class FileTransferOverview;
 
 	class MainController {
 		public:
@@ -140,6 +144,7 @@ namespace Swift {
 			LoginWindow* loginWindow_;
 			UIEventStream* uiEventStream_;
 			XMLConsoleController* xmlConsoleController_;
+			FileTransferListController* fileTransferListController_;
 			ChatsManager* chatsManager_;
 			ProfileController* profileController_;
 			ContactEditController* contactEditController_;
@@ -162,5 +167,7 @@ namespace Swift {
 			bool quitRequested_;
 			static const int SecondsToWaitBeforeForceQuitting;
 			bool eagleMode_;
+			FileTransferOverview* ftOverview_;
+			SOCKS5BytestreamProxyFinder* s5bProxyFinder_;
 	};
 }
diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp
index bbe81e6..6d707bb 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.cpp
+++ b/Swift/Controllers/Roster/ContactRosterItem.cpp
@@ -113,6 +113,14 @@ void ContactRosterItem::removeGroup(const std::string& group) {
 	groups_.erase(std::remove(groups_.begin(), groups_.end(), group), groups_.end());
 }
 
+void ContactRosterItem::setSupportedFeatures(const std::set<Feature>& features) {
+	features_ = features;
+}
+
+bool ContactRosterItem::supportsFeature(const Feature feat) const {
+	return features_.find(feat) != features_.end();
+}
+
 }
 
 
diff --git a/Swift/Controllers/Roster/ContactRosterItem.h b/Swift/Controllers/Roster/ContactRosterItem.h
index 7aa948c..67ffaa9 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.h
+++ b/Swift/Controllers/Roster/ContactRosterItem.h
@@ -13,6 +13,7 @@
 #include "Swiften/Elements/Presence.h"
 
 #include <map>
+#include <set>
 #include <boost/bind.hpp>
 #include "Swiften/Base/boost_bsignals.h"
 #include <boost/shared_ptr.hpp>
@@ -22,6 +23,11 @@ namespace Swift {
 class GroupRosterItem;
 class ContactRosterItem : public RosterItem {
 	public:
+		enum Feature {
+			FileTransferFeature,
+		};
+		
+	public:
 		ContactRosterItem(const JID& jid, const JID& displayJID, const std::string& name, GroupRosterItem* parent);
 		virtual ~ContactRosterItem();
 
@@ -40,6 +46,9 @@ class ContactRosterItem : public RosterItem {
 		/** Only used so a contact can know about the groups it's in*/
 		void addGroup(const std::string& group);
 		void removeGroup(const std::string& group);
+		
+		void setSupportedFeatures(const std::set<Feature>& features);
+		bool supportsFeature(Feature feat) const;
 	private:
 		JID jid_;
 		JID displayJID_;
@@ -48,6 +57,7 @@ class ContactRosterItem : public RosterItem {
 		boost::shared_ptr<Presence> offlinePresence_;
 		boost::shared_ptr<Presence> shownPresence_;
 		std::vector<std::string> groups_;
+		std::set<Feature> features_;
 };
 
 }
diff --git a/Swift/Controllers/Roster/Roster.cpp b/Swift/Controllers/Roster/Roster.cpp
index a62a18a..f3d058e 100644
--- a/Swift/Controllers/Roster/Roster.cpp
+++ b/Swift/Controllers/Roster/Roster.cpp
@@ -17,6 +17,7 @@
 #include <boost/bind.hpp>
 
 #include <iostream>
+#include <set>
 #include <deque>
 
 namespace Swift {
@@ -60,6 +61,13 @@ GroupRosterItem* Roster::getGroup(const std::string& groupName) {
 	return group;
 }
 
+void Roster::setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features) {
+	if (itemMap_[fullJIDMapping_ ? jid : jid.toBare()].empty()) return;
+	foreach(ContactRosterItem* item, itemMap_[fullJIDMapping_ ? jid : jid.toBare()]) {
+		item->setSupportedFeatures(features);
+	}
+}
+
 void Roster::removeGroup(const std::string& group) {
 	root_->removeGroupChild(group);
 }
@@ -74,7 +82,7 @@ void Roster::handleChildrenChanged(GroupRosterItem* item) {
 
 void Roster::addContact(const JID& jid, const JID& displayJID, const std::string& name, const std::string& groupName, const std::string& avatarPath) {
 	GroupRosterItem* group(getGroup(groupName));
-	ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);
+	ContactRosterItem *item = new ContactRosterItem(jid, displayJID, name, group);	
 	item->setAvatarPath(avatarPath);
 	group->addChild(item);
 	if (itemMap_[fullJIDMapping_ ? jid : jid.toBare()].size() > 0) {
diff --git a/Swift/Controllers/Roster/Roster.h b/Swift/Controllers/Roster/Roster.h
index 53161a8..2b4dd27 100644
--- a/Swift/Controllers/Roster/Roster.h
+++ b/Swift/Controllers/Roster/Roster.h
@@ -10,6 +10,7 @@
 #include "Swiften/JID/JID.h"
 #include "Swift/Controllers/Roster/RosterItemOperation.h"
 #include "Swift/Controllers/Roster/RosterFilter.h"
+#include <Swift/Controllers/Roster/ContactRosterItem.h>
 
 #include <vector>
 #include <map>
@@ -43,6 +44,8 @@ class Roster {
 		boost::signal<void (GroupRosterItem*)> onGroupAdded;
 		boost::signal<void (RosterItem*)> onDataChanged;
 		GroupRosterItem* getGroup(const std::string& groupName);
+		void setAvailableFeatures(const JID& jid, const std::set<ContactRosterItem::Feature>& features);
+
 	private:
 		void handleDataChanged(RosterItem* item);
 		void handleChildrenChanged(GroupRosterItem* item);
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp
index 5b61abf..66948c1 100644
--- a/Swift/Controllers/Roster/RosterController.cpp
+++ b/Swift/Controllers/Roster/RosterController.cpp
@@ -36,9 +36,14 @@
 #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h"
 #include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
 #include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
+#include <Swiften/FileTransfer/FileTransferManager.h>
 #include <Swiften/Client/NickManager.h>
 #include <Swift/Controllers/Intl.h>
 #include <Swiften/Base/format.h>
+#include <Swiften/Elements/DiscoInfo.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
 
 namespace Swift {
 
@@ -47,8 +52,9 @@ static const std::string SHOW_OFFLINE = "showOffline";
 /**
  * The controller does not gain ownership of these parameters.
  */
-RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings)
- : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream) {
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview)
+ : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview) {
+	assert(fileTransferOverview);
 	iqRouter_ = iqRouter;
 	presenceOracle_ = presenceOracle;
 	subscriptionManager_ = subscriptionManager;
@@ -74,15 +80,17 @@ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, Avata
 	nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
 	mainWindow_->setMyJID(jid);
 	mainWindow_->setMyNick(nickManager_->getOwnNick());
+	
+	entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1));
 
 	if (settings->getBoolSetting(SHOW_OFFLINE, false)) {
 		uiEventStream->onUIEvent(boost::shared_ptr<UIEvent>(new ToggleShowOfflineUIEvent(true)));
 	}
 }
 
-RosterController::~RosterController() {
+RosterController::~RosterController() {	
 	nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1));
-
+	
 	delete offlineFilter_;
 	delete expandiness_;
 
@@ -91,6 +99,7 @@ RosterController::~RosterController() {
 		delete mainWindow_;
 	}
 	delete roster_;
+	
 }
 
 void RosterController::setEnabled(bool enabled) {
@@ -226,6 +235,10 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
 			}
 		}
 	}
+	else if (boost::shared_ptr<SendFileUIEvent> sendFileEvent = boost::dynamic_pointer_cast<SendFileUIEvent>(event)) {
+		//TODO add send file dialog to ChatView of receipient jid
+		ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename());
+	}
 }
 
 void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) {
@@ -302,4 +315,15 @@ std::set<std::string> RosterController::getGroups() const {
 	return xmppRoster_->getGroups();
 }
 
+void RosterController::handleOnCapsChanged(const JID& jid) {
+	DiscoInfo::ref info = entityCapsManager_->getCaps(jid);
+	if (info) {
+		std::set<ContactRosterItem::Feature> features;
+		if (info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+			features.insert(ContactRosterItem::FileTransferFeature);
+		}
+		roster_->setAvailableFeatures(jid, features);
+	}
+}
+
 }
diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h
index 0a2b818..66748ca 100644
--- a/Swift/Controllers/Roster/RosterController.h
+++ b/Swift/Controllers/Roster/RosterController.h
@@ -8,12 +8,14 @@
 
 #include "Swiften/JID/JID.h"
 #include <string>
+#include <set>
 #include "Swiften/Elements/Presence.h"
 #include "Swiften/Elements/ErrorPayload.h"
 #include "Swiften/Elements/RosterPayload.h"
 #include "Swiften/Avatars/AvatarManager.h"
 #include "Swift/Controllers/UIEvents/UIEvent.h"
 #include "RosterGroupExpandinessPersister.h"
+#include "Swift/Controllers/FileTransfer/FileTransferOverview.h"
 
 #include "Swiften/Base/boost_bsignals.h"
 #include <boost/shared_ptr.hpp>
@@ -35,10 +37,12 @@ namespace Swift {
 	class IQRouter;
 	class SettingsProvider;
 	class NickManager;
-
+	class EntityCapsProvider;
+	class FileTransferManager;
+	
 	class RosterController {
 		public:
-			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings);
+			RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_, SettingsProvider* settings, EntityCapsProvider* entityCapsProvider, FileTransferOverview* fileTransferOverview);
 			~RosterController();
 			void showRosterWindow();
 			MainWindow* getWindow() {return mainWindow_;};
@@ -69,6 +73,7 @@ namespace Swift {
 			void handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload);
 			void applyAllPresenceTo(const JID& jid);
 			void handleEditProfileRequest();
+			void handleOnCapsChanged(const JID& jid);
 
 			JID myJID_;
 			XMPPRoster* xmppRoster_;
@@ -86,6 +91,9 @@ namespace Swift {
 			IQRouter* iqRouter_;
 			SettingsProvider* settings_;
 			UIEventStream* uiEventStream_;
+			EntityCapsProvider* entityCapsManager_;
+			FileTransferOverview* ftOverview_;
+			
 			boost::bsignals::scoped_connection changeStatusConnection_;
 			boost::bsignals::scoped_connection signOutConnection_;
 			boost::bsignals::scoped_connection uiEventConnection_;
diff --git a/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp b/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
index 3acab12..963c5cd 100644
--- a/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/LeastCommonSubsequenceTest.cpp
@@ -4,12 +4,12 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
-#include <cppunit/extensions/HelperMacros.h>
-#include <cppunit/extensions/TestFactoryRegistry.h>
 #include <boost/assign/list_of.hpp>
 #include <functional>
 
 #include <QA/Checker/IO.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
 #include <Swift/Controllers/Roster/LeastCommonSubsequence.h>
 
 using namespace Swift;
diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
index ca74dbb..fbee894 100644
--- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp
@@ -31,11 +31,21 @@
 #include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h"
 #include "Swiften/MUC/MUCRegistry.h"
 #include <Swiften/Client/DummyNickManager.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/Disco/CapsProvider.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
 
 using namespace Swift;
 
 #define CHILDREN mainWindow_->roster->getRoot()->getChildren()
 
+class DummyCapsProvider : public CapsProvider {
+		DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());}
+};
+
 class RosterControllerTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(RosterControllerTest);
 		CPPUNIT_TEST(testAdd);
@@ -66,12 +76,21 @@ class RosterControllerTest : public CppUnit::TestFixture {
 			uiEventStream_ = new UIEventStream();
 			settings_ = new DummySettingsProvider();
 			nickManager_ = new DummyNickManager();
-			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_);
+			capsProvider_ = new DummyCapsProvider();
+			entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
+			jingleSessionManager_ = new JingleSessionManager(router_);
+
+			ftManager_ = new DummyFileTransferManager();
+			ftOverview_ = new FileTransferOverview(ftManager_);
+			rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_);
 			mainWindow_ = mainWindowFactory_->last;
 		};
 
 		void tearDown() {
 			delete rosterController_;
+			delete ftManager_;
+			delete jingleSessionManager_;
+			
 			delete nickManager_;
 			delete nickResolver_;
 			delete mucRegistry_;
@@ -313,6 +332,11 @@ class RosterControllerTest : public CppUnit::TestFixture {
 		UIEventStream* uiEventStream_;
 		MockMainWindow* mainWindow_;
 		DummySettingsProvider* settings_;
+		DummyCapsProvider* capsProvider_;
+		EntityCapsManager* entityCapsManager_;
+		JingleSessionManager* jingleSessionManager_;
+		FileTransferManager* ftManager_;
+		FileTransferOverview* ftOverview_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest);
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index 031e93a..289f055 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -31,6 +31,10 @@ if env["SCONS_STAGE"] == "build" :
 			"MainController.cpp",
 			"ProfileController.cpp",
 			"ContactEditController.cpp",
+			"FileTransfer/FileTransferController.cpp",
+			"FileTransfer/FileTransferOverview.cpp",
+			"FileTransfer/FileTransferProgressInfo.cpp",
+			"FileTransfer/SOCKS5BytestreamProxyFinder.cpp",
 			"Roster/RosterController.cpp",
 			"Roster/RosterGroupExpandinessPersister.cpp",
 			"Roster/ContactRosterItem.cpp",
@@ -42,6 +46,7 @@ if env["SCONS_STAGE"] == "build" :
 			"SoundEventController.cpp",
 			"SystemTrayController.cpp",
 			"XMLConsoleController.cpp",
+			"FileTransferListController.cpp",
 			"StatusTracker.cpp",
 			"PresenceNotifier.cpp",
 			"EventNotifier.cpp",
diff --git a/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h b/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h
new file mode 100644
index 0000000..aff6909
--- /dev/null
+++ b/Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+
+class RequestFileTransferListUIEvent : public UIEvent {
+};
+
+}
diff --git a/Swift/Controllers/UIEvents/SendFileUIEvent.h b/Swift/Controllers/UIEvents/SendFileUIEvent.h
new file mode 100644
index 0000000..3bfa69d
--- /dev/null
+++ b/Swift/Controllers/UIEvents/SendFileUIEvent.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swiften/JID/JID.h>
+#include <Swift/Controllers/UIEvents/UIEvent.h>
+
+namespace Swift {
+	class SendFileUIEvent : public UIEvent {
+		public:
+			typedef boost::shared_ptr<SendFileUIEvent> ref;
+
+			SendFileUIEvent(const JID& jid, const std::string& filename) : jid(jid), filename(filename) {
+			}
+
+			const JID& getJID() const {
+				return jid;
+			}
+			
+			const std::string& getFilename() const {
+				return filename;
+			}
+
+		private:
+			JID jid;
+			std::string filename;
+	};
+}
diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h
index faef5c8..b90efd9 100644
--- a/Swift/Controllers/UIInterfaces/ChatWindow.h
+++ b/Swift/Controllers/UIInterfaces/ChatWindow.h
@@ -24,12 +24,14 @@ namespace Swift {
 	class TabComplete;
 	class RosterItem;
 	class ContactRosterItem;
+	class FileTransferController;
 
 	class ChatWindow {
 		public:
 			enum AckState {Pending, Received, Failed};
 			enum Tristate {Yes, No, Maybe};
 			enum OccupantAction {Kick};
+			enum FileTransferState {WaitingForAccept, Negotiating, Transferring, Canceled, Finished, FTFailed};
 			ChatWindow() {}
 			virtual ~ChatWindow() {};
 
@@ -45,6 +47,11 @@ namespace Swift {
 			virtual void addPresenceMessage(const std::string& message) = 0;
 			virtual void addErrorMessage(const std::string& message) = 0;
 			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;
+			
+			// File transfer related stuff
+			virtual std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) = 0;
+			virtual void setFileTransferProgress(std::string, const int percentageDone) = 0;
+			virtual void setFileTransferStatus(std::string, const FileTransferState state, const std::string& msg = "") = 0;
 
 			virtual void setContactChatState(ChatState::ChatStateType state) = 0;
 			virtual void setName(const std::string& name) = 0;
@@ -88,6 +95,12 @@ namespace Swift {
 			boost::signal<void ()> onAlertButtonClicked;
 			boost::signal<void (ContactRosterItem*)> onOccupantSelectionChanged;
 			boost::signal<void (ChatWindow::OccupantAction, ContactRosterItem*)> onOccupantActionSelected;
+			
+			// File transfer related
+			boost::signal<void (std::string /* id */)> onFileTransferCancel;
+			boost::signal<void (std::string /* id */, std::string /* description */)> onFileTransferStart;
+			boost::signal<void (std::string /* id */, std::string /* path */)> onFileTransferAccept;
+			boost::signal<void (std::string /* path */)> onSendFileRequest;
 	};
 }
 #endif
diff --git a/Swift/Controllers/UIInterfaces/FileTransferListWidget.h b/Swift/Controllers/UIInterfaces/FileTransferListWidget.h
new file mode 100644
index 0000000..01dcfd3
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/FileTransferListWidget.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+namespace Swift {
+
+class FileTransferOverview;
+
+class FileTransferListWidget {
+public:
+	virtual ~FileTransferListWidget() {}
+
+	virtual void show() = 0;
+	virtual void activate() = 0;
+
+	virtual void setFileTransferOverview(FileTransferOverview*) = 0;
+};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h b/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h
new file mode 100644
index 0000000..0b08fb3
--- /dev/null
+++ b/Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h"
+
+namespace Swift {
+
+class FileTransferListWidgetFactory {
+public:
+	virtual ~FileTransferListWidgetFactory() {}
+
+	virtual FileTransferListWidget* createFileTransferListWidget() = 0;
+};
+
+}
diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h
index 57f55d0..cf89dab 100644
--- a/Swift/Controllers/UIInterfaces/UIFactory.h
+++ b/Swift/Controllers/UIInterfaces/UIFactory.h
@@ -18,6 +18,7 @@
 #include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h>
 #include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>
+#include <Swift/Controllers/UIInterfaces/FileTransferListWidgetFactory.h>
 
 namespace Swift {
 	class UIFactory : 
@@ -27,12 +28,13 @@ namespace Swift {
 			public LoginWindowFactory, 
 			public MainWindowFactory, 
 			public MUCSearchWindowFactory, 
-			public XMLConsoleWidgetFactory, 
+			public XMLConsoleWidgetFactory,
 			public UserSearchWindowFactory, 
 			public JoinMUCWindowFactory,
 			public ProfileWindowFactory,
 			public ContactEditWindowFactory,
-			public AdHocCommandWindowFactory {
+			public AdHocCommandWindowFactory,
+			public FileTransferListWidgetFactory {
 		public:
 			virtual ~UIFactory() {}
 	};
diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h
index 574248f..b410c69 100644
--- a/Swift/Controllers/UnitTest/MockChatWindow.h
+++ b/Swift/Controllers/UnitTest/MockChatWindow.h
@@ -20,6 +20,11 @@ namespace Swift {
 			virtual void addErrorMessage(const std::string& /*message*/) {};
 			virtual void addPresenceMessage(const std::string& /*message*/) {};
 
+			// File transfer related stuff
+			virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/) { return 0; };
+			virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { };
+			virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { };
+			
 			virtual void setContactChatState(ChatState::ChatStateType /*state*/) {};
 			virtual void setName(const std::string& name) {name_ = name;};
 			virtual void show() {};
@@ -40,7 +45,7 @@ namespace Swift {
 			virtual void setAlert(const std::string& /*alertText*/, const std::string& /*buttonText*/) {};
 			virtual void cancelAlert() {};
 			virtual void setCorrectionEnabled(Tristate /*enabled*/) {}
-			void setAvailableOccupantActions(const std::vector<OccupantAction>& actions) {}
+			void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {}
 
 			boost::signal<void ()> onClosed;
 			boost::signal<void ()> onAllMessagesRead;
diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp
index 4c6a729..d51f74c 100644
--- a/Swift/QtUI/QtChatView.cpp
+++ b/Swift/QtUI/QtChatView.cpp
@@ -18,6 +18,8 @@
 #include <QMessageBox>
 #include <QApplication>
 
+#include <Swiften/Base/Log.h>
+
 #include "QtWebView.h"
 #include "QtChatTheme.h"
 
@@ -47,9 +49,11 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent) : QWidget(parent), f
 #else
 	mainLayout->addWidget(webView_);
 #endif
+	setAcceptDrops(true);
 
 	webPage_ = new QWebPage(this);
 	webPage_->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+	webPage_->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
 	webView_->setPage(webPage_);
 	connect(webPage_, SIGNAL(selectionChanged()), SLOT(copySelectionToClipboard()));
 
@@ -151,6 +155,10 @@ QString QtChatView::getLastSentMessage() {
 	return lastElement_.toPlainText();
 }
 
+void QtChatView::addToJSEnvironment(const QString& name, QObject* obj) {
+	webView_->page()->currentFrame()->addToJavaScriptWindowObject(name, obj);
+}
+
 void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) {
 	rememberScrolledToBottom();
 	QWebElement message = document_.findFirst("#" + id);
@@ -263,4 +271,69 @@ void QtChatView::resetView() {
 	connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection);
 }
 
+QWebElement findDivElementWithID(QWebElement document, QString id) {
+	QWebElementCollection divs = document.findAll("div");
+	foreach(QWebElement div, divs) {
+		if (div.attribute("id") == id) {
+			return div;
+		}
+	}
+	return QWebElement();
+}
+
+void QtChatView::setFileTransferProgress(QString id, const int percentageDone) {
+	QWebElement ftElement = findDivElementWithID(document_, id);
+	if (ftElement.isNull()) {
+		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id!" << std::endl;
+		return;
+	}
+	QWebElement progressBar = ftElement.findFirst("div.progressbar");
+	progressBar.setStyleProperty("width", QString::number(percentageDone) + "%");
+
+	QWebElement progressBarValue = ftElement.findFirst("div.progressbar-value");
+	progressBarValue.setInnerXml(QString::number(percentageDone) + " %");
+}
+
+void QtChatView::setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& /* msg */) {
+	QWebElement ftElement = findDivElementWithID(document_, id);
+	if (ftElement.isNull()) {
+		SWIFT_LOG(debug) << "Tried to access FT UI via invalid id! id = " << id.toStdString() << std::endl;
+		return;
+	}
+
+	QString newInnerHTML = "";
+	if (state == ChatWindow::WaitingForAccept) {
+		newInnerHTML =	"Waiting for other side to accept the transfer.<br/>"
+						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+	}
+	if (state == ChatWindow::Negotiating) {
+		// replace with text "Negotiaging" + Cancel button
+		newInnerHTML =	"Negotiating...<br/>"
+						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+	}
+	else if (state == ChatWindow::Transferring) {
+		// progress bar + Cancel Button
+		newInnerHTML =	"<div style=\"position: relative; width: 90%; height: 20px; border: 2px solid grey; -webkit-border-radius: 10px;\">"
+							"<div class=\"progressbar\" style=\"width: 0%; height: 100%; background: #AAA; -webkit-border-radius: 6px;\">"
+								"<div class=\"progressbar-value\" style=\"position: absolute; top: 0px; left: 0px; width: 100%; text-align: center; padding-top: 2px;\">"
+									"0%"
+								"</div>"
+							"</div>"
+						"</div>"
+						"<input id=\"discard\" type=\"submit\" value=\"Cancel\" onclick=\"filetransfer.cancel(\'" + id + "\');\">";
+	}
+	else if (state == ChatWindow::Canceled) {
+		newInnerHTML = "Transfer has been canceled!";
+	}
+	else if (state == ChatWindow::Finished) {
+		// text "Successful transfer"
+		newInnerHTML = "Transfer completed successfully.";
+	}
+	else if (state == ChatWindow::FTFailed) {
+		newInnerHTML = "Transfer failed.";
+	}
+
+	ftElement.setInnerXml(newInnerHTML);
+}
+
 }
diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h
index eda7e42..cf47029 100644
--- a/Swift/QtUI/QtChatView.h
+++ b/Swift/QtUI/QtChatView.h
@@ -16,6 +16,8 @@
 
 #include "ChatSnippet.h"
 
+#include <Swift/Controllers/UIInterfaces/ChatWindow.h>
+
 class QWebPage;
 class QUrl;
 
@@ -34,6 +36,9 @@ namespace Swift {
 			void rememberScrolledToBottom();
 			void setAckXML(const QString& id, const QString& xml);
 			QString getLastSentMessage();
+			void addToJSEnvironment(const QString&, QObject*);
+			void setFileTransferProgress(QString id, const int percentageDone);
+			void setFileTransferStatus(QString id, const ChatWindow::FileTransferState state, const QString& msg);
 
 		signals:
 			void gotFocus();
diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp
index a36bc32..238100a 100644
--- a/Swift/QtUI/QtChatWindow.cpp
+++ b/Swift/QtUI/QtChatWindow.cpp
@@ -19,8 +19,15 @@
 #include "QtScaledAvatarCache.h"
 
 #include "SwifTools/TabComplete.h"
+#include <Swift/Controllers/UIEvents/UIEventStream.h>
+#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>
+#include "QtFileTransferJSBridge.h"
+
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
 
 #include <QLabel>
+#include <QInputDialog>
 #include <QApplication>
 #include <QBoxLayout>
 #include <QCloseEvent>
@@ -33,9 +40,12 @@
 #include <QTime>
 #include <QUrl>
 #include <QPushButton>
+#include <QFileDialog>
+
+#include <QDebug>
 
 namespace Swift {
-QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), eventStream_(eventStream) {
+QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageWasSystem_(false), previousMessageWasPresence_(false), previousMessageWasFileTransfer_(false), eventStream_(eventStream) {
 	unreadCount_ = 0;
 	inputEnabled_ = true;
 	completer_ = NULL;
@@ -45,6 +55,8 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 	updateTitleWithUnreadCount();
 	QtSettingsProvider settings;
 
+	setAcceptDrops(true);
+
 	alertStyleSheet_ = "background: rgb(255, 255, 153); color: black";
 
 	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);
@@ -117,15 +129,23 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt
 	connect(messageLog_, SIGNAL(gotFocus()), input_, SLOT(setFocus()));
 	resize(400,300);
 	connect(messageLog_, SIGNAL(fontResized(int)), this, SIGNAL(fontResized(int)));
+
 	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtChatWindow::handleOccupantSelectionChanged, this, _1));
 	treeWidget_->onOccupantActionSelected.connect(boost::bind(boost::ref(onOccupantActionSelected), _1, _2));
 
+	fileTransferJS = new QtFileTransferJSBridge();
+	messageLog_->addToJSEnvironment("filetransfer", fileTransferJS);
+	connect(fileTransferJS, SIGNAL(setDescription(QString)), this, SLOT(handleFileTransferSetDescription(QString)));
+	connect(fileTransferJS, SIGNAL(sendRequest(QString)), this, SLOT(handleFileTransferStart(QString)));
+	connect(fileTransferJS, SIGNAL(acceptRequest(QString, QString)), this, SLOT(handleFileTransferAccept(QString, QString)));
+	connect(fileTransferJS, SIGNAL(cancel(QString)), this, SLOT(handleFileTransferCancel(QString)));
 }
 
 QtChatWindow::~QtChatWindow() {
-
+	delete fileTransferJS;
 }
 
+
 void QtChatWindow::handleOccupantSelectionChanged(RosterItem* item) {
 	onOccupantSelectionChanged(dynamic_cast<ContactRosterItem*>(item));
 }
@@ -306,6 +326,7 @@ void QtChatWindow::closeEvent(QCloseEvent* event) {
 }
 
 void QtChatWindow::convertToMUC() {
+	setAcceptDrops(false);
 	treeWidget_->show();
 }
 
@@ -391,7 +412,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
 	QString styleSpanEnd = style == "" ? "" : "</span>";
 	htmlString += styleSpanStart + messageHTML + styleSpanEnd;
 
-	bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+	bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
 	if (lastLineTracker_.getShouldMoveLastLine()) {
 		/* should this be queued? */
 		messageLog_->addLastSeenLine();
@@ -406,6 +427,7 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri
 	previousSenderName_ = P2QSTRING(senderName);
 	previousMessageWasSystem_ = false;
 	previousMessageWasPresence_ = false;
+	previousMessageWasFileTransfer_ = false;
 	return id;
 }
 
@@ -431,6 +453,94 @@ std::string QtChatWindow::addAction(const std::string &message, const std::strin
 	return addMessage(" *" + message + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time);
 }
 
+std::string formatSize(const uintmax_t bytes) {
+	static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL};
+	int power = 0;
+	double engBytes = bytes;
+	while (engBytes >= 1000) {
+		++power;
+		engBytes = engBytes / 1000.0;
+	}
+	return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") );
+}
+
+std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes) {
+	qDebug() << "addFileTransfer";
+	std::string ft_id = id_.generateID();
+	
+	std::string htmlString;
+	if (senderIsSelf) {
+		// outgoing
+		htmlString = "Send file: " + filename + " ( " + formatSize(sizeInBytes) + ") </br>" +
+			"<div id='" + ft_id + "'>" +
+			"<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
+			"<input id='description' type='submit' value='Set Description' onclick='filetransfer.setDescription(\"" + ft_id + "\");' />" +
+			"<input id='send' type='submit' value='Send' onclick='filetransfer.sendRequest(\"" + ft_id + "\");' />" +
+			"</div>";
+	} else {
+		// incoming
+		htmlString = "Receiving file: " + filename + " ( " + formatSize(sizeInBytes)  + ") </br>" +
+			"<div id='" + ft_id + "'>" +
+			"<input id='discard' type='submit' value='Cancel' onclick='filetransfer.cancel(\"" + ft_id + "\");' />" +
+			"<input id='accept' type='submit' value='Accept' onclick='filetransfer.acceptRequest(\"" + ft_id + "\", \"" + filename + "\");' />" +
+			"</div>";
+	}
+
+	//addMessage(message, senderName, senderIsSelf, boost::shared_ptr<SecurityLabel>(), "", boost::posix_time::second_clock::local_time());
+
+	bool appendToPrevious = !previousMessageWasFileTransfer_ && !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName)));
+	if (lastLineTracker_.getShouldMoveLastLine()) {
+		/* should this be queued? */
+		messageLog_->addLastSeenLine();
+		/* if the line is added we should break the snippet */
+		appendToPrevious = false;
+	}
+	QString qAvatarPath = "qrc:/icons/avatar.png";
+	std::string id = id_.generateID();
+	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(QString::fromStdString(htmlString), Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id))));
+
+
+	return ft_id;
+}
+
+void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) {
+	messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone);
+}
+
+void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) {
+	messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg));
+}
+
+void QtChatWindow::handleFileTransferCancel(QString id) {
+	qDebug() << "QtChatWindow::handleFileTransferCancel(" << id << ")";
+	onFileTransferCancel(id.toStdString());
+}
+
+void QtChatWindow::handleFileTransferSetDescription(QString id) {
+	bool ok = false;
+	QString text = QInputDialog::getText(this, tr("File transfer description"),
+		tr("Description:"), QLineEdit::Normal, "", &ok);
+	if (ok) {
+		descriptions[id] = text;
+	}
+}
+
+void QtChatWindow::handleFileTransferStart(QString id) {
+	qDebug() << "QtChatWindow::handleFileTransferStart(" << id << ")";
+	
+	QString text = descriptions.find(id) == descriptions.end() ? QString() : descriptions[id];
+	onFileTransferStart(id.toStdString(), text.toStdString());
+}
+
+void QtChatWindow::handleFileTransferAccept(QString id, QString filename) {
+	qDebug() << "QtChatWindow::handleFileTransferAccept(" << id << ", " << filename << ")";
+
+	QString path = QFileDialog::getSaveFileName(this, tr("Save File"), filename);
+	if (!path.isEmpty()) {
+		onFileTransferAccept(id.toStdString(), path.toStdString());
+	}
+}
+
 void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
 	if (isWidgetSelected()) {
 		onAllMessagesRead();
@@ -443,6 +553,7 @@ void QtChatWindow::addErrorMessage(const std::string& errorMessage) {
 	previousMessageWasSelf_ = false;
 	previousMessageWasSystem_ = true;
 	previousMessageWasPresence_ = false;
+	previousMessageWasFileTransfer_ = false;
 }
 
 void QtChatWindow::addSystemMessage(const std::string& message) {
@@ -458,6 +569,7 @@ void QtChatWindow::addSystemMessage(const std::string& message) {
 	previousMessageWasSelf_ = false;
 	previousMessageWasSystem_ = true;
 	previousMessageWasPresence_ = false;
+	previousMessageWasFileTransfer_ = false;
 }
 
 void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) {
@@ -532,6 +644,21 @@ void QtChatWindow::moveEvent(QMoveEvent*) {
 	emit geometryChanged();
 }
 
+void QtChatWindow::dragEnterEvent(QDragEnterEvent *event) {
+	if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
+		// TODO: check whether contact actually supports file transfer
+		event->acceptProposedAction();
+	}
+}
+
+void QtChatWindow::dropEvent(QDropEvent *event) {
+	if (event->mimeData()->urls().size() == 1) {
+		onSendFileRequest(event->mimeData()->urls().at(0).toLocalFile().toStdString());
+	} else {
+		addSystemMessage("Sending of multiple files at once isn't supported at this time.");
+	}
+}
+
 void QtChatWindow::replaceLastMessage(const std::string& message) {
 	messageLog_->replaceLastMessage(P2QSTRING(Linkify::linkify(message)));
 }
diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h
index d38e9c6..f0c078c 100644
--- a/Swift/QtUI/QtChatWindow.h
+++ b/Swift/QtUI/QtChatWindow.h
@@ -14,6 +14,8 @@
 
 #include "Swiften/Base/IDGenerator.h"
 
+#include <map>
+
 class QTextEdit;
 class QLineEdit;
 class QComboBox;
@@ -28,6 +30,7 @@ namespace Swift {
 	class TreeWidget;
 	class QtTextEdit;
 	class UIEventStream;
+	class QtFileTransferJSBridge;
 	class QtChatWindow : public QtTabbable, public ChatWindow {
 		Q_OBJECT
 		public:
@@ -39,6 +42,11 @@ namespace Swift {
 			void addPresenceMessage(const std::string& message);
 			void addErrorMessage(const std::string& errorMessage);
 			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);
+			// File transfer related stuff
+			std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes);
+			void setFileTransferProgress(std::string id, const int percentageDone);
+			void setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg);
+			
 			void show();
 			void activate();
 			void setUnreadMessageCount(int count);
@@ -79,6 +87,9 @@ namespace Swift {
 			void resizeEvent(QResizeEvent* event);
 			void moveEvent(QMoveEvent* event);
 
+			void dragEnterEvent(QDragEnterEvent *event);
+			void dropEvent(QDropEvent *event);
+
 		protected:
 			void showEvent(QShowEvent* event);
 
@@ -88,6 +99,13 @@ namespace Swift {
 			void handleKeyPressEvent(QKeyEvent* event);
 			void handleSplitterMoved(int pos, int index);
 			void handleAlertButtonClicked();
+
+			
+			void handleFileTransferCancel(QString id);
+			void handleFileTransferSetDescription(QString id);
+			void handleFileTransferStart(QString id);
+			void handleFileTransferAccept(QString id, QString filename);
+
 		private:
 			void updateTitleWithUnreadCount();
 			void tabComplete();
@@ -116,6 +134,7 @@ namespace Swift {
 			bool previousMessageWasSelf_;
 			bool previousMessageWasSystem_;
 			bool previousMessageWasPresence_;
+			bool previousMessageWasFileTransfer_;
 			QString previousSenderName_;
 			bool inputClearing_;
 			UIEventStream* eventStream_;
@@ -124,5 +143,8 @@ namespace Swift {
 			QSplitter *logRosterSplitter_;
 			Tristate correctionEnabled_;
 			QString alertStyleSheet_;
+			
+			std::map<QString, QString> descriptions;
+			QtFileTransferJSBridge* fileTransferJS;
 	};
 }
diff --git a/Swift/QtUI/QtFileTransferJSBridge.cpp b/Swift/QtUI/QtFileTransferJSBridge.cpp
new file mode 100644
index 0000000..76c1509
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferJSBridge.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferJSBridge.h"
+
+namespace Swift {
+
+QtFileTransferJSBridge::QtFileTransferJSBridge() {
+	
+}
+
+QtFileTransferJSBridge::~QtFileTransferJSBridge() {
+	
+}
+
+}
\ No newline at end of file
diff --git a/Swift/QtUI/QtFileTransferJSBridge.h b/Swift/QtUI/QtFileTransferJSBridge.h
new file mode 100644
index 0000000..bd884e5
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferJSBridge.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QObject>
+
+#include <map>
+
+namespace Swift {
+
+class FileTransferController;
+
+class QtFileTransferJSBridge : public QObject {
+	 Q_OBJECT
+public:
+	QtFileTransferJSBridge();
+	virtual ~QtFileTransferJSBridge();
+signals:
+	void discard(QString id);
+	void sendRequest(QString id);
+	void setDescription(QString id);
+	void acceptRequest(QString id, QString filename);
+	void cancel(QString id);
+};
+
+}
diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp
new file mode 100644
index 0000000..1b8ec51
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListItemModel.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferListItemModel.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swift/Controllers/FileTransfer/FileTransferController.h>
+#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>
+
+namespace Swift {
+
+extern std::string formatSize(const uintmax_t bytes);
+
+QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) {
+}
+
+void QtFileTransferListItemModel::setFileTransferOverview(FileTransferOverview *overview) {
+	fileTransferOverview = overview;
+	fileTransferOverview->onNewFileTransferController.connect(boost::bind(&QtFileTransferListItemModel::handleNewFileTransferController, this, _1));
+}
+
+void QtFileTransferListItemModel::handleNewFileTransferController(FileTransferController* newController) {
+	emit layoutAboutToBeChanged();
+	emit layoutChanged();
+	dataChanged(createIndex(0,0), createIndex(fileTransferOverview->getFileTransfers().size(),4));
+	newController->onStateChage.connect(boost::bind(&QtFileTransferListItemModel::handleStateChange, this, fileTransferOverview->getFileTransfers().size() - 1));
+	newController->onProgressChange.connect(boost::bind(&QtFileTransferListItemModel::handleProgressChange, this, fileTransferOverview->getFileTransfers().size() - 1));
+}
+
+void QtFileTransferListItemModel::handleStateChange(int index) {
+	emit dataChanged(createIndex(index, 2), createIndex(index, 2));
+}
+
+void QtFileTransferListItemModel::handleProgressChange(int index) {
+	emit dataChanged(createIndex(index, 3), createIndex(index, 3));
+}
+
+QVariant QtFileTransferListItemModel::headerData(int section, Qt::Orientation /* orientation */, int role) const {
+	if (role != Qt::DisplayRole) return QVariant();
+	if (section == Direction) return QVariant("Direction");
+	if (section == OtherParty) return QVariant("Other Party");
+	if (section == State) return QVariant("State");
+	if (section == Progress) return QVariant("Progress");
+	if (section == OverallSize) return QVariant("Size");
+	return QVariant();
+}
+
+int QtFileTransferListItemModel::columnCount(const QModelIndex& /* parent */) const {
+	return NoOfColumns;
+}
+
+QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) const {
+	if (role != Qt::DisplayRole || !index.isValid() ||
+		!fileTransferOverview || static_cast<size_t>(index.row()) >= fileTransferOverview->getFileTransfers().size()) {
+		return QVariant();
+	}
+	FileTransferController* controller = fileTransferOverview->getFileTransfers().at(index.row());
+	if (index.column() == Direction) {
+		return controller->isIncoming() ? QVariant("Incoming") : QVariant("Outgoing");
+	}
+	if (index.column() == OtherParty) {
+		return QVariant(QString::fromStdString(controller->getOtherParty().toString()));
+	}
+	if (index.column() == State) {
+		FileTransfer::State state = controller->getState();
+		switch(state.state) {
+			case FileTransfer::State::WaitingForStart:
+				return QVariant("Waiting for start");
+			case FileTransfer::State::WaitingForAccept:
+				return QVariant("Waiting for other side to accept");
+			case FileTransfer::State::Negotiating:
+				return QVariant("Negotiating");
+			case FileTransfer::State::Transferring:
+				return QVariant("Transferring");
+			case FileTransfer::State::Finished:
+				return QVariant("Finished");
+			case FileTransfer::State::Failed:
+				return QVariant("Failed");
+			case FileTransfer::State::Canceled:
+				return QVariant("Canceled");
+		}
+	}
+
+	if (index.column() == Progress) {
+		return QVariant(QString::number(controller->getProgress()));
+	}
+	if (index.column() == OverallSize) {
+		return QVariant(QString::fromStdString(formatSize((controller->getSize()))));
+	}
+	return QVariant();
+}
+
+QModelIndex QtFileTransferListItemModel::parent(const QModelIndex& /* child */) const {
+	return createIndex(0,0);
+}
+
+int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const {
+	return fileTransferOverview ? fileTransferOverview->getFileTransfers().size() : 0;
+}
+
+QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const {
+	return createIndex(row, column, 0);
+}
+
+}
diff --git a/Swift/QtUI/QtFileTransferListItemModel.h b/Swift/QtUI/QtFileTransferListItemModel.h
new file mode 100644
index 0000000..1d892a5
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListItemModel.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <QAbstractItemModel>
+
+namespace Swift {
+
+class FileTransferController;
+class FileTransferOverview;
+
+class QtFileTransferListItemModel : public QAbstractItemModel {
+	Q_OBJECT
+public:
+	explicit QtFileTransferListItemModel(QObject *parent = 0);
+
+	void setFileTransferOverview(FileTransferOverview*);
+
+	QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+	int columnCount(const QModelIndex &parent) const;
+	QVariant data(const QModelIndex &index, int role) const;
+	QModelIndex parent(const QModelIndex &child) const;
+	int rowCount(const QModelIndex &parent) const;
+	QModelIndex index(int row, int column, const QModelIndex &parent) const;
+
+private:
+	enum Columns {
+		Direction = 0,
+		OtherParty,
+		State,
+		Progress,
+		OverallSize,
+		NoOfColumns,
+	};
+
+private:
+	void handleNewFileTransferController(FileTransferController*);
+	void handleStateChange(int index);
+	void handleProgressChange(int index);
+
+signals:
+
+public slots:
+
+private:
+	FileTransferOverview *fileTransferOverview;
+
+};
+
+}
diff --git a/Swift/QtUI/QtFileTransferListWidget.cpp b/Swift/QtUI/QtFileTransferListWidget.cpp
new file mode 100644
index 0000000..01c632f
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListWidget.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "QtFileTransferListWidget.h"
+
+#include <Swift/QtUI/QtFileTransferListItemModel.h>
+
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QWidget>
+#include <QPushButton>
+
+namespace Swift {
+
+QtFileTransferListWidget::QtFileTransferListWidget() : fileTransferOverview(0) {
+	QVBoxLayout* layout = new QVBoxLayout(this);
+	layout->setSpacing(0);
+	layout->setContentsMargins(0,0,0,0);
+
+	treeView = new QTreeView(this);
+	treeView->setRootIsDecorated(false);
+	treeView->setItemsExpandable(false);
+	layout->addWidget(treeView);
+
+	itemModel = new QtFileTransferListItemModel();
+	treeView->setModel(itemModel);
+
+	QWidget* bottom = new QWidget(this);
+	layout->addWidget(bottom);
+	bottom->setAutoFillBackground(true);
+
+	QHBoxLayout* buttonLayout = new QHBoxLayout(bottom);
+	buttonLayout->setContentsMargins(10,0,20,0);
+	buttonLayout->setSpacing(0);
+
+	QPushButton* clearFinished = new QPushButton(tr("Clear Finished Transfers"), bottom);
+	clearFinished->setEnabled(false);
+	//connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear()));
+	buttonLayout->addWidget(clearFinished);
+
+	setWindowTitle(tr("File Transfer List"));
+	emit titleUpdated();
+}
+
+QtFileTransferListWidget::~QtFileTransferListWidget() {
+	delete itemModel;
+}
+
+void QtFileTransferListWidget::showEvent(QShowEvent* event) {
+	emit windowOpening();
+	emit titleUpdated(); /* This just needs to be somewhere after construction */
+	QWidget::showEvent(event);
+}
+
+void QtFileTransferListWidget::show() {
+	QWidget::show();
+	emit windowOpening();
+}
+
+void QtFileTransferListWidget::activate() {
+	emit wantsToActivate();
+}
+
+void QtFileTransferListWidget::setFileTransferOverview(FileTransferOverview *overview) {
+	fileTransferOverview = overview;
+	itemModel->setFileTransferOverview(overview);
+}
+
+void QtFileTransferListWidget::closeEvent(QCloseEvent* event) {
+	emit windowClosing();
+	event->accept();
+}
+
+}
diff --git a/Swift/QtUI/QtFileTransferListWidget.h b/Swift/QtUI/QtFileTransferListWidget.h
new file mode 100644
index 0000000..c828d4e
--- /dev/null
+++ b/Swift/QtUI/QtFileTransferListWidget.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include "Swift/Controllers/UIInterfaces/FileTransferListWidget.h"
+
+#include "QtTabbable.h"
+
+#include <QCloseEvent>
+#include <QShowEvent>
+#include <QTreeView>
+
+namespace Swift {
+
+class FileTransferOverview;
+class QtFileTransferListItemModel;
+
+class QtFileTransferListWidget : public QtTabbable, public FileTransferListWidget {
+	Q_OBJECT
+
+public:
+	QtFileTransferListWidget();
+	virtual ~QtFileTransferListWidget();
+
+	void show();
+	void activate();
+
+	void setFileTransferOverview(FileTransferOverview *);
+
+private:
+	virtual void closeEvent(QCloseEvent* event);
+	virtual void showEvent(QShowEvent* event);
+
+private:
+	QTreeView* treeView;
+
+	QtFileTransferListItemModel* itemModel;
+	FileTransferOverview* fileTransferOverview;
+};
+
+}
diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp
index 475863c..543af65 100644
--- a/Swift/QtUI/QtLoginWindow.cpp
+++ b/Swift/QtUI/QtLoginWindow.cpp
@@ -7,6 +7,7 @@
 #include "QtLoginWindow.h"
 
 #include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
 #include <algorithm>
 #include <cassert>
 
@@ -28,6 +29,7 @@
  
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h"
+#include "Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h"
 #include "Swift/Controllers/UIEvents/ToggleSoundsUIEvent.h"
 #include "Swift/Controllers/UIEvents/ToggleNotificationsUIEvent.h"
 #include "Swiften/Base/Platform.h"
@@ -166,6 +168,10 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow(), forg
 	connect(xmlConsoleAction_, SIGNAL(triggered()), SLOT(handleShowXMLConsole()));
 	generalMenu_->addAction(xmlConsoleAction_);
 
+	fileTransferOverviewAction_ = new QAction(tr("Show &File Transfer Overview"), this);
+	connect(fileTransferOverviewAction_, SIGNAL(triggered()), SLOT(handleShowFileTransferOverview()));
+	generalMenu_->addAction(fileTransferOverviewAction_);
+
 	toggleSoundsAction_ = new QAction(tr("&Play Sounds"), this);
 	toggleSoundsAction_->setCheckable(true);
 	toggleSoundsAction_->setChecked(true);
@@ -356,6 +362,10 @@ void QtLoginWindow::handleShowXMLConsole() {
 	uiEventStream_->send(boost::shared_ptr<RequestXMLConsoleUIEvent>(new RequestXMLConsoleUIEvent()));
 }
 
+void QtLoginWindow::handleShowFileTransferOverview() {
+	uiEventStream_->send(boost::make_shared<RequestFileTransferListUIEvent>());
+}
+
 void QtLoginWindow::handleToggleSounds(bool enabled) {
 	uiEventStream_->send(boost::shared_ptr<ToggleSoundsUIEvent>(new ToggleSoundsUIEvent(enabled)));
 }
diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h
index 4628af7..414bb20 100644
--- a/Swift/QtUI/QtLoginWindow.h
+++ b/Swift/QtUI/QtLoginWindow.h
@@ -51,6 +51,7 @@ namespace Swift {
 			void handleCertficateChecked(bool);
 			void handleQuit();
 			void handleShowXMLConsole();
+			void handleShowFileTransferOverview();
 			void handleToggleSounds(bool enabled);
 			void handleToggleNotifications(bool enabled);
 			void handleAbout();
@@ -88,5 +89,6 @@ namespace Swift {
 			QPointer<QtAboutWidget> aboutDialog_;
 			bool forgetful_;
 			QAction* xmlConsoleAction_;
+			QAction* fileTransferOverviewAction_;
 	};
 }
diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp
index bd936d4..9de700c 100644
--- a/Swift/QtUI/QtUIFactory.cpp
+++ b/Swift/QtUI/QtUIFactory.cpp
@@ -24,6 +24,7 @@
 #include "QtProfileWindow.h"
 #include "QtContactEditWindow.h"
 #include "QtAdHocCommandWindow.h"
+#include "QtFileTransferListWidget.h"
 
 #define CHATWINDOW_FONT_SIZE "chatWindowFontSize"
 
@@ -43,6 +44,15 @@ XMLConsoleWidget* QtUIFactory::createXMLConsoleWidget() {
 	return widget;
 }
 
+FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {
+	QtFileTransferListWidget* widget = new QtFileTransferListWidget();
+	tabs->addTab(widget);
+	if (!tabs->isVisible()) {
+		tabs->show();
+	}
+	widget->show();
+	return widget;
+}
 
 MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) {
 	lastMainWindow  = new QtMainWindow(settings, eventStream);
diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h
index e7ea6c6..8fc5395 100644
--- a/Swift/QtUI/QtUIFactory.h
+++ b/Swift/QtUI/QtUIFactory.h
@@ -39,6 +39,7 @@ namespace Swift {
 			virtual JoinMUCWindow* createJoinMUCWindow(UIEventStream* uiEventStream);
 			virtual ProfileWindow* createProfileWindow();
 			virtual ContactEditWindow* createContactEditWindow();
+			virtual FileTransferListWidget* createFileTransferListWidget();
 			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);
 
 		private slots:
diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp
index 5f9539e..d849ce7 100644
--- a/Swift/QtUI/QtWebView.cpp
+++ b/Swift/QtUI/QtWebView.cpp
@@ -45,7 +45,9 @@ void QtWebView::setFontSizeIsMinimal(bool minimum) {
 
 void QtWebView::contextMenuEvent(QContextMenuEvent* ev) {
 	// Filter out the relevant actions from the standard actions
+	
 	QMenu* menu = page()->createStandardContextMenu();
+	/*
 	QList<QAction*> actions(menu->actions());
 	for (int i = 0; i < actions.size(); ++i) {
 		QAction* action = actions.at(i);
@@ -59,7 +61,7 @@ void QtWebView::contextMenuEvent(QContextMenuEvent* ev) {
 		if (removeAction) {
 			menu->removeAction(action);
 		}
-	}
+	}*/
 
 	// Add our own custom actions
 	menu->addAction(tr("Clear"), this, SIGNAL(clearRequested()));
diff --git a/Swift/QtUI/QtXMLConsoleWidget.cpp b/Swift/QtUI/QtXMLConsoleWidget.cpp
index b0c0385..0c88ce6 100644
--- a/Swift/QtUI/QtXMLConsoleWidget.cpp
+++ b/Swift/QtUI/QtXMLConsoleWidget.cpp
@@ -6,6 +6,8 @@
 
 #include "QtXMLConsoleWidget.h"
 
+#include <Swiften/Client/XMLBeautifier.h>
+
 #include <QCloseEvent>
 #include <QTextEdit>
 #include <QVBoxLayout>
@@ -49,6 +51,11 @@ QtXMLConsoleWidget::QtXMLConsoleWidget() {
 
 	setWindowTitle(tr("Debug Console"));
 	emit titleUpdated();
+	beautifier = new XMLBeautifier(true, false);
+}
+
+QtXMLConsoleWidget::~QtXMLConsoleWidget() {
+	delete beautifier;
 }
 
 void QtXMLConsoleWidget::showEvent(QShowEvent* event) {
@@ -72,11 +79,11 @@ void QtXMLConsoleWidget::closeEvent(QCloseEvent* event) {
 }
 
 void QtXMLConsoleWidget::handleDataRead(const SafeByteArray& data) {
-	appendTextIfEnabled(std::string(tr("<!-- IN -->").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(33,98,33));
+	appendTextIfEnabled(std::string(tr("<!-- IN -->").toUtf8()) + "\n" + beautifier->beautify(safeByteArrayToString(data)) + "\n", QColor(33,98,33));
 }
 
 void QtXMLConsoleWidget::handleDataWritten(const SafeByteArray& data) {
-	appendTextIfEnabled(std::string(tr("<!-- OUT -->").toUtf8()) + "\n" + safeByteArrayToString(data) + "\n", QColor(155,1,0));
+	appendTextIfEnabled(std::string(tr("<!-- OUT -->").toUtf8()) + "\n" + beautifier->beautify(safeByteArrayToString(data)) + "\n", QColor(155,1,0));
 }
 
 void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QColor& color) {
diff --git a/Swift/QtUI/QtXMLConsoleWidget.h b/Swift/QtUI/QtXMLConsoleWidget.h
index 73a3bad..c22251f 100644
--- a/Swift/QtUI/QtXMLConsoleWidget.h
+++ b/Swift/QtUI/QtXMLConsoleWidget.h
@@ -14,11 +14,14 @@ class QCheckBox;
 class QColor;
 
 namespace Swift {
+	class XMLBeautifier;
+
 	class QtXMLConsoleWidget : public QtTabbable, public XMLConsoleWidget {
 			Q_OBJECT
 
 		public:
 			QtXMLConsoleWidget();
+			~QtXMLConsoleWidget();
 
 			void show();
 			void activate();
@@ -35,5 +38,6 @@ namespace Swift {
 		private:
 			QTextEdit* textEdit;
 			QCheckBox* enabled;
+			XMLBeautifier* beautifier;
 	};
 }
diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp
index 923f977..f3b0441 100644
--- a/Swift/QtUI/Roster/QtRosterWidget.cpp
+++ b/Swift/QtUI/Roster/QtRosterWidget.cpp
@@ -9,10 +9,12 @@
 #include <QContextMenuEvent>
 #include <QMenu>
 #include <QInputDialog>
+#include <QFileDialog>
 
 #include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h"
 #include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h"
 #include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
 #include "QtContactEditWindow.h"
 #include "Swift/Controllers/Roster/ContactRosterItem.h"
 #include "Swift/Controllers/Roster/GroupRosterItem.h"
@@ -54,6 +56,10 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
 	if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
 		QAction* editContact = contextMenu.addAction(tr("Edit"));
 		QAction* removeContact = contextMenu.addAction(tr("Remove"));
+		QAction* sendFile = NULL;
+		if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+			sendFile = contextMenu.addAction(tr("Send File"));
+		}
 		QAction* result = contextMenu.exec(event->globalPos());
 		if (result == editContact) {
 			eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID()));
@@ -63,6 +69,12 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) {
 				eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID()));
 			}
 		}
+		else if (result == sendFile) {
+			QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;"));
+			if (!fileName.isEmpty()) {
+				eventStream_->send(boost::make_shared<SendFileUIEvent>(contact->getJID(), fileName.toStdString()));
+			}
+		}
 	}
 	else if (GroupRosterItem* group = dynamic_cast<GroupRosterItem*>(item)) {
 		QAction* renameGroupAction = contextMenu.addAction(tr("Rename"));
diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp
index 96a078b..79dd6a2 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.cpp
+++ b/Swift/QtUI/Roster/QtTreeWidget.cpp
@@ -8,11 +8,14 @@
 
 #include <boost/smart_ptr/make_shared.hpp>
 
+#include <QUrl>
+
 #include "Swiften/Base/Platform.h"
 #include "Swift/Controllers/Roster/ContactRosterItem.h"
 #include "Swift/Controllers/Roster/GroupRosterItem.h"
 #include "Swift/Controllers/UIEvents/UIEventStream.h"
 #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
+#include "Swift/Controllers/UIEvents/SendFileUIEvent.h"
 #include "QtSwiftUtil.h"
 
 namespace Swift {
@@ -31,6 +34,7 @@ QtTreeWidget::QtTreeWidget(UIEventStream* eventStream, QWidget* parent) : QTreeV
 	expandAll();
 	setAnimated(true);
 	setIndentation(0);
+	setAcceptDrops(true);
 	setRootIsDecorated(true);
 	connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(handleItemActivated(const QModelIndex&)));
 	connect(model_, SIGNAL(itemExpanded(const QModelIndex&, bool)), this, SLOT(handleModelItemExpanded(const QModelIndex&, bool)));
@@ -104,6 +108,41 @@ void QtTreeWidget::handleItemActivated(const QModelIndex& index) {
 	}
 }
 
+void QtTreeWidget::dragEnterEvent(QDragEnterEvent *event) {
+	if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1) {
+		event->acceptProposedAction();
+	}
+}
+
+void QtTreeWidget::dropEvent(QDropEvent *event) {
+	QModelIndex index = indexAt(event->pos());
+	if (index.isValid()) {
+		RosterItem* item = static_cast<RosterItem*>(index.internalPointer());
+		if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
+			if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+				QString filename = event->mimeData()->urls().at(0).toLocalFile();
+				if (!filename.isEmpty()) {
+					eventStream_->send(boost::make_shared<SendFileUIEvent>(contact->getJID(), filename.toStdString()));
+				}
+			}
+		}
+	}
+}
+
+void QtTreeWidget::dragMoveEvent(QDragMoveEvent* event) {
+	QModelIndex index = indexAt(event->pos());
+	if (index.isValid()) {
+		RosterItem* item = static_cast<RosterItem*>(index.internalPointer());
+		if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) {
+			if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) {
+				event->accept();
+				return;
+			}
+		}
+	}
+	event->ignore();
+}
+
 void QtTreeWidget::handleExpanded(const QModelIndex& index) {
 	GroupRosterItem* item = dynamic_cast<GroupRosterItem*>(static_cast<RosterItem*>(index.internalPointer()));
 	if (item) {
diff --git a/Swift/QtUI/Roster/QtTreeWidget.h b/Swift/QtUI/Roster/QtTreeWidget.h
index 1ad56d6..271fbd5 100644
--- a/Swift/QtUI/Roster/QtTreeWidget.h
+++ b/Swift/QtUI/Roster/QtTreeWidget.h
@@ -8,6 +8,9 @@
 
 #include <QTreeView>
 #include <QModelIndex>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QDragMoveEvent>
 #include "Swift/QtUI/Roster/RosterModel.h"
 #include "Swift/QtUI/Roster/RosterDelegate.h"
 
@@ -31,6 +34,10 @@ class QtTreeWidget : public QTreeView{
 		void handleExpanded(const QModelIndex&);
 		void handleCollapsed(const QModelIndex&);
 		void handleClicked(const QModelIndex&);
+	protected:
+		void dragEnterEvent(QDragEnterEvent* event);
+		void dropEvent(QDropEvent* event);
+		void dragMoveEvent(QDragMoveEvent* event);
 
 	protected:
 		QModelIndexList getSelectedIndexes() const;
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 33450ed..1d7ab78 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -84,6 +84,8 @@ sources = [
     "QtTabWidget.cpp",
     "QtTextEdit.cpp",
     "QtXMLConsoleWidget.cpp",
+	"QtFileTransferListWidget.cpp",
+	"QtFileTransferListItemModel.cpp",
     "QtAdHocCommandWindow.cpp",
     "QtUtilities.cpp",
     "QtBookmarkDetailWindow.cpp",
@@ -133,6 +135,7 @@ sources = [
     "QtWebView.cpp",
     "qrc_DefaultTheme.cc",
     "qrc_Swift.cc",
+    "QtFileTransferJSBridge.cpp",
   ]
 
 myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index c53bcaf..e95e704 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -26,6 +26,9 @@
 #include <Swiften/TLS/BlindCertificateTrustChecker.h>
 #include <Swiften/Client/NickManagerImpl.h>
 #include <Swiften/Client/ClientSession.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Network/NetworkFactories.h>
+#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
 
 namespace Swift {
 
@@ -59,9 +62,15 @@ Client::Client(const JID& jid, const SafeString& password, NetworkFactories* net
 	nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry);
 
 	blindCertificateTrustChecker = new BlindCertificateTrustChecker();
+	
+	jingleSessionManager = new JingleSessionManager(getIQRouter());
+	fileTransferManager = NULL;
 }
 
 Client::~Client() {
+	delete fileTransferManager;
+	delete jingleSessionManager;
+	
 	delete blindCertificateTrustChecker;
 
 	delete nickResolver;
@@ -98,6 +107,10 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers
 	softwareVersionResponder->setVersion(name, version, os);
 }
 
+void Client::handleConnected() {
+	fileTransferManager = new FileTransferManagerImpl(getJID(), jingleSessionManager, getIQRouter(), getEntityCapsProvider(), presenceOracle, getNetworkFactories()->getConnectionFactory(), getNetworkFactories()->getConnectionServerFactory(), getNetworkFactories()->getTimerFactory(), getNetworkFactories()->getPlatformNATTraversalWorker());
+}
+
 void Client::requestRoster() {
 	// FIXME: We should set this once when the session is finished, but there
 	// is currently no callback for this
@@ -139,4 +152,8 @@ NickManager* Client::getNickManager() const {
 	return nickManager;
 }
 
+FileTransferManager* Client::getFileTransferManager() const {
+	return fileTransferManager;
+}
+
 }
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 08289a5..7269f10 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -33,6 +33,9 @@ namespace Swift {
 	class SubscriptionManager;
 	class ClientDiscoManager;
 	class NickManager;
+	class FileTransferManager;
+	class JingleSessionManager;
+	class FileTransferManagerImpl;
 
 	/**
 	 * Provides the core functionality for writing XMPP client software.
@@ -131,6 +134,11 @@ namespace Swift {
 			ClientDiscoManager* getDiscoManager() const {
 				return discoManager;
 			}
+			
+			/**
+			 * Returns a FileTransferManager for the client. This is only available after the onConnected signal has been fired.
+			 */
+			FileTransferManager* getFileTransferManager() const;
 
 			/**
 			 * Configures the client to always trust a non-validating
@@ -149,6 +157,9 @@ namespace Swift {
 		private:
 			Storages* getStorages() const;
 
+		protected:
+			void handleConnected();
+
 		private:
 			Storages* storages;
 			MemoryStorages* memoryStorages;
@@ -168,6 +179,8 @@ namespace Swift {
 			SubscriptionManager* subscriptionManager;
 			MUCManager* mucManager;
 			ClientDiscoManager* discoManager;
+			JingleSessionManager* jingleSessionManager;
+			FileTransferManagerImpl* fileTransferManager;
 			BlindCertificateTrustChecker* blindCertificateTrustChecker;
 	};
 }
diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp
index c1e398d..c1093eb 100644
--- a/Swiften/Client/ClientXMLTracer.cpp
+++ b/Swiften/Client/ClientXMLTracer.cpp
@@ -12,13 +12,18 @@
 namespace Swift {
 
 ClientXMLTracer::ClientXMLTracer(CoreClient* client) {
-	client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1));
-	client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1));
+	beautifier = new XMLBeautifier(true, true);
+	client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1));
+	client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1));
+}
+
+ClientXMLTracer::~ClientXMLTracer() {
+	delete beautifier;
 }
 
 void ClientXMLTracer::printData(char direction, const SafeByteArray& data) {
 	printLine(direction);
-	std::cerr << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl;
+	std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
 }
 
 void ClientXMLTracer::printLine(char c) {
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
index dd94e0e..0752faa 100644
--- a/Swiften/Client/ClientXMLTracer.h
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -7,15 +7,19 @@
 #pragma once
 
 #include <Swiften/Client/CoreClient.h>
+#include <Swiften/Client/XMLBeautifier.h>
 #include <Swiften/Base/SafeByteArray.h>
 
 namespace Swift {
 	class ClientXMLTracer {
 		public:
 			ClientXMLTracer(CoreClient* client);
+			~ClientXMLTracer();
+		private:
+			void printData(char direction, const SafeByteArray& data);
+			void printLine(char c);
 
 		private:
-			static void printData(char direction, const SafeByteArray& data);
-			static void printLine(char c);
+			XMLBeautifier *beautifier;
 	};
 }
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index cceec74..37055e4 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -276,6 +276,7 @@ void CoreClient::handleDataWritten(const SafeByteArray& data) {
 
 void CoreClient::handleStanzaChannelAvailableChanged(bool available) {
 	if (available) {
+		handleConnected();
 		onConnected();
 	}
 }
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 16813de..3472e76 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -188,6 +188,15 @@ namespace Swift {
 				return session_;
 			}
 
+			NetworkFactories* getNetworkFactories() const {
+				return networkFactories;
+			}
+
+			/**
+			 * Called before onConnected signal is emmitted.
+			 */
+			virtual void handleConnected() {};
+
 		private:
 			void handleConnectorFinished(boost::shared_ptr<Connection>);
 			void handleStanzaChannelAvailableChanged(bool available);
diff --git a/Swiften/Client/XMLBeautifier.cpp b/Swiften/Client/XMLBeautifier.cpp
new file mode 100644
index 0000000..b70fc48
--- /dev/null
+++ b/Swiften/Client/XMLBeautifier.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <sstream>
+#include <stack>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/XMLBeautifier.h>
+#include <Swiften/Parser/PlatformXMLParserFactory.h>
+
+namespace Swift {
+
+XMLBeautifier::XMLBeautifier(bool indention, bool coloring) : doIndention(indention), doColoring(coloring), intLevel(0), parser(NULL), lastWasStepDown(false) {
+	factory = new PlatformXMLParserFactory();
+}
+
+XMLBeautifier::~XMLBeautifier() {
+	delete factory;
+}
+
+std::string XMLBeautifier::beautify(const std::string &text) {
+	parser = factory->createXMLParser(this);
+	intLevel = 0;
+	buffer.str(std::string());
+	parser->parse(text);
+	delete parser;
+	return buffer.str();
+}
+
+void XMLBeautifier::indent() {
+	for (int i = 0; i < intLevel; ++i) {
+		buffer << " ";
+	}
+}
+
+// all bold but reset
+const char colorBlue[] = "\x1b[01;34m";
+const char colorCyan[] = "\x1b[01;36m";
+const char colorGreen[] = "\x1b[01;32m";
+const char colorMagenta[] = "\x1b[01;35m";
+const char colorRed[] = "\x1b[01;31m";
+const char colorReset[] = "\x1b[0m";
+const char colorYellow[] = "\x1b[01;33m";
+
+
+
+std::string XMLBeautifier::styleTag(const std::string& text) const {
+	std::string result;
+	result += colorYellow;
+	result += text;
+	result += colorReset;
+	return result;
+}
+
+std::string XMLBeautifier::styleNamespace(const std::string& text) const {
+	std::string result;
+	result += colorRed;
+	result += text;
+	result += colorReset;
+	return result;
+}
+
+std::string XMLBeautifier::styleAttribute(const std::string& text) const {
+	std::string result;
+	result += colorGreen;
+	result += text;
+	result += colorReset;
+	return result;
+}
+std::string XMLBeautifier::styleValue(const std::string& text) const {
+	std::string result;
+	result += colorCyan;
+	result += text;
+	result += colorReset;
+	return result;
+}
+
+void XMLBeautifier::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+	if (doIndention) {
+		if (intLevel) buffer << std::endl;
+	}
+	indent();
+	buffer << "<" << (doColoring ? styleTag(element) : element);
+	if (!ns.empty() && (!parentNSs.empty() && parentNSs.top() != ns)) {
+		buffer << " ";
+		buffer << (doColoring ? styleAttribute("xmlns") : "xmlns");
+		buffer << "=";
+		buffer << "\"" << (doColoring ? styleNamespace(ns) : ns) << "\"";
+	}
+	if (!attributes.getEntries().empty()) {
+		foreach(AttributeMap::Entry entry, attributes.getEntries()) {
+			buffer << " ";
+			buffer << (doColoring ? styleAttribute(entry.getAttribute().getName()) : entry.getAttribute().getName());
+			buffer << "=";
+			buffer << "\"" << (doColoring ? styleValue(entry.getValue()) : entry.getValue()) << "\"";
+		}
+	}
+	buffer << ">";
+	++intLevel;
+	lastWasStepDown = false;
+	parentNSs.push(ns);
+}
+
+void XMLBeautifier::handleEndElement(const std::string& element, const std::string& /* ns */) {
+	--intLevel;
+	parentNSs.pop();
+	if (/*hadCDATA.top() ||*/ lastWasStepDown) {
+		if (doIndention) {
+			buffer << std::endl;
+		}
+		indent();
+	}
+	buffer << "</" << (doColoring ? styleTag(element) : element) << ">";
+	lastWasStepDown = true;
+}
+
+void XMLBeautifier::handleCharacterData(const std::string& data) {
+	buffer << data;
+	lastWasStepDown = false;
+}
+
+}
diff --git a/Swiften/Client/XMLBeautifier.h b/Swiften/Client/XMLBeautifier.h
new file mode 100644
index 0000000..44dfd20
--- /dev/null
+++ b/Swiften/Client/XMLBeautifier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <stack>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Parser/XMLParserFactory.h>
+#include <Swiften/Parser/XMLParserClient.h>
+#include <Swiften/Parser/XMLParser.h>
+
+namespace Swift {
+
+class XMLBeautifier : public XMLParserClient {
+public:
+	XMLBeautifier(bool indention, bool coloring);
+	virtual ~XMLBeautifier();
+
+	std::string beautify(const std::string&);
+
+private:
+	void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes);
+	void handleEndElement(const std::string& element, const std::string& ns);
+	void handleCharacterData(const std::string& data);
+
+private:
+	void indent();
+
+private:
+	std::string styleTag(const std::string& text) const;
+	std::string styleNamespace(const std::string& text) const;
+	std::string styleAttribute(const std::string& text) const;
+	std::string styleValue(const std::string& text) const;
+
+private:
+	bool doIndention;
+	bool doColoring;
+
+	int intLevel;
+	std::string inputBuffer;
+	std::stringstream buffer;
+	XMLParserFactory* factory;
+	XMLParser* parser;
+
+	bool lastWasStepDown;
+	std::stack<std::string> parentNSs;
+};
+}
diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp
index f54b6bf..35d4d04 100644
--- a/Swiften/Elements/DiscoInfo.cpp
+++ b/Swiften/Elements/DiscoInfo.cpp
@@ -16,7 +16,11 @@ const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmp
 const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search");
 const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands");
 const std::string DiscoInfo::MessageCorrectionFeature = std::string("urn:xmpp:message-correct:0");
-
+const std::string DiscoInfo::JingleFeature = std::string("urn:xmpp:jingle:1");
+const std::string DiscoInfo::JingleFTFeature = std::string("urn:xmpp:jingle:apps:file-transfer:3");
+const std::string DiscoInfo::JingleTransportsIBBFeature = std::string("urn:xmpp:jingle:transports:ibb:1");
+const std::string DiscoInfo::JingleTransportsS5BFeature = std::string("urn:xmpp:jingle:transports:s5b:1");
+const std::string DiscoInfo::Bytestream = std::string("http://jabber.org/protocol/bytestreams");
 
 
 bool DiscoInfo::Identity::operator<(const Identity& other) const {
diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h
index c5c9e1c..6d6e722 100644
--- a/Swiften/Elements/DiscoInfo.h
+++ b/Swiften/Elements/DiscoInfo.h
@@ -23,6 +23,11 @@ namespace Swift {
 			static const std::string JabberSearchFeature;
 			static const std::string CommandsFeature;
 			static const std::string MessageCorrectionFeature;
+			static const std::string JingleFeature;
+			static const std::string JingleFTFeature;
+			static const std::string JingleTransportsIBBFeature;
+			static const std::string JingleTransportsS5BFeature;
+			static const std::string Bytestream;
 
 			class Identity {
 				public:
diff --git a/Swiften/Elements/JingleContentPayload.h b/Swiften/Elements/JingleContentPayload.h
index c44a806..183b8eb 100644
--- a/Swiften/Elements/JingleContentPayload.h
+++ b/Swiften/Elements/JingleContentPayload.h
@@ -21,6 +21,7 @@ namespace Swift {
 			typedef boost::shared_ptr<JingleContentPayload> ref;
 
 			enum Creator {
+				UnknownCreator,
 				InitiatorCreator,
 				ResponderCreator,
 			};
diff --git a/Swiften/Elements/JingleFileTransferDescription.h b/Swiften/Elements/JingleFileTransferDescription.h
index 19644bd..04f3f1f 100644
--- a/Swiften/Elements/JingleFileTransferDescription.h
+++ b/Swiften/Elements/JingleFileTransferDescription.h
@@ -7,7 +7,7 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
+#include <vector>
 
 #include <Swiften/Elements/JingleDescription.h>
 #include <Swiften/Elements/StreamInitiationFileInfo.h>
@@ -17,15 +17,25 @@ namespace Swift {
 		public:
 			typedef boost::shared_ptr<JingleFileTransferDescription> ref;
 
-			void setOffer(const StreamInitiationFileInfo& offer) {
-				this->offer = offer;
+			void addOffer(const StreamInitiationFileInfo& offer) {
+				offers.push_back(offer);
 			}
+			
 
-			const boost::optional<StreamInitiationFileInfo>& getOffer() const {
-				return offer;
+			const std::vector<StreamInitiationFileInfo>& getOffers() const {
+				return offers;
+			}
+			
+			void addRequest(const StreamInitiationFileInfo& request) {
+				reqeusts.push_back(request);
+			}
+			
+			const std::vector<StreamInitiationFileInfo>& getRequests() const {
+				return reqeusts;
 			}
 
 		private:
-			boost::optional<StreamInitiationFileInfo> offer;
+			std::vector<StreamInitiationFileInfo> offers;
+			std::vector<StreamInitiationFileInfo> reqeusts;
 	};
 }
diff --git a/Swiften/Elements/JingleFileTransferHash.h b/Swiften/Elements/JingleFileTransferHash.h
new file mode 100644
index 0000000..5603531
--- /dev/null
+++ b/Swiften/Elements/JingleFileTransferHash.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+#include <Swiften/Elements/JingleDescription.h>
+
+namespace Swift {
+
+class JingleFileTransferHash : public Payload {
+public:
+	typedef std::map<std::string, std::string> HashesMap;
+public:
+	typedef boost::shared_ptr<JingleFileTransferHash> ref;
+
+	void setHash(const std::string& algo, const std::string& hash) {
+		hashes[algo] = hash;
+	}
+
+	const HashesMap& getHashes() const {
+		return hashes;
+	}
+
+private:
+	HashesMap hashes;
+};
+	
+}
diff --git a/Swiften/Elements/JingleFileTransferReceived.h b/Swiften/Elements/JingleFileTransferReceived.h
new file mode 100644
index 0000000..75c95d9
--- /dev/null
+++ b/Swiften/Elements/JingleFileTransferReceived.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+
+class JingleFileTransferReceived : public Payload {
+	public:
+		typedef boost::shared_ptr<JingleFileTransferReceived> ref;
+
+		void setFileInfo(const StreamInitiationFileInfo& fileInfo) {
+			this->fileInfo = fileInfo;
+		}
+
+		const StreamInitiationFileInfo& getFileInfo() const {
+			return this->fileInfo;
+		}
+	private:
+		StreamInitiationFileInfo fileInfo;
+
+};
+
+}
diff --git a/Swiften/Elements/JingleIBBTransportPayload.h b/Swiften/Elements/JingleIBBTransportPayload.h
index 67aab09..8c174f0 100644
--- a/Swiften/Elements/JingleIBBTransportPayload.h
+++ b/Swiften/Elements/JingleIBBTransportPayload.h
@@ -29,14 +29,6 @@ namespace Swift {
 				return stanzaType;
 			}
 
-			void setSessionID(const std::string& id) {
-				sessionID = id;
-			}
-
-			const std::string& getSessionID() const {
-				return sessionID;
-			}
-
 			int getBlockSize() const {
 				return blockSize;
 			}
@@ -46,7 +38,6 @@ namespace Swift {
 			}
 
 		private:
-			std::string sessionID;
 			int blockSize;
 			StanzaType stanzaType;
 	};
diff --git a/Swiften/Elements/JinglePayload.h b/Swiften/Elements/JinglePayload.h
index 5c766b8..31d4448 100644
--- a/Swiften/Elements/JinglePayload.h
+++ b/Swiften/Elements/JinglePayload.h
@@ -7,6 +7,7 @@
 #pragma once
 
 #include <vector>
+#include <boost/shared_ptr.hpp>
 #include <boost/optional.hpp>
 
 #include <string>
@@ -14,13 +15,15 @@
 #include <Swiften/Elements/Payload.h>
 #include <Swiften/Elements/JingleContentPayload.h>
 
+#include <Swiften/Base/Log.h>
 
 namespace Swift {
 	class JinglePayload : public Payload {
 		public:
 			typedef boost::shared_ptr<JinglePayload> ref;
-			struct Reason {
+			struct Reason : public Payload {
 					enum Type {
+						UnknownType,
 						AlternativeSession,
 						Busy,
 						Cancel,
@@ -39,13 +42,15 @@ namespace Swift {
 						UnsupportedApplications,
 						UnsupportedTransports
 					};
-					
+					Reason() : type(UnknownType), text("") {}
 					Reason(Type type, const std::string& text = "") : type(type), text(text) {}
+					~Reason() {}
 					Type type;
 					std::string text;
 			};
 
 			enum Action {
+				UnknownAction,
 				ContentAccept,
 				ContentAdd,
 				ContentModify,
@@ -62,8 +67,11 @@ namespace Swift {
 				TransportReject,
 				TransportReplace
 			};
-
+			JinglePayload() : action(SessionTerminate), sessionID("") {
+			}
+			
 			JinglePayload(Action action, const std::string& sessionID) : action(action), sessionID(sessionID) {
+			
 			}
 
 			void setAction(Action action) {
@@ -99,11 +107,46 @@ namespace Swift {
 			}
 
 			void addContent(JingleContentPayload::ref content) {
-				this->contents.push_back(content);
+				this->payloads.push_back(content);
+			}
+			
+			void addPayload(boost::shared_ptr<Payload> payload) {
+				this->payloads.push_back(payload);
+			}
+			
+			const std::vector<JingleContentPayload::ref> getContents() const {
+				return getPayloads<JingleContentPayload>();
+			}
+			
+			const std::vector<boost::shared_ptr<Payload> > getPayloads() const {
+				return payloads;
+			}
+			
+			template<typename T>
+			const std::vector<boost::shared_ptr<T> > getPayloads() const {
+				std::vector<boost::shared_ptr<T> > matched_payloads;
+				for (std::vector<boost::shared_ptr<Payload> >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) {
+					boost::shared_ptr<T> result = boost::dynamic_pointer_cast<T>(*i);
+					if (result) {
+						matched_payloads.push_back(result);
+					}
+				}
+				
+				return matched_payloads;
+				
 			}
 
-			const std::vector<JingleContentPayload::ref>& getContents() const {
-				return contents;
+			template<typename T>
+			const boost::shared_ptr<T> getPayload() const {
+				boost::shared_ptr<T> result;
+				for (std::vector<boost::shared_ptr<Payload> >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) {
+					result = boost::dynamic_pointer_cast<T>(*i);
+					if (result) {
+						return result;
+					}
+				}
+				
+				return result;
 			}
 
 			void setReason(const Reason& reason) {
@@ -119,7 +162,7 @@ namespace Swift {
 			JID initiator;
 			JID responder;
 			std::string sessionID;
-			std::vector<JingleContentPayload::ref> contents;
+			std::vector<boost::shared_ptr<Payload> > payloads;
 			boost::optional<Reason> reason;
 	};
 }
diff --git a/Swiften/Elements/JingleS5BTransportPayload.h b/Swiften/Elements/JingleS5BTransportPayload.h
index 7b3089f..980af27 100644
--- a/Swiften/Elements/JingleS5BTransportPayload.h
+++ b/Swiften/Elements/JingleS5BTransportPayload.h
@@ -6,23 +6,107 @@
 
 #pragma once
 
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
 #include <Swiften/Elements/JingleTransportPayload.h>
 #include <Swiften/Elements/Bytestreams.h>
+#include <Swiften/Network/HostAddressPort.h>
 
-// FIXME: Remove Bytestreams, and replace by our own candidate
 
 namespace Swift {
 	class JingleS5BTransportPayload : public JingleTransportPayload {
 		public:
-			const Bytestreams& getInfo() const {
-				return info;
+			enum Mode {
+				TCPMode, // default case
+				UDPMode,
+			};
+
+			struct Candidate {
+				enum Type {
+					DirectType, // default case
+					AssistedType,
+					TunnelType,
+					ProxyType,
+				};
+
+				Candidate() : priority(0), type(DirectType) {}
+
+				std::string cid;
+				JID jid;
+				HostAddressPort hostPort;
+				int priority;
+				Type type;
+			};
+
+			struct CompareCandidate {
+				bool operator() (const JingleS5BTransportPayload::Candidate& c1, const JingleS5BTransportPayload::Candidate& c2) const {
+					if (c1.priority < c2.priority) return true;
+					return false;
+				}
+			};
+
+		public:
+			JingleS5BTransportPayload() : mode(TCPMode), candidateError(false), proxyError(false) {}
+
+			Mode getMode() const {
+				return mode;
+			}
+
+			void setMode(Mode mode) {
+				this->mode = mode;
+			}
+
+			const std::vector<Candidate>& getCandidates() {
+				return candidates;
+			}
+
+			void addCandidate(const Candidate& candidate) {
+				candidates.push_back(candidate);
+			}
+
+			void setCandidateUsed(const std::string& cid) {
+				candidateUsedCID = cid;
+			}
+
+			const std::string& getCandidateUsed() const {
+				return candidateUsedCID;
+			}
+
+			void setActivated(const std::string& cid) {
+				activatedCID = cid;
 			}
 
-			void setInfo(const Bytestreams& info) {
-				this->info = info;
+			const std::string& getActivated() const {
+				return activatedCID;
 			}
 
+			void setCandidateError(bool hasError) {
+				candidateError = hasError;
+			}
+			
+			bool hasCandidateError() const {
+				return candidateError;
+			}
+			
+			void setProxyError(bool hasError) {
+				proxyError = hasError;
+			}
+
+			bool hasProxyError() const {
+				return proxyError;
+			}
+		public:
+			typedef boost::shared_ptr<JingleS5BTransportPayload> ref;
+
 		private:
-			Bytestreams info;
+			Mode mode;
+			std::vector<Candidate> candidates;
+			
+			std::string candidateUsedCID;
+			std::string activatedCID;
+			bool candidateError;
+			bool proxyError;
 	};
 }
diff --git a/Swiften/Elements/JingleTransportPayload.h b/Swiften/Elements/JingleTransportPayload.h
index 7a9ea29..b870be9 100644
--- a/Swiften/Elements/JingleTransportPayload.h
+++ b/Swiften/Elements/JingleTransportPayload.h
@@ -13,6 +13,18 @@
 namespace Swift {
 	class JingleTransportPayload : public Payload {
 		public:
+			void setSessionID(const std::string& id) {
+				sessionID = id;
+			}
+
+			const std::string& getSessionID() const {
+				return sessionID;
+			}
+
+		public:
 			typedef boost::shared_ptr<JingleTransportPayload> ref;
+
+		private:
+			std::string sessionID;
 	};
 }
diff --git a/Swiften/Elements/Payload.h b/Swiften/Elements/Payload.h
index 8b6d44a..f994ebc 100644
--- a/Swiften/Elements/Payload.h
+++ b/Swiften/Elements/Payload.h
@@ -6,9 +6,13 @@
 
 #pragma once
 
+#include <boost/shared_ptr.hpp>
+
 namespace Swift {
 	class Payload {
 		public:
+			typedef boost::shared_ptr<Payload> ref;
+		public:
 			virtual ~Payload();
 	};
 }
diff --git a/Swiften/Elements/S5BProxyRequest.h b/Swiften/Elements/S5BProxyRequest.h
new file mode 100644
index 0000000..fcd0cb2
--- /dev/null
+++ b/Swiften/Elements/S5BProxyRequest.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Elements/Payload.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/HostAddressPort.h>
+
+namespace Swift {
+
+class S5BProxyRequest : public Payload {
+public:
+	typedef boost::shared_ptr<S5BProxyRequest> ref;
+
+public:
+	struct StreamHost {
+		HostAddressPort addressPort;
+		JID jid;
+	};
+
+public:
+	const boost::optional<StreamHost>& getStreamHost() const {
+		return streamHost;
+	}
+
+	void setStreamHost(const StreamHost& streamHost) {
+		this->streamHost = boost::optional<StreamHost>(streamHost);
+	}
+
+	const std::string& getSID() const {
+		return sid;
+	}
+
+	void setSID(const std::string& sid) {
+		this->sid = sid;
+	}
+
+	const boost::optional<JID>& getActivate() const {
+		return activate;
+	}
+
+	void setActivate(const JID& activate) {
+		this->activate = activate;
+	}
+
+private:
+	boost::optional<StreamHost> streamHost;
+
+	std::string sid;
+	boost::optional<JID> activate;
+};
+
+}
diff --git a/Swiften/Elements/StreamInitiationFileInfo.h b/Swiften/Elements/StreamInitiationFileInfo.h
index 92b9824..9484bc0 100644
--- a/Swiften/Elements/StreamInitiationFileInfo.h
+++ b/Swiften/Elements/StreamInitiationFileInfo.h
@@ -6,14 +6,97 @@
 
 #pragma once
 
+#include <Swiften/Elements/Payload.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
 #include <string>
 
 namespace Swift {
-	struct StreamInitiationFileInfo {
-		StreamInitiationFileInfo(const std::string& name = "", const std::string& description = "", int size = -1) : name(name), description(description), size(size) {}
 
-		std::string name;
-		std::string description;
-		int size;
-	};
+class StreamInitiationFileInfo : public Payload {
+public:
+	typedef boost::shared_ptr<StreamInitiationFileInfo> ref;
+	
+public:
+	StreamInitiationFileInfo(const std::string& name = "", const std::string& description = "", int size = 0,
+				 const std::string& hash = "", const boost::posix_time::ptime &date = boost::posix_time::ptime(), const std::string& algo="md5") : 
+		name(name), description(description), size(size), hash(hash), date(date), algo(algo), supportsRangeRequests(false), rangeOffset(0) {}
+	
+	void setName(const std::string& name) {
+		this->name = name;;
+	}
+	
+	const std::string& getName() const {
+		return this->name;
+	}
+	
+	void setDescription(const std::string& description) {
+		this->description = description;
+	}
+	
+	const std::string& getDescription() const {
+		return this->description;
+	}
+	
+	void setSize(const boost::uintmax_t size) {
+		this->size = size;
+	}
+	
+	boost::uintmax_t getSize() const {
+		return this->size;
+	}
+	
+	void setHash(const std::string& hash) {
+		this->hash = hash;
+	}
+	
+	const std::string& getHash() const {
+		return this->hash;
+	}
+	
+	void setDate(const boost::posix_time::ptime& date) {
+		this->date = date;
+	}
+	
+	const boost::posix_time::ptime& getDate() const {
+		return this->date;
+	}
+	
+	void setAlgo(const std::string& algo) {
+		this->algo = algo;
+	}
+	
+	const std::string& getAlgo() const {
+		return this->algo;
+	}
+	
+	void setSupportsRangeRequests(const bool supportsIt) {
+		supportsRangeRequests = supportsIt;
+	}
+	
+	bool getSupportsRangeRequests() const {
+		return supportsRangeRequests;
+	}
+	
+	void setRangeOffset(const int offset) {
+		supportsRangeRequests = offset >= 0 ? true : false;
+		rangeOffset = offset;
+	}
+	
+	int getRangeOffset() const {
+		return rangeOffset;
+	}
+
+private:
+	std::string name;
+	std::string description;
+	boost::uintmax_t size;
+	std::string hash;
+	boost::posix_time::ptime date;
+	std::string algo;
+	bool supportsRangeRequests;
+	boost::uintmax_t rangeOffset;
+};
+
 }
diff --git a/Swiften/Examples/SendFile/ReceiveFile.cpp b/Swiften/Examples/SendFile/ReceiveFile.cpp
index effa1b7..f80f03a 100644
--- a/Swiften/Examples/SendFile/ReceiveFile.cpp
+++ b/Swiften/Examples/SendFile/ReceiveFile.cpp
@@ -12,12 +12,18 @@
 #include <Swiften/Elements/Presence.h>
 #include <Swiften/Base/foreach.h>
 #include <Swiften/Client/Client.h>
+#include <Swiften/Elements/DiscoInfo.h>
 #include <Swiften/Network/BoostNetworkFactories.h>
 #include <Swiften/EventLoop/SimpleEventLoop.h>
 #include <Swiften/Client/ClientXMLTracer.h>
+#include <Swiften/Disco/ClientDiscoManager.h>
 #include <Swiften/FileTransfer/IncomingFileTransferManager.h>
 #include <Swiften/FileTransfer/FileWriteBytestream.h>
 #include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
 
 using namespace Swift;
 
@@ -26,19 +32,20 @@ BoostNetworkFactories networkFactories(&eventLoop);
 
 int exitCode = 2;
 
+static const std::string CLIENT_NAME = "Swiften FT Test";
+static const std::string CLIENT_NODE = "http://swift.im";
+
 class FileReceiver {
 	public:
 		FileReceiver(const JID& jid, const std::string& password) : jid(jid), password(password), jingleSessionManager(NULL), incomingFileTransferManager(NULL) {
 			client = new Swift::Client(jid, password, &networkFactories);
 			client->onConnected.connect(boost::bind(&FileReceiver::handleConnected, this));
 			client->onDisconnected.connect(boost::bind(&FileReceiver::handleDisconnected, this, _1));
-			//tracer = new ClientXMLTracer(client);
+			tracer = new ClientXMLTracer(client);
 		}
 
 		~FileReceiver() {
-			delete incomingFileTransferManager;
-			delete jingleSessionManager;
-			//delete tracer;
+			delete tracer;
 			client->onDisconnected.disconnect(boost::bind(&FileReceiver::handleDisconnected, this, _1));
 			client->onConnected.disconnect(boost::bind(&FileReceiver::handleConnected, this));
 			delete client;
@@ -57,13 +64,24 @@ class FileReceiver {
 
 	private:
 		void handleConnected() {
-			client->sendPresence(Presence::create());
-			jingleSessionManager = new JingleSessionManager(client->getIQRouter());
-			incomingFileTransferManager = new IncomingFileTransferManager(jingleSessionManager, client->getIQRouter());
-			incomingFileTransferManager->onIncomingFileTransfer.connect(boost::bind(&FileReceiver::handleIncomingFileTransfer, this, _1));
+			Swift::logging = true;
+			client->getFileTransferManager()->startListeningOnPort(9999);
+			client->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileReceiver::handleIncomingFileTransfer, this, _1));
+			
+			DiscoInfo discoInfo;
+			discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
+			discoInfo.addFeature(DiscoInfo::JingleFeature);
+			discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+			discoInfo.addFeature(DiscoInfo::Bytestream);
+			discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+			discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+			client->getDiscoManager()->setCapsNode(CLIENT_NODE);
+			client->getDiscoManager()->setDiscoInfo(discoInfo);
+			client->getPresenceSender()->sendPresence(Presence::create());
 		}
 
 		void handleIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
+			SWIFT_LOG(debug) << "foo" << std::endl;
 			incomingFileTransfers.push_back(transfer);
 			transfer->accept(boost::make_shared<FileWriteBytestream>("out"));
 			//transfer->onFinished.connect(boost::bind(&FileReceiver::handleFileTransferFinished, this, _1));
@@ -100,6 +118,9 @@ class FileReceiver {
 		JingleSessionManager* jingleSessionManager;
 		IncomingFileTransferManager* incomingFileTransferManager;
 		std::vector<IncomingFileTransfer::ref> incomingFileTransfers;
+		DefaultLocalJingleTransportCandidateGeneratorFactory *localFactory;
+		DefaultRemoteJingleTransportCandidateSelectorFactory *remoteFactory;
+		SOCKS5BytestreamRegistry* bytestreamRegistry;
 };
 
 
diff --git a/Swiften/Examples/SendFile/SendFile.cpp b/Swiften/Examples/SendFile/SendFile.cpp
index 205b442..9b2105b 100644
--- a/Swiften/Examples/SendFile/SendFile.cpp
+++ b/Swiften/Examples/SendFile/SendFile.cpp
@@ -4,6 +4,7 @@
  * See Documentation/Licenses/GPLv3.txt for more information.
  */
 
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/bind.hpp>
 #include <boost/filesystem.hpp>
 #include <iostream>
@@ -20,6 +21,17 @@
 #include <Swiften/FileTransfer/FileReadBytestream.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 #include <Swiften/Network/BoostConnectionServer.h>
+#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Disco/EntityCapsManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/StringCodecs/MD5.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/FileTransfer/FileTransferManager.h>
 
 using namespace Swift;
 
@@ -30,45 +42,65 @@ int exitCode = 2;
 
 class FileSender {
 	public:
-		FileSender(const JID& jid, const std::string& password, const JID& recipient, const boost::filesystem::path& file, int port) : jid(jid), password(password), recipient(recipient), file(file), transfer(NULL) {
-			connectionServer = BoostConnectionServer::create(port, networkFactories.getIOServiceThread()->getIOService(), &eventLoop);
-			socksBytestreamServer = new SOCKS5BytestreamServer(connectionServer);
+	FileSender(const JID& jid, const std::string& password, const JID& recipient, const boost::filesystem::path& file) : jid(jid), password(password), recipient(recipient), file(file) {
 
 			client = new Swift::Client(jid, password, &networkFactories);
 			client->onConnected.connect(boost::bind(&FileSender::handleConnected, this));
 			client->onDisconnected.connect(boost::bind(&FileSender::handleDisconnected, this, _1));
-			//tracer = new ClientXMLTracer(client);
+			tracer = new ClientXMLTracer(client);
+			client->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileSender::handleCapsChanged, this, _1));
 		}
 
 		~FileSender() {
-			//delete tracer;
+			delete tracer;
 			client->onDisconnected.disconnect(boost::bind(&FileSender::handleDisconnected, this, _1));
 			client->onConnected.disconnect(boost::bind(&FileSender::handleConnected, this));
 			delete client;
-			delete socksBytestreamServer;
 		}
-
+		
 		void start() {
-			connectionServer->start();
-			socksBytestreamServer->start();
 			client->connect();
 		}
 
-		void stop() {
-			if (transfer) {
-				transfer->stop();
-			}
-			client->disconnect();
-			socksBytestreamServer->stop();
-			connectionServer->stop();
-		}
-
 	private:
 		void handleConnected() {
 			client->sendPresence(Presence::create());
+
+			client->getFileTransferManager()->startListeningOnPort(19999);
+			//ByteArray fileData;
+			//readByteArrayFromFile(fileData, file.string());
+			
+			// gather file information
+			/*StreamInitiationFileInfo fileInfo;
+			
+			fileInfo.setName(file.filename());
+			fileInfo.setSize(boost::filesystem::file_size(file));
+			fileInfo.setDescription("Some file!");
+			fileInfo.setDate(boost::posix_time::from_time_t(boost::filesystem::last_write_time(file)));*/
+			//fileInfo.setHash(Hexify::hexify(MD5::getHash(fileData)));
+			/*
 			transfer = new OutgoingSIFileTransfer("myid",	client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::shared_ptr<FileReadBytestream>(new FileReadBytestream(file)), client->getIQRouter(), socksBytestreamServer);
 			transfer->onFinished.connect(boost::bind(&FileSender::handleFileTransferFinished, this, _1));
 			transfer->start();
+			 */
+		}
+
+		void handleCapsChanged(JID jid) {
+			if (jid.toBare() == recipient) {
+				// create ReadBytestream from file
+				boost::shared_ptr<FileReadBytestream> fileStream = boost::make_shared<FileReadBytestream>(file);
+
+				outgoingFileTransfer = client->getFileTransferManager()->createOutgoingFileTransfer(recipient, file, "Some File!", fileStream);
+
+				if (outgoingFileTransfer) {
+					std::cout << "started FT" << std::endl;
+					outgoingFileTransfer->start();
+					// TODO: getting notified about FT status and end
+				} else {
+					std::cout << "[ ERROR ] " << recipient << " doesn't support any kind of file transfer!" << std::endl;
+					//client->disconnect();
+				}
+			}
 		}
 
 		void handleDisconnected(const boost::optional<ClientError>&) {
@@ -85,23 +117,23 @@ class FileSender {
 				exit(0);
 			}
 		}
-
+		
 		void exit(int code) {
 			exitCode = code;
-			stop();
 			eventLoop.stop();
 		}
 	
 	private:
 		BoostConnectionServer::ref connectionServer;
 		SOCKS5BytestreamServer* socksBytestreamServer;
+		SOCKS5BytestreamRegistry* registry;
+		OutgoingFileTransfer::ref outgoingFileTransfer;
 		JID jid;
 		std::string password;
 		JID recipient;
 		boost::filesystem::path file;
 		Client* client;
 		ClientXMLTracer* tracer;
-		OutgoingSIFileTransfer* transfer;
 };
 
 
@@ -113,9 +145,9 @@ int main(int argc, char* argv[]) {
 
 	JID sender(argv[1]);
 	JID recipient(argv[3]);
-	FileSender fileSender(sender, std::string(argv[2]), recipient, boost::filesystem::path(argv[4]), 8888);
+	Swift::logging = true;
+	FileSender fileSender(sender, std::string(argv[2]), recipient, boost::filesystem::path(argv[4]));
 	fileSender.start();
-
 	{
 		/*BoostTimer::ref timer(BoostTimer::create(30000, &MainBoostIOServiceThread::getInstance().getIOService()));
 		timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h
index 4704db6..a6945c3 100644
--- a/Swiften/FileTransfer/ByteArrayReadBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h
@@ -22,6 +22,8 @@ namespace Swift {
 					readSize = data.size() - position;
 				}
 				std::vector<unsigned char> result(data.begin() + position, data.begin() + position + readSize);
+
+				onRead(result);
 				position += readSize;
 				return result;
 			}
diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
index 6c360e6..ef97ed9 100644
--- a/Swiften/FileTransfer/ByteArrayWriteBytestream.h
+++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h
@@ -16,6 +16,7 @@ namespace Swift {
 
 			virtual void write(const std::vector<unsigned char>& bytes) {
 				data.insert(data.end(), bytes.begin(), bytes.end());
+				onWrite(bytes);
 			}
 
 			const std::vector<unsigned char>& getData() const {
diff --git a/Swiften/FileTransfer/ConnectivityManager.cpp b/Swiften/FileTransfer/ConnectivityManager.cpp
new file mode 100644
index 0000000..174d6ab
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "ConnectivityManager.h"
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+ConnectivityManager::ConnectivityManager(PlatformNATTraversalWorker* worker) : natTraversalWorker(worker) {
+
+}
+
+ConnectivityManager::~ConnectivityManager() {
+	std::set<int> leftOpenPorts = ports;
+	foreach(int port, leftOpenPorts) {
+		removeListeningPort(port);
+	}
+}
+
+void ConnectivityManager::addListeningPort(int port) {
+	ports.insert(port);
+	boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> getIPRequest = natTraversalWorker->createGetPublicIPRequest();
+	if (getIPRequest) {
+		getIPRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalGetPublicIPResult, this, _1));
+		getIPRequest->run();
+	}
+
+	boost::shared_ptr<PlatformNATTraversalForwardPortRequest> forwardPortRequest = natTraversalWorker->createForwardPortRequest(port, port);
+	if (forwardPortRequest) {
+		forwardPortRequest->onResult.connect(boost::bind(&ConnectivityManager::natTraversalForwardPortResult, this, _1));
+		forwardPortRequest->run();
+	}
+}
+
+void ConnectivityManager::removeListeningPort(int port) {
+	SWIFT_LOG(debug) << "remove listening port " << port << std::endl;
+	ports.erase(port);
+	boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> removePortForwardingRequest = natTraversalWorker->createRemovePortForwardingRequest(port, port);
+	if (removePortForwardingRequest) {
+		removePortForwardingRequest->run();
+	}
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getHostAddressPorts() const {
+	PlatformNetworkEnvironment env;
+	std::vector<HostAddressPort> results;
+
+	std::vector<HostAddress> addresses;
+
+	foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+		foreach (HostAddress address, iface->getAddresses()) {
+			foreach (int port, ports) {
+				results.push_back(HostAddressPort(address, port));
+			}
+		}
+	}
+
+	return results;
+}
+
+std::vector<HostAddressPort> ConnectivityManager::getAssistedHostAddressPorts() const {
+	std::vector<HostAddressPort> results;
+
+	if (publicAddress) {
+		foreach (int port, ports) {
+			results.push_back(HostAddressPort(publicAddress.get(), port));
+		}
+	}
+
+	return results;
+}
+
+void ConnectivityManager::natTraversalGetPublicIPResult(boost::optional<HostAddress> address) {
+	if (address) {
+		publicAddress = address;
+		SWIFT_LOG(debug) << "Public IP discovered as " << publicAddress.get().toString() << "." << std::endl;
+	} else {
+		SWIFT_LOG(debug) << "No public IP discoverable." << std::endl;
+	}
+}
+
+void ConnectivityManager::natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping) {
+	if (mapping) {
+		SWIFT_LOG(debug) << "Mapping port was successful." << std::endl;
+	} else {
+		SWIFT_LOG(debug) << "Mapping port has failed." << std::endl;
+	}
+}
+
+
+}
diff --git a/Swiften/FileTransfer/ConnectivityManager.h b/Swiften/FileTransfer/ConnectivityManager.h
new file mode 100644
index 0000000..87041b2
--- /dev/null
+++ b/Swiften/FileTransfer/ConnectivityManager.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class ConnectivityManager {
+public:
+	ConnectivityManager(PlatformNATTraversalWorker*);
+	~ConnectivityManager();
+public:
+	void addListeningPort(int port);
+	void removeListeningPort(int port);
+
+	std::vector<HostAddressPort> getHostAddressPorts() const;
+	std::vector<HostAddressPort> getAssistedHostAddressPorts() const;
+
+private:
+	void natTraversalGetPublicIPResult(boost::optional<HostAddress> address);
+	void natTraversalForwardPortResult(boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping> mapping);
+
+private:
+	PlatformNATTraversalWorker* natTraversalWorker;
+
+	std::set<int> ports;
+	boost::optional<HostAddress> publicAddress;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
new file mode 100644
index 0000000..5b6da4c
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGenerator.h"
+
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGenerator::DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGenerator::~DefaultLocalJingleTransportCandidateGenerator() {
+}
+
+void DefaultLocalJingleTransportCandidateGenerator::generateLocalTransportCandidates(JingleTransportPayload::ref transportPayload) {
+	if (boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload)) {
+		JingleTransportPayload::ref payL = boost::make_shared<JingleTransportPayload>();
+		payL->setSessionID(transportPayload->getSessionID());
+		onLocalTransportCandidatesGenerated(payL);
+	}
+	if (boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload)) {
+		JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+		payL->setSessionID(transportPayload->getSessionID());
+		payL->setMode(JingleS5BTransportPayload::TCPMode);
+
+		const unsigned long localPreference = 0;
+
+		// get direct candidates
+		std::vector<HostAddressPort> directCandidates = connectivityManager->getHostAddressPorts();
+		foreach(HostAddressPort addressPort, directCandidates) {
+			JingleS5BTransportPayload::Candidate candidate;
+			candidate.type = JingleS5BTransportPayload::Candidate::DirectType;
+			candidate.jid = ownJID;
+			candidate.hostPort = addressPort;
+			candidate.priority = 65536 * 126 + localPreference;
+			candidate.cid = idGenerator.generateID();
+			payL->addCandidate(candidate);
+		}
+
+		// get assissted candidates
+		std::vector<HostAddressPort> assisstedCandidates = connectivityManager->getAssistedHostAddressPorts();
+		foreach(HostAddressPort addressPort, assisstedCandidates) {
+			JingleS5BTransportPayload::Candidate candidate;
+			candidate.type = JingleS5BTransportPayload::Candidate::AssistedType;
+			candidate.jid = ownJID;
+			candidate.hostPort = addressPort;
+			candidate.priority = 65536 * 120 + localPreference;
+			candidate.cid = idGenerator.generateID();
+			payL->addCandidate(candidate);
+		}
+
+		// get proxy candidates
+		std::vector<S5BProxyRequest::ref> proxyCandidates = s5bProxy->getS5BProxies();
+		foreach(S5BProxyRequest::ref proxy, proxyCandidates) {
+			JingleS5BTransportPayload::Candidate candidate;
+			candidate.type = JingleS5BTransportPayload::Candidate::ProxyType;
+			candidate.jid = proxy->getStreamHost().get().jid;
+			candidate.hostPort = proxy->getStreamHost().get().addressPort;
+			candidate.priority = 65536 * 10 + localPreference;
+			candidate.cid = idGenerator.generateID();
+			payL->addCandidate(candidate);
+		}
+
+		onLocalTransportCandidatesGenerated(payL);
+	}
+	
+}
+
+bool DefaultLocalJingleTransportCandidateGenerator::isActualCandidate(JingleTransportPayload::ref transportPayload) {
+	if (!transportPayload.get()) return false;
+	return false;
+}
+
+int DefaultLocalJingleTransportCandidateGenerator::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+	return 0;
+}
+
+JingleTransport::ref DefaultLocalJingleTransportCandidateGenerator::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+	return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
new file mode 100644
index 0000000..7d45491
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+class ConnectivityManager;
+
+class DefaultLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+	DefaultLocalJingleTransportCandidateGenerator(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, JID& ownJID);
+	virtual ~DefaultLocalJingleTransportCandidateGenerator();
+
+	virtual void generateLocalTransportCandidates(JingleTransportPayload::ref);
+
+	virtual bool isActualCandidate(JingleTransportPayload::ref);
+	virtual int getPriority(JingleTransportPayload::ref);
+	virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+	IDGenerator idGenerator;
+	ConnectivityManager* connectivityManager;
+	SOCKS5BytestreamRegistry* s5bRegistry;
+	SOCKS5BytestreamProxy* s5bProxy;
+	JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
new file mode 100644
index 0000000..ed0386e
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultLocalJingleTransportCandidateGeneratorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID) : connectivityManager(connectivityManager), s5bRegistry(s5bRegistry), s5bProxy(s5bProxy), ownJID(ownJID) {
+}
+
+DefaultLocalJingleTransportCandidateGeneratorFactory::~DefaultLocalJingleTransportCandidateGeneratorFactory() {
+}
+
+LocalJingleTransportCandidateGenerator* DefaultLocalJingleTransportCandidateGeneratorFactory::createCandidateGenerator() {
+	return new DefaultLocalJingleTransportCandidateGenerator(connectivityManager, s5bRegistry, s5bProxy, ownJID);
+}
+
+
+}
diff --git a/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
new file mode 100644
index 0000000..511d0a1
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class ConnectivityManager;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class DefaultLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory{
+public:
+	DefaultLocalJingleTransportCandidateGeneratorFactory(ConnectivityManager* connectivityManager, SOCKS5BytestreamRegistry* s5bRegistry, SOCKS5BytestreamProxy* s5bProxy, const JID& ownJID);
+	virtual ~DefaultLocalJingleTransportCandidateGeneratorFactory();
+	
+	LocalJingleTransportCandidateGenerator* createCandidateGenerator();
+
+private:
+	ConnectivityManager* connectivityManager;
+	SOCKS5BytestreamRegistry* s5bRegistry;
+	SOCKS5BytestreamProxy* s5bProxy;
+	JID ownJID;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
new file mode 100644
index 0000000..32b4df8
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelector.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelector::DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelector::~DefaultRemoteJingleTransportCandidateSelector() {
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::addRemoteTransportCandidates(JingleTransportPayload::ref transportPayload) {
+	JingleS5BTransportPayload::ref s5bPayload;
+	transportSID = transportPayload->getSessionID();
+	if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+		foreach(JingleS5BTransportPayload::Candidate c,  s5bPayload->getCandidates()) {
+			candidates.push(c);
+		}
+	}
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::selectCandidate() {
+	tryNextCandidate(true);
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate(bool error) {
+	if (error) {
+		if (s5bSession) {
+			SWIFT_LOG(debug) << "failed to connect" << std::endl;
+		}
+		if (candidates.empty()) {
+			// failed to connect to any of the candidates
+			// issue an error
+			SWIFT_LOG(debug) << "out of candidates )=" << std::endl;
+			JingleS5BTransportPayload::ref failed = boost::make_shared<JingleS5BTransportPayload>();
+			failed->setCandidateError(true);
+			failed->setSessionID(transportSID);
+			onRemoteTransportCandidateSelectFinished(failed);
+		} else {
+			lastCandidate = candidates.top();
+			// only try direct or assisted for now
+			if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType ||
+				lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType || lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType ) {
+				// create connection
+				connection = connectionFactory->createConnection();
+				s5bSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, lastCandidate.hostPort, SOCKS5BytestreamRegistry::getHostname(transportSID, requester, target), timerFactory);
+
+				// bind onReady to this method
+				s5bSession->onSessionReady.connect(boost::bind(&DefaultRemoteJingleTransportCandidateSelector::tryNextCandidate, this, _1));
+
+				std::string candidateType;
+				if (lastCandidate.type == JingleS5BTransportPayload::Candidate::DirectType) {
+					candidateType = "direct";
+				} else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::AssistedType) {
+					candidateType = "assisted";
+				} else if (lastCandidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+					candidateType = "proxy";
+				}
+
+				// initiate connect
+				SWIFT_LOG(debug) << "try to connect to candidate of type " << candidateType << " : " << lastCandidate.hostPort.toString() << std::endl;
+				s5bSession->start();
+
+				// that's it. we're gonna be called back
+				candidates.pop();
+			} else {
+				s5bSession.reset();
+				candidates.pop();
+				tryNextCandidate(true);
+			}
+		}
+	} else {
+		// we have a working connection, hooray
+		JingleS5BTransportPayload::ref success = boost::make_shared<JingleS5BTransportPayload>();
+		success->setCandidateUsed(lastCandidate.cid);
+		success->setSessionID(transportSID);
+		onRemoteTransportCandidateSelectFinished(success);
+	}
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setMinimumPriority(int priority) {
+	SWIFT_LOG(debug) << "priority: " << priority << std::endl;
+}
+
+void DefaultRemoteJingleTransportCandidateSelector::setRequesterTargtet(const JID& requester, const JID& target) {
+	this->requester = requester;
+	this->target = target;
+}
+
+SOCKS5BytestreamClientSession::ref DefaultRemoteJingleTransportCandidateSelector::getS5BSession() {
+	return s5bSession;
+}
+
+bool DefaultRemoteJingleTransportCandidateSelector::isActualCandidate(JingleTransportPayload::ref /* transportPayload */) {
+	return false;
+}
+
+int DefaultRemoteJingleTransportCandidateSelector::getPriority(JingleTransportPayload::ref /* transportPayload */) {
+	return 0;
+}
+
+JingleTransport::ref DefaultRemoteJingleTransportCandidateSelector::selectTransport(JingleTransportPayload::ref /* transportPayload */) {
+	return JingleTransport::ref();
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
new file mode 100644
index 0000000..255acd9
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <queue>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+public:
+	DefaultRemoteJingleTransportCandidateSelector(ConnectionFactory*, TimerFactory*);
+	virtual ~DefaultRemoteJingleTransportCandidateSelector();
+
+	virtual void addRemoteTransportCandidates(JingleTransportPayload::ref);
+	virtual void selectCandidate();
+	virtual void setMinimumPriority(int);
+	void setRequesterTargtet(const JID& requester, const JID& target);
+	virtual SOCKS5BytestreamClientSession::ref getS5BSession();
+
+	virtual bool isActualCandidate(JingleTransportPayload::ref);
+	virtual int getPriority(JingleTransportPayload::ref);
+	virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref);
+
+private:
+	void tryNextCandidate(bool error);
+
+private:
+	ConnectionFactory* connectionFactory;
+	TimerFactory* timerFactory;
+
+	std::priority_queue<JingleS5BTransportPayload::Candidate, std::vector<JingleS5BTransportPayload::Candidate>, JingleS5BTransportPayload::CompareCandidate> candidates;
+
+	std::string transportSID;
+	boost::shared_ptr<Connection> connection;
+	boost::shared_ptr<SOCKS5BytestreamClientSession> s5bSession;
+	JingleS5BTransportPayload::Candidate lastCandidate;
+	JID requester;
+	JID target;
+};
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
new file mode 100644
index 0000000..8ebbf46
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "DefaultRemoteJingleTransportCandidateSelectorFactory.h"
+
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelector.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : connectionFactory(connectionFactory), timerFactory(timerFactory) {
+}
+
+DefaultRemoteJingleTransportCandidateSelectorFactory::~DefaultRemoteJingleTransportCandidateSelectorFactory() {
+}
+
+RemoteJingleTransportCandidateSelector* DefaultRemoteJingleTransportCandidateSelectorFactory::createCandidateSelector() {
+	return new DefaultRemoteJingleTransportCandidateSelector(connectionFactory, timerFactory);
+}
+
+}
diff --git a/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
new file mode 100644
index 0000000..ca29e1f
--- /dev/null
+++ b/Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+
+namespace Swift {
+
+class ConnectionFactory;
+class TimerFactory;
+
+class DefaultRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+	DefaultRemoteJingleTransportCandidateSelectorFactory(ConnectionFactory*, TimerFactory*);
+	virtual ~DefaultRemoteJingleTransportCandidateSelectorFactory();
+
+	RemoteJingleTransportCandidateSelector* createCandidateSelector();
+
+private:
+	ConnectionFactory* connectionFactory;
+	TimerFactory* timerFactory;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileReadBytestream.cpp b/Swiften/FileTransfer/FileReadBytestream.cpp
index 84d6490..f0139b8 100644
--- a/Swiften/FileTransfer/FileReadBytestream.cpp
+++ b/Swiften/FileTransfer/FileReadBytestream.cpp
@@ -30,6 +30,7 @@ std::vector<unsigned char> FileReadBytestream::read(size_t size)  {
 	assert(stream->good());
 	stream->read(reinterpret_cast<char*>(&result[0]), size);
 	result.resize(stream->gcount());
+	onRead(result);
 	return result;
 }
 
diff --git a/Swiften/FileTransfer/FileTransfer.h b/Swiften/FileTransfer/FileTransfer.h
new file mode 100644
index 0000000..6c37d8d
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransfer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+
+namespace Swift {
+
+class FileTransfer {
+public:
+	struct State {
+		enum FTState {
+			Canceled,
+			Failed,
+			Finished,
+			Negotiating,
+			Transferring,
+			WaitingForStart,
+			WaitingForAccept,
+		};
+
+		FTState state;
+		std::string message;
+
+		State(FTState state) : state(state), message("") {}
+		State(FTState state, std::string message) : state(state), message(message) {}
+	};
+
+public:
+	typedef boost::shared_ptr<FileTransfer> ref;
+
+public:
+	uintmax_t fileSizeInBytes;
+	std::string filename;
+	std::string algo;
+	std::string hash;
+
+public:
+	virtual void cancel() = 0;
+
+public:
+	boost::signal<void (int /* proccessedBytes */)> onProcessedBytes;
+	boost::signal<void (State)> onStateChange;
+	boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+
+public:
+	virtual ~FileTransfer() {}
+};
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.cpp b/Swiften/FileTransfer/FileTransferManager.cpp
new file mode 100644
index 0000000..69be852
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+
+FileTransferManager::~FileTransferManager() {
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManager.h b/Swiften/FileTransfer/FileTransferManager.h
new file mode 100644
index 0000000..3a1628f
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManager.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+
+namespace Swift {
+	class ReadBytestream;
+	class S5BProxyRequest;
+
+	class FileTransferManager {
+		public:
+			virtual ~FileTransferManager();
+			
+			virtual void startListeningOnPort(int port) = 0;
+			virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest> proxy) = 0;
+
+			virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+			virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) = 0;
+			
+			boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
+	};
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.cpp b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
new file mode 100644
index 0000000..f89a3e9
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/FileTransferManagerImpl.h>
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include "Swiften/Disco/EntityCapsProvider.h"
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/FileTransfer/ConnectivityManager.h>
+#include <Swiften/FileTransfer/OutgoingFileTransferManager.h>
+#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
+#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Presence/PresenceOracle.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/ConnectionServerFactory.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+
+namespace Swift {
+
+FileTransferManagerImpl::FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker) : ownJID(ownFullJID), jingleSM(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), presenceOracle(presOracle), timerFactory(timerFactory), connectionFactory(connectionFactory), connectionServerFactory(connectionServerFactory), natTraversalWorker(natTraversalWorker), bytestreamServer(NULL) {
+	assert(!ownFullJID.isBare());
+
+	connectivityManager = new ConnectivityManager(natTraversalWorker);
+	bytestreamRegistry = new SOCKS5BytestreamRegistry();
+	bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+	localCandidateGeneratorFactory = new DefaultLocalJingleTransportCandidateGeneratorFactory(connectivityManager, bytestreamRegistry, bytestreamProxy, ownFullJID);
+	remoteCandidateSelectorFactory = new DefaultRemoteJingleTransportCandidateSelectorFactory(connectionFactory, timerFactory);
+	outgoingFTManager = new OutgoingFileTransferManager(ownJID, jingleSM, iqRouter, capsProvider, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy);
+	incomingFTManager = new IncomingFileTransferManager(ownJID, jingleSM, iqRouter, remoteCandidateSelectorFactory, localCandidateGeneratorFactory, bytestreamRegistry, bytestreamProxy, timerFactory);
+	incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer);
+}
+
+FileTransferManagerImpl::~FileTransferManagerImpl() {
+	if (bytestreamServer) {
+		bytestreamServer->stop();
+	}
+	delete incomingFTManager;
+	delete outgoingFTManager;
+	delete remoteCandidateSelectorFactory;
+	delete localCandidateGeneratorFactory;
+	delete connectivityManager;
+}
+
+void FileTransferManagerImpl::startListeningOnPort(int port) {
+	// TODO: create a server for each interface we're on
+	SWIFT_LOG(debug) << "Start listening on port " << port << " and hope it's not in use." << std::endl;
+	boost::shared_ptr<ConnectionServer> server = connectionServerFactory->createConnectionServer(HostAddress("0.0.0.0"), port);
+	server->start();
+	bytestreamServer = new SOCKS5BytestreamServer(server, bytestreamRegistry);
+	bytestreamServer->start();
+	connectivityManager->addListeningPort(port);
+}
+
+void FileTransferManagerImpl::addS5BProxy(S5BProxyRequest::ref proxy) {
+	bytestreamProxy->addS5BProxy(proxy);
+}
+
+boost::optional<JID> FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) {
+	JID fullReceipientJID;
+	int priority = INT_MIN;
+	
+	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
+	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
+
+	//iterate over them
+	foreach(Presence::ref pres, presences) {
+		if (pres->getPriority() > priority) {
+			// look up caps from the jid
+			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
+			if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
+				info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
+			
+				priority = pres->getPriority();
+				fullReceipientJID = pres->getFrom();
+			}
+		}
+	}
+	
+	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream) {
+	std::string filename = filepath.filename();
+	uintmax_t sizeInBytes = boost::filesystem::file_size(filepath);
+	boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath));
+	return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream);
+}
+
+OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream) {
+	StreamInitiationFileInfo fileInfo;
+	fileInfo.setDate(lastModified);
+	fileInfo.setSize(sizeInBytes);
+	fileInfo.setName(filename);
+	fileInfo.setDescription(description);
+	
+	JID receipient = to;
+	
+	if(receipient.isBare()) {
+		boost::optional<JID> fullJID = highestPriorityJIDSupportingFileTransfer(receipient);
+		if (fullJID.is_initialized()) {
+			receipient = fullJID.get();
+		} else {
+			return OutgoingFileTransfer::ref();
+		}
+	}
+	
+	return outgoingFTManager->createOutgoingFileTransfer(receipient, bytestream, fileInfo);
+}
+
+}
diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.h b/Swiften/FileTransfer/FileTransferManagerImpl.h
new file mode 100644
index 0000000..b38eaea
--- /dev/null
+++ b/Swiften/FileTransfer/FileTransferManagerImpl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/IncomingFileTransfer.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+
+namespace Swift {
+
+class Client;
+class ConnectionFactory;
+class ConnectionServerFactory;
+class ConnectivityManager;
+class EntityCapsProvider;
+class IQRouter;
+class IncomingFileTransferManager;
+class JingleSessionManager;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransferManager;
+class PlatformNATTraversalWorker;
+class PresenceOracle;
+class ReadBytestream;
+class RemoteJingleTransportCandidateSelectorFactory;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamServer;
+class SOCKS5BytestreamProxy;
+class TimerFactory;
+
+class FileTransferManagerImpl : public FileTransferManager {
+public:
+	FileTransferManagerImpl(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, PlatformNATTraversalWorker* natTraversalWorker);
+	~FileTransferManagerImpl();
+	
+	void startListeningOnPort(int port);
+	void addS5BProxy(S5BProxyRequest::ref proxy);
+
+	OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const boost::filesystem::path& filepath, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream);
+	OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, boost::shared_ptr<ReadBytestream> bytestream);
+	
+ private:
+	boost::optional<JID> highestPriorityJIDSupportingFileTransfer(const JID& bareJID);
+	
+private:
+	JID ownJID;
+	
+	OutgoingFileTransferManager* outgoingFTManager;
+	IncomingFileTransferManager* incomingFTManager;
+	RemoteJingleTransportCandidateSelectorFactory* remoteCandidateSelectorFactory;
+	LocalJingleTransportCandidateGeneratorFactory* localCandidateGeneratorFactory;
+	JingleSessionManager* jingleSM;
+	IQRouter* iqRouter;
+	EntityCapsProvider* capsProvider;
+	PresenceOracle* presenceOracle;
+
+	TimerFactory* timerFactory;
+	ConnectionFactory* connectionFactory;
+	ConnectionServerFactory* connectionServerFactory;
+	PlatformNATTraversalWorker* natTraversalWorker;
+	SOCKS5BytestreamRegistry* bytestreamRegistry;
+	SOCKS5BytestreamServer* bytestreamServer;
+	SOCKS5BytestreamProxy* bytestreamProxy;
+	ConnectivityManager* connectivityManager;
+};
+
+}
diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp
index c38338a..6a22c6a 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.cpp
+++ b/Swiften/FileTransfer/FileWriteBytestream.cpp
@@ -27,6 +27,14 @@ void FileWriteBytestream::write(const std::vector<unsigned char>& data) {
 	}
 	assert(stream->good());
 	stream->write(reinterpret_cast<const char*>(&data[0]), data.size());
+	onWrite(data);
+}
+
+void FileWriteBytestream::close() {
+	if (stream) {
+		stream->close();
+		stream = NULL;
+	}
 }
 
 }
diff --git a/Swiften/FileTransfer/FileWriteBytestream.h b/Swiften/FileTransfer/FileWriteBytestream.h
index 8cfa718..82c4a65 100644
--- a/Swiften/FileTransfer/FileWriteBytestream.h
+++ b/Swiften/FileTransfer/FileWriteBytestream.h
@@ -18,6 +18,7 @@ namespace Swift {
 			~FileWriteBytestream();
 
 			virtual void write(const std::vector<unsigned char>&);
+			void close();
 
 		private:
 			boost::filesystem::path file;
diff --git a/Swiften/FileTransfer/IBBReceiveSession.cpp b/Swiften/FileTransfer/IBBReceiveSession.cpp
index 566dcca..43c26be 100644
--- a/Swiften/FileTransfer/IBBReceiveSession.cpp
+++ b/Swiften/FileTransfer/IBBReceiveSession.cpp
@@ -14,6 +14,8 @@
 #include <Swiften/FileTransfer/BytestreamException.h>
 #include <Swiften/Queries/SetResponder.h>
 
+#include <cassert>
+
 namespace Swift {
 
 class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
@@ -43,14 +45,17 @@ class IBBReceiveSession::IBBResponder : public SetResponder<IBB> {
 					}
 				}
 				else if (ibb->getAction() == IBB::Open) {
+					SWIFT_LOG(debug) << "IBB open received" << std::endl;
 					sendResponse(from, id, IBB::ref());
 				}
 				else if (ibb->getAction() == IBB::Close) {
+					SWIFT_LOG(debug) << "IBB close received" << std::endl;
 					sendResponse(from, id, IBB::ref());
 					session->finish(FileTransferError(FileTransferError::ClosedError));
 				}
 				return true;
 			}
+			SWIFT_LOG(debug) << "wrong from/sessionID: " << from << " == " << session->from << " / " <<ibb->getStreamID() << " == " << session->id << std::endl;
 			return false;
 		}
 
@@ -71,6 +76,8 @@ IBBReceiveSession::IBBReceiveSession(
 			size(size), 
 			router(router), 
 			active(false) {
+	assert(!id.empty());
+	assert(from.isValid());
 	responder = new IBBResponder(this, router);
 }
 
@@ -82,11 +89,13 @@ IBBReceiveSession::~IBBReceiveSession() {
 }
 
 void IBBReceiveSession::start() {
+	SWIFT_LOG(debug) << "receive session started" << std::endl;
 	active = true;
 	responder->start();
 }
 
 void IBBReceiveSession::stop() {
+	SWIFT_LOG(debug) << "receive session stopped" << std::endl;
 	responder->stop();
 	if (active) {
 		if (router->isAvailable()) {
diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp
index 5376276..a434cfb 100644
--- a/Swiften/FileTransfer/IBBSendSession.cpp
+++ b/Swiften/FileTransfer/IBBSendSession.cpp
@@ -35,7 +35,7 @@ void IBBSendSession::stop() {
 }
 
 void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
-	if (!error) {
+	if (!error && active) {
 		if (!bytestream->isFinished()) {
 			try {
 				std::vector<unsigned char> data = bytestream->read(blockSize);
@@ -43,6 +43,7 @@ void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {
 				sequenceNumber++;
 				request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));
 				request->send();
+				onBytesSent(data.size());
 			}
 			catch (const BytestreamException&) {
 				finish(FileTransferError(FileTransferError::ReadError));
diff --git a/Swiften/FileTransfer/IBBSendSession.h b/Swiften/FileTransfer/IBBSendSession.h
index 6c741cf..325f66c 100644
--- a/Swiften/FileTransfer/IBBSendSession.h
+++ b/Swiften/FileTransfer/IBBSendSession.h
@@ -32,7 +32,7 @@ namespace Swift {
 			}
 
 			boost::signal<void (boost::optional<FileTransferError>)> onFinished;
-
+			boost::signal<void (int)> onBytesSent;
 		private:
 			void handleIBBResponse(IBB::ref, ErrorPayload::ref);
 			void finish(boost::optional<FileTransferError>);
diff --git a/Swiften/FileTransfer/IncomingFileTransfer.h b/Swiften/FileTransfer/IncomingFileTransfer.h
index 1ccd76c..a6cf05e 100644
--- a/Swiften/FileTransfer/IncomingFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingFileTransfer.h
@@ -9,15 +9,18 @@
 #include <boost/shared_ptr.hpp>
 
 #include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
 #include <Swiften/FileTransfer/WriteBytestream.h>
 
 namespace Swift {
-	class IncomingFileTransfer {
+	class IncomingFileTransfer : public FileTransfer {
 		public:
 			typedef boost::shared_ptr<IncomingFileTransfer> ref;
 
 			virtual ~IncomingFileTransfer();
 			
 			virtual void accept(WriteBytestream::ref) = 0;
+			virtual const JID& getSender() const = 0;
 	};
 }
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
index 79d2391..c01c906 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp
@@ -18,7 +18,9 @@
 
 namespace Swift {
 
-IncomingFileTransferManager::IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router) : jingleSessionManager(jingleSessionManager), router(router) {
+IncomingFileTransferManager::IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router,
+							RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+														 LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory) : ourJID(ourFullJID), jingleSessionManager(jingleSessionManager), router(router), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), timerFactory(timerFactory) {
 	jingleSessionManager->addIncomingSessionHandler(this);
 }
 
@@ -29,13 +31,19 @@ IncomingFileTransferManager::~IncomingFileTransferManager() {
 bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents) {
 	if (JingleContentPayload::ref content = Jingle::getContentWithDescription<JingleFileTransferDescription>(contents)) {
 		if (content->getTransport<JingleIBBTransportPayload>() || content->getTransport<JingleS5BTransportPayload>()) {
-			RemoteJingleTransportCandidateSelectorFactory* a;
-			LocalJingleTransportCandidateGeneratorFactory* b;
-			IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session, content, a, b, router);
-			onIncomingFileTransfer(transfer);
+
+			JingleFileTransferDescription::ref description = content->getDescription<JingleFileTransferDescription>();
+
+			if (description && description->getOffers().size() == 1) {
+				IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(ourJID, session, content, remoteFactory, localFactory, router, bytestreamRegistry, bytestreamProxy, timerFactory);
+				onIncomingFileTransfer(transfer);
+			} else {
+				std::cerr << "Received a file-transfer request with no description or more than one file!" << std::endl;
+				session->sendTerminate(JinglePayload::Reason::FailedApplication);
+			}
 		}
 		else {
-			session->terminate(JinglePayload::Reason::UnsupportedTransports);
+			session->sendTerminate(JinglePayload::Reason::UnsupportedTransports);
 		}
 		return true;
 	}
diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.h b/Swiften/FileTransfer/IncomingFileTransferManager.h
index 428a838..d1573f6 100644
--- a/Swiften/FileTransfer/IncomingFileTransferManager.h
+++ b/Swiften/FileTransfer/IncomingFileTransferManager.h
@@ -17,10 +17,13 @@ namespace Swift {
 	class JingleSessionManager;
 	class RemoteJingleTransportCandidateSelectorFactory;
 	class LocalJingleTransportCandidateGeneratorFactory;
+	class SOCKS5BytestreamRegistry;
+	class SOCKS5BytestreamProxy;
+	class TimerFactory;
 
 	class IncomingFileTransferManager : public IncomingJingleSessionHandler {
 		public:
-			IncomingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router);
+			IncomingFileTransferManager(const JID& ourFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, TimerFactory* timerFactory);
 			~IncomingFileTransferManager();
 
 			boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;
@@ -29,7 +32,13 @@ namespace Swift {
 			bool handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents);
 
 		private:
+			JID ourJID;
 			JingleSessionManager* jingleSessionManager;
 			IQRouter* router;
+			RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+			LocalJingleTransportCandidateGeneratorFactory* localFactory;
+			SOCKS5BytestreamRegistry* bytestreamRegistry;
+			SOCKS5BytestreamProxy* bytestreamProxy;
+			TimerFactory* timerFactory;
 	};
 }
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
index 904b53e..1189830 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp
@@ -9,29 +9,47 @@
 #include <boost/bind.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
-#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
-#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
 #include <Swiften/Elements/JingleIBBTransportPayload.h>
 #include <Swiften/Elements/JingleS5BTransportPayload.h>
-#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
 #include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Queries/GenericRequest.h>
 
 namespace Swift {
 
 IncomingJingleFileTransfer::IncomingJingleFileTransfer(
+		const JID& ourJID,
 		JingleSession::ref session,
 		JingleContentPayload::ref content,
 		RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory,
 		LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory,
-		IQRouter* router) :
+		IQRouter* router,
+		SOCKS5BytestreamRegistry* registry,
+		SOCKS5BytestreamProxy* proxy,
+		TimerFactory* timerFactory) :
+			ourJID(ourJID),
 			session(session),
 			router(router),
+			timerFactory(timerFactory),
 			initialContent(content),
-			contentID(content->getName(), content->getCreator()),
 			state(Initial),
+			receivedBytes(0),
+			s5bRegistry(registry),
+			s5bProxy(proxy),
 			remoteTransportCandidateSelectFinished(false),
-			localTransportCandidateSelectFinished(false) {
+			localTransportCandidateSelectFinished(false),
+			serverSession(0) {
 	
 	candidateSelector = candidateSelectorFactory->createCandidateSelector();
 	candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
@@ -41,13 +59,26 @@ IncomingJingleFileTransfer::IncomingJingleFileTransfer(
 
 	session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
 	session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
-	session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
+	session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+	session->onSessionInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionInfoReceived, this, _1));
 
 	description = initialContent->getDescription<JingleFileTransferDescription>();
 	assert(description);
+	assert(description->getOffers().size() == 1);
+	StreamInitiationFileInfo fileInfo = description->getOffers().front();
+	fileSizeInBytes = fileInfo.getSize();
+	filename = fileInfo.getName();
+	hash = fileInfo.getHash();
+	algo = fileInfo.getAlgo();
+
+	waitOnHashTimer = timerFactory->createTimer(5000);
+	waitOnHashTimer->onTick.connect(boost::bind(&IncomingJingleFileTransfer::finishOffTransfer, this));
 }
 
 IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
+	stream->onWrite.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+	delete hashCalculator;
+
 	session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this));
 	session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2));
 	session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
@@ -60,44 +91,87 @@ IncomingJingleFileTransfer::~IncomingJingleFileTransfer() {
 }
 
 void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) {
-	assert(!stream);
+	assert(!this->stream);
 	this->stream = stream;
 
+	hashCalculator = new IncrementalBytestreamHashCalculator( algo == "md5" || hash.empty() , algo == "sha-1" || hash.empty() );
+	stream->onWrite.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+	stream->onWrite.connect(boost::bind(&IncomingJingleFileTransfer::handleWriteStreamDataReceived, this, _1));
+	onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
 	if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) {
+		SWIFT_LOG(debug) << "Got IBB transport payload!" << std::endl;
 		setActiveTransport(createIBBTransport(ibbTransport));
-		session->accept();
+		session->sendAccept(getContentID(), initialContent->getDescriptions()[0], ibbTransport);
 	}
 	else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) {
+		SWIFT_LOG(debug) << "Got S5B transport payload!" << std::endl;
 		state = CreatingInitialTransports;
+		s5bSessionID = s5bTransport->getSessionID().empty() ? idGenerator.generateID() : s5bTransport->getSessionID();
+		s5bDestination = SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator());
+		s5bRegistry->addWriteBytestream(s5bDestination, stream);
+		fillCandidateMap(theirCandidates, s5bTransport);
 		candidateSelector->addRemoteTransportCandidates(s5bTransport);
-		candidateGenerator->generateLocalTransportCandidates();
+		candidateSelector->setRequesterTargtet(session->getInitiator(), ourJID);
+		s5bTransport->setSessionID(s5bSessionID);
+		candidateGenerator->generateLocalTransportCandidates(s5bTransport);
 	}
 	else {
 		assert(false);
 	}
 }
 
+const JID& IncomingJingleFileTransfer::getSender() const {
+	return session->getInitiator();
+}
+
+void IncomingJingleFileTransfer::cancel() {
+	session->sendTerminate(JinglePayload::Reason::Cancel);
+
+	if (activeTransport) activeTransport->stop();
+	if (serverSession) serverSession->stop();
+	if (clientSession) clientSession->stop();
+	onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
 
 void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) {
 	if (state == CreatingInitialTransports) {
-		if (!candidates) {
-			localTransportCandidateSelectFinished = true;
+		if (JingleS5BTransportPayload::ref s5bCandidates = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(candidates)) {
+			//localTransportCandidateSelectFinished = true;
+			//JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+			//emptyCandidates->setSessionID(s5bCandidates->getSessionID());
+			fillCandidateMap(ourCandidates, s5bCandidates);
+			session->sendAccept(getContentID(), initialContent->getDescriptions()[0], s5bCandidates);
+
+			state = NegotiatingTransport;
+			candidateSelector->selectCandidate();
 		}
-		session->accept(candidates);
-		state = NegotiatingTransport;
-		candidateSelector->selectCandidate();
+	}
+	else {
+		SWIFT_LOG(debug) << "Unhandled state!" << std::endl;
 	}
 }
 
 
 void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) {
-	remoteTransportCandidateSelectFinished = true;
-	selectedRemoteTransportCandidate = transport;
-	session->sendTransportInfo(contentID, transport);
-	checkCandidateSelected();
+	SWIFT_LOG(debug) << std::endl;
+	if (state == Terminated) {
+		return;
+	}
+	if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+		//remoteTransportCandidateSelectFinished = true;
+		//selectedRemoteTransportCandidate = transport;
+		ourCandidate = s5bPayload;
+		//checkCandidateSelected();
+		decideOnUsedTransport();
+		session->sendTransportInfo(getContentID(), s5bPayload);
+	}
+	else {
+		SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+	}
 }
 
 void IncomingJingleFileTransfer::checkCandidateSelected() {
+	assert(false);
 	if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) {
 		if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) {
 			if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) {
@@ -121,37 +195,293 @@ void IncomingJingleFileTransfer::checkCandidateSelected() {
 
 void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) {
 	state = Transferring;
+	onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
 	activeTransport = transport;
 	activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1));
+	activeTransport->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
 	activeTransport->start();
 }
 
-void IncomingJingleFileTransfer::handleSessionTerminateReceived() {
-	// TODO
+bool IncomingJingleFileTransfer::verifyReceviedData() {
+	if (algo.empty() || hash.empty()) {
+		SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+		return true;
+	} else {
+		if (algo == "sha-1") {
+			SWIFT_LOG(debug) << "verify data via SHA-1 hash: " << (hash == hashCalculator->getSHA1String()) << std::endl;
+			return hash == hashCalculator->getSHA1String();
+		}
+		else if (algo == "md5") {
+			SWIFT_LOG(debug) << "verify data via MD5 hash: " << (hash == hashCalculator->getMD5String()) << std::endl;
+			return hash == hashCalculator->getMD5String();
+		}
+		else {
+			SWIFT_LOG(debug) << "no verification possible, skipping" << std::endl;
+			return true;
+		}
+	}
+}
+
+void IncomingJingleFileTransfer::finishOffTransfer() {
+	if (verifyReceviedData()) {
+		onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+		session->sendTerminate(JinglePayload::Reason::Success);
+	} else {
+		onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+		session->sendTerminate(JinglePayload::Reason::MediaError);
+	}
+	state = Terminated;
+	waitOnHashTimer->stop();
+}
+
+void IncomingJingleFileTransfer::handleSessionInfoReceived(JinglePayload::ref jinglePayload) {
+	if (state == Terminated) {
+		return;
+	}
+	JingleFileTransferHash::ref transferHash = jinglePayload->getPayload<JingleFileTransferHash>();
+	if (transferHash) {
+		SWIFT_LOG(debug) << "Recevied hash information." << std::endl;
+		if (transferHash->getHashes().find("sha-1") != transferHash->getHashes().end()) {
+			algo = "sha-1";
+			hash = transferHash->getHashes().at("sha-1");
+		}
+		else if (transferHash->getHashes().find("md5") != transferHash->getHashes().end()) {
+			algo = "md5";
+			hash = transferHash->getHashes().at("md5");
+		}
+		checkIfAllDataReceived();
+	}
+}
+
+void IncomingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+	SWIFT_LOG(debug) << "session terminate received" << std::endl;
+	if (activeTransport) activeTransport->stop();
+	if (reason && reason.get().type == JinglePayload::Reason::Cancel) {
+		onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Other user canceled the transfer."));
+	}
+	else if (reason && reason.get().type == JinglePayload::Reason::Success) {
+		/*if (verifyReceviedData()) {
+			onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+		} else {
+			onStateChange(FileTransfer::State(FileTransfer::State::Failed, "Verification failed."));
+		}*/
+	}
 	state = Terminated;
 }
 
+void IncomingJingleFileTransfer::checkIfAllDataReceived() {
+	if (receivedBytes == fileSizeInBytes) {
+		SWIFT_LOG(debug) << "All data received." << std::endl;
+		if (hash.empty()) {
+			SWIFT_LOG(debug) << "No hash information yet. Waiting 5 seconds on hash info." << std::endl;
+			waitOnHashTimer->start();
+		} else {
+			SWIFT_LOG(debug) << "We already have hash info using " << algo << " algorithm. Finishing off transfer." << std::endl;
+			finishOffTransfer();
+		}
+	}
+	else if (receivedBytes > fileSizeInBytes) {
+		SWIFT_LOG(debug) << "We got more than we could handle!" << std::endl;
+	}
+}
+
 void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector<unsigned char>& data) {
+	SWIFT_LOG(debug) << data.size() << " bytes received" << std::endl;
+	onProcessedBytes(data.size());
 	stream->write(data);
+	receivedBytes += data.size();
+	checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::handleWriteStreamDataReceived(const std::vector<unsigned char>& data) {
+	receivedBytes += data.size();
+	checkIfAllDataReceived();
+}
+
+void IncomingJingleFileTransfer::useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+	SWIFT_LOG(debug) << std::endl;
+	if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+		// get proxy client session from remoteCandidateSelector
+		clientSession = candidateSelector->getS5BSession();
+
+		// wait on <activated/> transport-info
+	} else {
+		// ask s5b client
+		clientSession = candidateSelector->getS5BSession();
+		if (clientSession) {
+			state = Transferring;
+			SWIFT_LOG(debug) << clientSession->getAddressPort().toString() << std::endl;
+			clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+			clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+			clientSession->startReceiving(stream);
+		} else {
+			SWIFT_LOG(debug) << "No S5B client session found!!!" << std::endl;
+		}
+	}
 }
 
+void IncomingJingleFileTransfer::useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate) {
+	SWIFT_LOG(debug) << std::endl;
+
+	if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+		// get proxy client session from s5bRegistry
+		clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, ourJID, session->getInitiator()));
+		clientSession->onSessionReady.connect(boost::bind(&IncomingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+		clientSession->start();
+
+		// on reply send activate
+	} else {
+		// ask s5b server
+		serverSession = s5bRegistry->getConnectedSession(s5bDestination);
+		if (serverSession) {
+			state = Transferring;
+			serverSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+			serverSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+			serverSession->startTransfer();
+		} else {
+			SWIFT_LOG(debug) << "No S5B server session found!!!" << std::endl;
+		}
+	}
+}
+
+void IncomingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+	map.clear();
+	foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+		map[candidate.cid] = candidate;
+	}
+}
+
+
+void IncomingJingleFileTransfer::decideOnUsedTransport() {
+	if (ourCandidate && theirCandidate) {
+		if (ourCandidate->hasCandidateError() && theirCandidate->hasCandidateError()) {
+			state = WaitingForFallbackOrTerminate;
+			return;
+		}
+		std::string our_cid = ourCandidate->getCandidateUsed();
+		std::string their_cid = theirCandidate->getCandidateUsed();
+		if (ourCandidate->hasCandidateError() && !their_cid.empty()) {
+			useTheirCandidateChoiceForTransfer(ourCandidates[their_cid]);
+			onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+		}
+		else if (theirCandidate->hasCandidateError() && !our_cid.empty()) {
+			useOurCandidateChoiceForTransfer(theirCandidates[our_cid]);
+			onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+		}
+		else if (!our_cid.empty() && !their_cid.empty()) {
+			// compare priorites, if same they win
+			if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+				SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+				session->sendTerminate(JinglePayload::Reason::FailedTransport);
+				onStateChange(FileTransfer::State(FileTransfer::State::Canceled, "Failed to negotiate candidate."));
+				onFinished(FileTransferError(FileTransferError::PeerError));
+				return;
+			}
+
+			JingleS5BTransportPayload::Candidate our_candidate = theirCandidates[our_cid];
+			JingleS5BTransportPayload::Candidate their_candidate = ourCandidates[their_cid];
+			if (our_candidate.priority > their_candidate.priority) {
+				useOurCandidateChoiceForTransfer(our_candidate);
+			}
+			else if (our_candidate.priority < their_candidate.priority) {
+				useTheirCandidateChoiceForTransfer(their_candidate);
+			}
+			else {
+				useTheirCandidateChoiceForTransfer(their_candidate);
+			}
+			onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+		}
+		else {
+			assert(false);
+		}
+	} else {
+		SWIFT_LOG(debug) << "Can't make a transport decision yet." << std::endl;
+	}
+}
+
+void IncomingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+	if (error) {
+		// indicate proxy error
+	} else {
+		// activate proxy
+		activateProxySession(proxy);
+	}
+}
+
+void IncomingJingleFileTransfer::activateProxySession(const JID &proxy) {
+	S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+	proxyRequest->setSID(s5bSessionID);
+	proxyRequest->setActivate(session->getInitiator());
+
+	boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+	request->onResponse.connect(boost::bind(&IncomingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+	request->send();
+}
+
+void IncomingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+	SWIFT_LOG(debug) << std::endl;
+	if (error) {
+		SWIFT_LOG(debug) << "ERROR" << std::endl;
+	} else {
+		// send activated to other jingle party
+		JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+		proxyActivate->setActivated(theirCandidate->getCandidateUsed());
+		session->sendTransportInfo(getContentID(), proxyActivate);
+
+		// start transferring
+		clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+		clientSession->startReceiving(stream);
+		onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+	}
+}
 
 void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) {
-	localTransportCandidateSelectFinished = true;
+	SWIFT_LOG(debug) << "transport info received" << std::endl;
+	if (state == Terminated) {
+		return;
+	}
+	if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+		if (!s5bPayload->getActivated().empty()) {
+			if (ourCandidate->getCandidateUsed() == s5bPayload->getActivated()) {
+				clientSession->onBytesReceived.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+				clientSession->onFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleTransferFinished, this, _1));
+				clientSession->startReceiving(stream);
+				onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+			} else {
+				SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+				JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+				proxyError->setProxyError(true);
+				proxyError->setSessionID(s5bSessionID);
+				session->sendTransportInfo(getContentID(), proxyError);
+			}
+		} else {
+			theirCandidate = s5bPayload;
+			decideOnUsedTransport();
+		}
+	}
+	else {
+		SWIFT_LOG(debug) << "Expected something different here." << std::endl;
+	}
+	/*localTransportCandidateSelectFinished = true;
 	selectedLocalTransportCandidate = transport;
 	if (candidateGenerator->isActualCandidate(transport)) {
 		candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport));
-	}
-	checkCandidateSelected();
+	}*/
+	//checkCandidateSelected();
 }
 
 void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) {
+	if (state == Terminated) {
+		return;
+	}
 	if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+		SWIFT_LOG(debug) << "transport replaced with IBB" << std::endl;
 		setActiveTransport(createIBBTransport(ibbTransport));
-		session->acceptTransport(content, transport);
-	}
-	else {
-		session->rejectTransport(content, transport);
+		session->sendTransportAccept(content, ibbTransport);
+	} else {
+		SWIFT_LOG(debug) << "transport replaced failed" << std::endl;
+		session->sendTransportReject(content, transport);
 	}
 }
 
@@ -163,7 +493,25 @@ void IncomingJingleFileTransfer::stopActiveTransport() {
 }
 
 JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) {
-	return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffer()->size, router);
+	// TODO: getOffer() -> getOffers correction
+	return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffers()[0].getSize(), router);
+}
+
+JingleContentID IncomingJingleFileTransfer::getContentID() const {
+	return JingleContentID(initialContent->getName(), initialContent->getCreator());
+}
+
+void IncomingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+	if (state == Terminated) {
+		return;
+	}
+
+	if (error) {
+		session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+		onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+		onFinished(error);
+	}
+	//
 }
 
 }
diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.h b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
index 164d868..4ae0d1d 100644
--- a/Swiften/FileTransfer/IncomingJingleFileTransfer.h
+++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.h
@@ -8,14 +8,21 @@
 
 #include <boost/shared_ptr.hpp>
 
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Network/Timer.h>
 #include <Swiften/Jingle/JingleSession.h>
 #include <Swiften/Jingle/JingleContentID.h>
 #include <Swiften/FileTransfer/IncomingFileTransfer.h>
 #include <Swiften/FileTransfer/JingleTransport.h>
 #include <Swiften/FileTransfer/JingleIncomingIBBTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
 #include <Swiften/Elements/JingleContentPayload.h>
 #include <Swiften/Elements/JingleFileTransferDescription.h>
 #include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
 
 namespace Swift {
 	class IQRouter;
@@ -23,6 +30,9 @@ namespace Swift {
 	class LocalJingleTransportCandidateGeneratorFactory;
 	class RemoteJingleTransportCandidateSelector;
 	class LocalJingleTransportCandidateGenerator;
+	class SOCKS5BytestreamRegistry;
+	class SOCKS5BytestreamProxy;
+	class IncrementalBytestreamHashCalculator;
 
 	class IncomingJingleFileTransfer : public IncomingFileTransfer {
 		public:
@@ -37,42 +47,86 @@ namespace Swift {
 			};
 
 			IncomingJingleFileTransfer(
+					const JID& ourJID,
 					JingleSession::ref,
 					JingleContentPayload::ref content,
 					RemoteJingleTransportCandidateSelectorFactory*,
 					LocalJingleTransportCandidateGeneratorFactory*,
-					IQRouter* router);
+					IQRouter* router,
+					SOCKS5BytestreamRegistry* bytestreamRegistry,
+					SOCKS5BytestreamProxy* bytestreamProxy,
+					TimerFactory*);
 			~IncomingJingleFileTransfer();
 
 			virtual void accept(WriteBytestream::ref);
+			virtual const JID& getSender() const;
+			void cancel();
 
 		private:
-			void handleSessionTerminateReceived();
+			void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason>);
+			void handleSessionInfoReceived(JinglePayload::ref);
 			void handleTransportReplaceReceived(const JingleContentID&, JingleTransportPayload::ref);
 			void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
 			void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates);
 			void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref candidate);
 			void setActiveTransport(JingleTransport::ref transport);
 			void handleTransportDataReceived(const std::vector<unsigned char>& data);
+			void handleWriteStreamDataReceived(const std::vector<unsigned char>& data);
 			void stopActiveTransport();
 			void checkCandidateSelected();
 			JingleIncomingIBBTransport::ref createIBBTransport(JingleIBBTransportPayload::ref ibbTransport);
+			JingleContentID getContentID() const;
+			void checkIfAllDataReceived();
+			bool verifyReceviedData();
+			void finishOffTransfer();
+			void handleTransferFinished(boost::optional<FileTransferError>);
 
 		private:
+			typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+		private:
+			void activateProxySession(const JID &proxy);
+			void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+			void proxySessionReady(const JID& proxy, bool error);
+
+		private:
+			void useOurCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+			void useTheirCandidateChoiceForTransfer(JingleS5BTransportPayload::Candidate candidate);
+			void decideOnUsedTransport();
+			void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+		private:
+			JID ourJID;
 			JingleSession::ref session;
 			IQRouter* router;
+			TimerFactory* timerFactory;
 			JingleContentPayload::ref initialContent;
-			JingleContentID contentID;
 			State state;
 			JingleFileTransferDescription::ref description;
 			WriteBytestream::ref stream;
+			uintmax_t receivedBytes;
+			IncrementalBytestreamHashCalculator* hashCalculator;
+			Timer::ref waitOnHashTimer;
+			IDGenerator idGenerator;
+
 			RemoteJingleTransportCandidateSelector* candidateSelector;
 			LocalJingleTransportCandidateGenerator* candidateGenerator;
+			SOCKS5BytestreamRegistry* s5bRegistry;
+			SOCKS5BytestreamProxy* s5bProxy;
 			bool remoteTransportCandidateSelectFinished;
 			JingleTransportPayload::ref selectedRemoteTransportCandidate;
 			bool localTransportCandidateSelectFinished;
 			JingleTransportPayload::ref selectedLocalTransportCandidate;
 
+			JingleS5BTransportPayload::ref ourCandidate;
+			JingleS5BTransportPayload::ref theirCandidate;
+			CandidateMap ourCandidates;
+			CandidateMap theirCandidates;
+			SOCKS5BytestreamClientSession::ref clientSession;
+			std::string s5bDestination;
+			std::string s5bSessionID;
+			SOCKS5BytestreamServerSession* serverSession;
+
 			JingleTransport::ref activeTransport;
 	};
 }
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
new file mode 100644
index 0000000..6b53a1b
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/MD5.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+IncrementalBytestreamHashCalculator::IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1) {
+	md5Hasher = doMD5 ? new MD5() : NULL;
+	sha1Hasher = doSHA1 ? new SHA1() : NULL;
+}
+
+IncrementalBytestreamHashCalculator::~IncrementalBytestreamHashCalculator() {
+	delete md5Hasher;
+	delete sha1Hasher;
+}
+
+void IncrementalBytestreamHashCalculator::feedData(const ByteArray& data) {
+	if (md5Hasher) {
+		md5Hasher->update(data);
+	}
+	if (sha1Hasher) {
+		sha1Hasher->update(data);
+	}
+}
+/*
+void IncrementalBytestreamHashCalculator::feedData(const SafeByteArray& data) {
+	if (md5Hasher) {
+		md5Hasher->update(createByteArray(data.data(), data.size()));
+	}
+	if (sha1Hasher) {
+		sha1Hasher->update(createByteArray(data.data(), data.size()));
+	}
+}*/
+
+std::string IncrementalBytestreamHashCalculator::getSHA1String() {
+	if (sha1Hasher) {
+		ByteArray result = sha1Hasher->getHash();
+		return Hexify::hexify(result);
+	} else {
+		return std::string();
+	}
+}
+
+std::string IncrementalBytestreamHashCalculator::getMD5String() {
+	if (md5Hasher) {
+		ByteArray result = md5Hasher->getHash();
+		return Hexify::hexify(result);
+	} else {
+		return std::string();
+	}
+}
+
+}
diff --git a/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
new file mode 100644
index 0000000..64f4b5f
--- /dev/null
+++ b/Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/SafeByteArray.h>
+
+namespace Swift {
+
+class MD5;
+class SHA1;
+
+class IncrementalBytestreamHashCalculator {
+public:
+	IncrementalBytestreamHashCalculator(bool doMD5, bool doSHA1);
+	~IncrementalBytestreamHashCalculator();
+
+	void feedData(const ByteArray& data);
+	//void feedData(const SafeByteArray& data);
+
+	std::string getSHA1String();
+	std::string getMD5String();
+
+private:
+	MD5* md5Hasher;
+	SHA1* sha1Hasher;
+};
+
+}
diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
index 0ca899f..9b5c354 100644
--- a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
+++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp
@@ -8,8 +8,9 @@
 
 namespace Swift {
 
-JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(from, id, size, router) {
+JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(id, from, size, router) {
 	ibbSession.onDataReceived.connect(boost::ref(onDataReceived));
+	ibbSession.onFinished.connect(boost::ref(onFinished));
 }
 
 void JingleIncomingIBBTransport::start() {
diff --git a/Swiften/FileTransfer/JingleTransport.h b/Swiften/FileTransfer/JingleTransport.h
index 1d163d0..fa296e8 100644
--- a/Swiften/FileTransfer/JingleTransport.h
+++ b/Swiften/FileTransfer/JingleTransport.h
@@ -9,6 +9,7 @@
 #include <boost/shared_ptr.hpp>
 
 #include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/FileTransfer/FileTransfer.h>
 
 namespace Swift {
 	class JingleTransport {
@@ -21,5 +22,6 @@ namespace Swift {
 			virtual void stop() = 0;
 
 			boost::signal<void (const std::vector<unsigned char>&)> onDataReceived;
+			boost::signal<void (boost::optional<FileTransferError>)> onFinished;
 	};
 }
diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
index c111005..041afe3 100644
--- a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
+++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h
@@ -15,8 +15,10 @@ namespace Swift {
 	class LocalJingleTransportCandidateGenerator {
 		public:
 			virtual ~LocalJingleTransportCandidateGenerator();
-
-			virtual void generateLocalTransportCandidates() = 0;
+			/**
+			* Should call onLocalTransportCandidatesGenerated if it has finished discovering local candidates.
+			*/
+			virtual void generateLocalTransportCandidates(JingleTransportPayload::ref) = 0;
 
 			virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
 			virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h
index a8c1e81..1ec1ae3 100644
--- a/Swiften/FileTransfer/OutgoingFileTransfer.h
+++ b/Swiften/FileTransfer/OutgoingFileTransfer.h
@@ -6,8 +6,14 @@
 
 #pragma once
 
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/FileTransfer/FileTransfer.h>
+
 namespace Swift {
-	class OutgoingFileTransfer {
+	class OutgoingFileTransfer : public FileTransfer {
+		public:
+			typedef boost::shared_ptr<OutgoingFileTransfer> ref;
 		public:
 			virtual ~OutgoingFileTransfer();
 
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.cpp b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
new file mode 100644
index 0000000..321a57a
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingFileTransferManager.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Jingle/JingleSessionManager.h>
+#include <Swiften/Jingle/JingleSessionImpl.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Base/IDGenerator.h>
+
+namespace Swift {
+
+OutgoingFileTransferManager::OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy) : ownJID(ownFullJID), jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy) {
+	idGenerator = new IDGenerator();
+}
+
+OutgoingFileTransferManager::~OutgoingFileTransferManager() {
+	delete idGenerator;
+}
+
+boost::shared_ptr<OutgoingFileTransfer> OutgoingFileTransferManager::createOutgoingFileTransfer(const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
+	// check if receipient support Jingle FT
+	
+	
+	JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(ownJID, receipient, idGenerator->generateID(), iqRouter);
+	
+	//jsManager->getSession(receipient, idGenerator->generateID());
+	assert(jingleSession);
+	jsManager->registerOutgoingSession(ownJID, jingleSession);
+	boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT =  boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy));
+	
+	// otherwise try SI
+	
+	// else fail
+	
+	return jingleFT;
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingFileTransferManager.h b/Swiften/FileTransfer/OutgoingFileTransferManager.h
new file mode 100644
index 0000000..d57c14d
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingFileTransferManager.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/JID/JID.h>
+
+namespace Swift {
+
+class JingleSessionManager;
+class IQRouter;
+class EntityCapsProvider;
+class RemoteJingleTransportCandidateSelectorFactory;
+class LocalJingleTransportCandidateGeneratorFactory;
+class OutgoingFileTransfer;
+class JID;
+class IDGenerator;
+class ReadBytestream;
+class StreamInitiationFileInfo;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingFileTransferManager {
+public:
+	OutgoingFileTransferManager(const JID& ownFullJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy);
+	~OutgoingFileTransferManager();
+	
+	boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID&, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
+
+private:
+	JID ownJID;
+	JingleSessionManager* jsManager;
+	IQRouter* iqRouter;
+	EntityCapsProvider* capsProvider;
+	RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+	LocalJingleTransportCandidateGeneratorFactory* localFactory;
+	IDGenerator *idGenerator;
+	SOCKS5BytestreamRegistry* bytestreamRegistry;
+	SOCKS5BytestreamProxy* bytestreamProxy;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
new file mode 100644
index 0000000..9b71165
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "OutgoingJingleFileTransfer.h"
+
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JingleTransportPayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/FileTransfer/IBBSendSession.h>
+#include <Swiften/FileTransfer/IncrementalBytestreamHashCalculator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+OutgoingJingleFileTransfer::OutgoingJingleFileTransfer(JingleSession::ref session,
+					RemoteJingleTransportCandidateSelectorFactory* remoteFactory,
+					LocalJingleTransportCandidateGeneratorFactory* localFactory,
+					IQRouter* router,
+					IDGenerator *idGenerator,
+					const JID& toJID,
+					boost::shared_ptr<ReadBytestream> readStream,
+					const StreamInitiationFileInfo &fileInfo,
+					SOCKS5BytestreamRegistry* bytestreamRegistry,
+					SOCKS5BytestreamProxy* bytestreamProxy) :
+	session(session), remoteFactory(remoteFactory), localFactory(localFactory), router(router), idGenerator(idGenerator), toJID(toJID), readStream(readStream), fileInfo(fileInfo), s5bRegistry(bytestreamRegistry), s5bProxy(bytestreamProxy), serverSession(NULL), contentID(JingleContentID(idGenerator->generateID(), JingleContentPayload::InitiatorCreator)), canceled(false) {
+	session->onSessionAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionAcceptReceived, this, _1, _2, _3));
+	session->onSessionTerminateReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleSessionTerminateReceived, this, _1));
+	session->onTransportInfoReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2));
+	session->onTransportAcceptReceived.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransportAcceptReceived, this, _1, _2));
+	fileSizeInBytes = fileInfo.getSize();
+	filename = fileInfo.getName();
+
+	localCandidateGenerator = localFactory->createCandidateGenerator();
+	localCandidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1));
+
+	remoteCandidateSelector = remoteFactory->createCandidateSelector();
+	remoteCandidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1));
+	// calculate both, MD5 and SHA-1 since we don't know which one the other side supports
+	hashCalculator = new IncrementalBytestreamHashCalculator(true, true);
+	this->readStream->onRead.connect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+}
+
+OutgoingJingleFileTransfer::~OutgoingJingleFileTransfer() {
+	readStream->onRead.disconnect(boost::bind(&IncrementalBytestreamHashCalculator::feedData, hashCalculator, _1));
+	delete hashCalculator;
+}
+	
+void OutgoingJingleFileTransfer::start() {
+	onStateChange(FileTransfer::State(FileTransfer::State::WaitingForStart));
+
+	s5bSessionID = s5bRegistry->generateSessionID();
+	SWIFT_LOG(debug) << "S5B SessionID: " << s5bSessionID << std::endl;
+
+	//s5bProxy->connectToProxies(s5bSessionID);
+
+	JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+	localCandidateGenerator->generateLocalTransportCandidates(transport);
+}
+
+void OutgoingJingleFileTransfer::stop() {
+
+}
+
+void OutgoingJingleFileTransfer::cancel() {
+	canceled = true;
+	session->sendTerminate(JinglePayload::Reason::Cancel);
+
+	if (ibbSession) {
+		ibbSession->stop();
+	}
+	SOCKS5BytestreamServerSession *serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+	if (serverSession) {
+		serverSession->stop();
+	}
+	if (clientSession) {
+		clientSession->stop();
+	}
+	onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+}
+
+void OutgoingJingleFileTransfer::handleSessionAcceptReceived(const JingleContentID& id, JingleDescription::ref /* decription */, JingleTransportPayload::ref transportPayload) {
+	if (canceled) {
+		return;
+	}
+	onStateChange(FileTransfer::State(FileTransfer::State::Negotiating));
+
+	JingleIBBTransportPayload::ref ibbPayload;
+	JingleS5BTransportPayload::ref s5bPayload;
+	if ((ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transportPayload))) {
+		ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+		ibbSession->setBlockSize(ibbPayload->getBlockSize());
+		ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+		ibbSession->start();
+		onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+	}
+	else if ((s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transportPayload))) {
+		fillCandidateMap(theirCandidates, s5bPayload);
+		remoteCandidateSelector->setRequesterTargtet(toJID, session->getInitiator());
+		remoteCandidateSelector->addRemoteTransportCandidates(s5bPayload);
+		remoteCandidateSelector->selectCandidate();
+	}
+	else {
+		// TODO: error handling
+		SWIFT_LOG(debug) << "Unknown transport payload! Try replaceing with IBB." << std::endl;
+		replaceTransportWithIBB(id);
+	}
+}
+
+void OutgoingJingleFileTransfer::handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason) {
+	if (canceled) {
+		return;
+	}
+
+	if (ibbSession) {
+		ibbSession->stop();
+	}
+	if (clientSession) {
+		clientSession->stop();
+	}
+	if (serverSession) {
+		serverSession->stop();
+	}
+
+	if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Cancel) {
+		onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
+		onFinished(FileTransferError(FileTransferError::PeerError));
+	} else if (reason.is_initialized() && reason.get().type == JinglePayload::Reason::Success) {
+		onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+		onFinished(boost::optional<FileTransferError>());
+	} else {
+		onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+		onFinished(FileTransferError(FileTransferError::PeerError));
+	}
+	canceled = true;
+}
+
+void OutgoingJingleFileTransfer::handleTransportAcceptReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+	if (canceled) {
+		return;
+	}
+
+	if (JingleIBBTransportPayload::ref ibbPayload = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) {
+		ibbSession = boost::make_shared<IBBSendSession>(ibbPayload->getSessionID(), toJID, readStream, router);
+		ibbSession->setBlockSize(ibbPayload->getBlockSize());
+		ibbSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		ibbSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+		ibbSession->start();
+		onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+	} else {
+		// error handling
+		SWIFT_LOG(debug) << "Replacing with anything other than IBB isn't supported yet." << std::endl;
+		session->sendTerminate(JinglePayload::Reason::FailedTransport);
+		onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+	}
+}
+
+void OutgoingJingleFileTransfer::startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+	SWIFT_LOG(debug) << "Transferring data using our candidate." << std::endl;
+	if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+		// get proxy client session from remoteCandidateSelector
+		clientSession = remoteCandidateSelector->getS5BSession();
+
+		// wait on <activated/> transport-info
+	} else {
+		clientSession = remoteCandidateSelector->getS5BSession();
+		clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+		clientSession->startSending(readStream);
+		onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+	}
+	assert(clientSession);
+}
+
+void OutgoingJingleFileTransfer::startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate candidate) {
+	SWIFT_LOG(debug) << "Transferring data using their candidate." << std::endl;
+	if (candidate.type == JingleS5BTransportPayload::Candidate::ProxyType) {
+		// connect to proxy
+		clientSession = s5bProxy->createSOCKS5BytestreamClientSession(candidate.hostPort, SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+		clientSession->onSessionReady.connect(boost::bind(&OutgoingJingleFileTransfer::proxySessionReady, this, candidate.jid, _1));
+		clientSession->start();
+
+		// on reply send activate
+
+	} else {
+		serverSession = s5bRegistry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID));
+		serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+		serverSession->startTransfer();
+	}
+	onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+}
+
+// decide on candidates according to http://xmpp.org/extensions/xep-0260.html#complete
+void OutgoingJingleFileTransfer::decideOnCandidates() {
+	if (ourCandidateChoice && theirCandidateChoice) {
+		std::string our_cid = ourCandidateChoice->getCandidateUsed();
+		std::string their_cid = theirCandidateChoice->getCandidateUsed();
+		if (ourCandidateChoice->hasCandidateError() && theirCandidateChoice->hasCandidateError()) {
+			replaceTransportWithIBB(contentID);
+		}
+		else if (!our_cid.empty() && theirCandidateChoice->hasCandidateError()) {
+			// use our candidate
+			startTransferViaOurCandidateChoice(theirCandidates[our_cid]);
+		}
+		else if (!their_cid.empty() && ourCandidateChoice->hasCandidateError()) {
+			// use their candidate
+			startTransferViaTheirCandidateChoice(ourCandidates[their_cid]);
+		}
+		else if (!our_cid.empty() && !their_cid.empty()) {
+			// compare priorites, if same we win
+			if (ourCandidates.find(their_cid) == ourCandidates.end() || theirCandidates.find(our_cid) == theirCandidates.end()) {
+				SWIFT_LOG(debug) << "Didn't recognize candidate IDs!" << std::endl;
+				session->sendTerminate(JinglePayload::Reason::FailedTransport);
+				onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+				onFinished(FileTransferError(FileTransferError::PeerError));
+				return;
+			}
+
+			JingleS5BTransportPayload::Candidate ourCandidate = theirCandidates[our_cid];
+			JingleS5BTransportPayload::Candidate theirCandidate = ourCandidates[their_cid];
+			if (ourCandidate.priority > theirCandidate.priority) {
+				startTransferViaOurCandidateChoice(ourCandidate);
+			}
+			else if (ourCandidate.priority < theirCandidate.priority) {
+				startTransferViaTheirCandidateChoice(theirCandidate);
+			}
+			else {
+				startTransferViaOurCandidateChoice(ourCandidate);
+			}
+		}
+	} else {
+		SWIFT_LOG(debug) << "Can't make a decision yet!" << std::endl;
+	}
+}
+
+void OutgoingJingleFileTransfer::fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload) {
+	map.clear();
+	foreach (JingleS5BTransportPayload::Candidate candidate, s5bPayload->getCandidates()) {
+		map[candidate.cid] = candidate;
+	}
+}
+
+void OutgoingJingleFileTransfer::proxySessionReady(const JID& proxy, bool error) {
+	if (error) {
+		// indicate proxy error
+	} else {
+		// activate proxy
+		activateProxySession(proxy);
+	}
+}
+
+void OutgoingJingleFileTransfer::activateProxySession(const JID& proxy) {
+	S5BProxyRequest::ref proxyRequest = boost::make_shared<S5BProxyRequest>();
+	proxyRequest->setSID(s5bSessionID);
+	proxyRequest->setActivate(toJID);
+
+	boost::shared_ptr<GenericRequest<S5BProxyRequest> > request = boost::make_shared<GenericRequest<S5BProxyRequest> >(IQ::Set, proxy, proxyRequest, router);
+	request->onResponse.connect(boost::bind(&OutgoingJingleFileTransfer::handleActivateProxySessionResult, this, _1, _2));
+	request->send();
+}
+
+void OutgoingJingleFileTransfer::handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> /*request*/, ErrorPayload::ref error) {
+	if (error) {
+		SWIFT_LOG(debug) << "ERROR" << std::endl;
+	} else {
+		// send activated to other jingle party
+		JingleS5BTransportPayload::ref proxyActivate = boost::make_shared<JingleS5BTransportPayload>();
+		proxyActivate->setActivated(theirCandidateChoice->getCandidateUsed());
+		session->sendTransportInfo(contentID, proxyActivate);
+
+		// start transferring
+		clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+		clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+		clientSession->startSending(readStream);
+		onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+	}
+}
+
+void OutgoingJingleFileTransfer::sendSessionInfoHash() {
+	SWIFT_LOG(debug) << std::endl;
+	JingleFileTransferHash::ref hashElement = boost::make_shared<JingleFileTransferHash>();
+	hashElement->setHash("sha-1", hashCalculator->getSHA1String());
+	hashElement->setHash("md5", hashCalculator->getMD5String());
+	session->sendInfo(hashElement);
+}
+
+void OutgoingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID& /* contentID */, JingleTransportPayload::ref transport) {
+	if (canceled) {
+		return;
+	}
+	if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport)) {
+		if (s5bPayload->hasCandidateError() || !s5bPayload->getCandidateUsed().empty()) {
+			theirCandidateChoice = s5bPayload;
+			decideOnCandidates();
+		} else if(!s5bPayload->getActivated().empty()) {
+			if (ourCandidateChoice->getCandidateUsed() == s5bPayload->getActivated()) {
+				clientSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
+				clientSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
+				clientSession->startSending(readStream);
+				onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
+			} else {
+				SWIFT_LOG(debug) << "ourCandidateChoice doesn't match activated proxy candidate!" << std::endl;
+				JingleS5BTransportPayload::ref proxyError = boost::make_shared<JingleS5BTransportPayload>();
+				proxyError->setProxyError(true);
+				proxyError->setSessionID(s5bSessionID);
+				session->sendTransportInfo(contentID, proxyError);
+			}
+		}
+	}
+}
+
+void OutgoingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+	if (canceled) {
+		return;
+	}
+	JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>();
+	description->addOffer(fileInfo);
+
+	JingleTransportPayload::ref transport;
+	if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(payload)) {
+		ibbTransport->setBlockSize(4096);
+		ibbTransport->setSessionID(idGenerator->generateID());
+		transport = ibbTransport;
+	}
+	else if (JingleS5BTransportPayload::ref s5bTransport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+		//fillCandidateMap(ourCandidates, s5bTransport);
+		//s5bTransport->setSessionID(s5bSessionID);
+
+		JingleS5BTransportPayload::ref emptyCandidates = boost::make_shared<JingleS5BTransportPayload>();
+		emptyCandidates->setSessionID(s5bTransport->getSessionID());
+		fillCandidateMap(ourCandidates, emptyCandidates);
+
+		transport = emptyCandidates;
+		s5bRegistry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(s5bSessionID, session->getInitiator(), toJID), readStream);
+	}
+	else {
+		SWIFT_LOG(debug) << "Unknown tranport payload: " << typeid(*payload).name() << std::endl;
+		return;
+	}
+	session->sendInitiate(contentID, description, transport);
+	onStateChange(FileTransfer::State(FileTransfer::State::WaitingForAccept));
+}
+
+void OutgoingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref payload) {
+	if (canceled) {
+		return;
+	}
+	if (JingleS5BTransportPayload::ref s5bPayload = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(payload)) {
+		ourCandidateChoice = s5bPayload;
+		session->sendTransportInfo(contentID, s5bPayload);
+		decideOnCandidates();
+	}
+}
+
+void OutgoingJingleFileTransfer::replaceTransportWithIBB(const JingleContentID& id) {
+	SWIFT_LOG(debug) << "Both parties failed. Replace transport with IBB." << std::endl;
+	JingleIBBTransportPayload::ref ibbTransport = boost::make_shared<JingleIBBTransportPayload>();
+	ibbTransport->setBlockSize(4096);
+	ibbTransport->setSessionID(idGenerator->generateID());
+	session->sendTransportReplace(id, ibbTransport);
+}
+
+void OutgoingJingleFileTransfer::handleTransferFinished(boost::optional<FileTransferError> error) {
+	if (error) {
+		session->sendTerminate(JinglePayload::Reason::ConnectivityError);
+		onStateChange(FileTransfer::State(FileTransfer::State::Failed));
+		onFinished(error);
+	} else {
+		sendSessionInfoHash();
+		/*
+		session->terminate(JinglePayload::Reason::Success);
+		onStateChange(FileTransfer::State(FileTransfer::State::Finished));
+		*/
+	}
+	//
+}
+
+}
diff --git a/Swiften/FileTransfer/OutgoingJingleFileTransfer.h b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
new file mode 100644
index 0000000..fecfbdb
--- /dev/null
+++ b/Swiften/FileTransfer/OutgoingJingleFileTransfer.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Elements/ErrorPayload.h>
+#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Jingle/JingleSession.h>
+#include <Swiften/StringCodecs/SHA1.h>
+
+namespace Swift {
+
+class RemoteJingleTransportCandidateSelectorFactory;
+class RemoteJingleTransportCandidateSelector;
+class LocalJingleTransportCandidateGeneratorFactory;
+class LocalJingleTransportCandidateGenerator;
+class IQRouter;
+class ReadBytestream;
+class IBBSendSession;
+class IDGenerator;
+class IncrementalBytestreamHashCalculator;
+class SOCKS5BytestreamRegistry;
+class SOCKS5BytestreamProxy;
+
+class OutgoingJingleFileTransfer : public OutgoingFileTransfer {
+public:
+	OutgoingJingleFileTransfer(JingleSession::ref,
+					RemoteJingleTransportCandidateSelectorFactory*,
+					LocalJingleTransportCandidateGeneratorFactory*,
+					IQRouter*,
+					IDGenerator*,
+					const JID&,
+					boost::shared_ptr<ReadBytestream>,
+					const StreamInitiationFileInfo&,
+					SOCKS5BytestreamRegistry*,
+					SOCKS5BytestreamProxy*);
+	virtual ~OutgoingJingleFileTransfer();
+	
+	void start();
+	void stop();
+
+	void cancel();
+
+private:
+	void handleSessionAcceptReceived(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+	void handleSessionTerminateReceived(boost::optional<JinglePayload::Reason> reason);
+	void handleTransportAcceptReceived(const JingleContentID&, JingleTransportPayload::ref);
+	void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref);
+
+	void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref);
+	void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref);
+
+private:
+	void replaceTransportWithIBB(const JingleContentID&);
+	void handleTransferFinished(boost::optional<FileTransferError>);
+	void activateProxySession(const JID &proxy);
+	void handleActivateProxySessionResult(boost::shared_ptr<S5BProxyRequest> request, ErrorPayload::ref error);
+	void proxySessionReady(const JID& proxy, bool error);
+
+private:
+	typedef std::map<std::string, JingleS5BTransportPayload::Candidate> CandidateMap;
+
+private:
+	void startTransferViaOurCandidateChoice(JingleS5BTransportPayload::Candidate);
+	void startTransferViaTheirCandidateChoice(JingleS5BTransportPayload::Candidate);
+	void decideOnCandidates();
+	void fillCandidateMap(CandidateMap& map, JingleS5BTransportPayload::ref s5bPayload);
+
+private:
+	void sendSessionInfoHash();
+
+private:
+	JingleSession::ref session;
+	RemoteJingleTransportCandidateSelector* remoteCandidateSelector;
+	RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
+	LocalJingleTransportCandidateGenerator* localCandidateGenerator;
+	LocalJingleTransportCandidateGeneratorFactory* localFactory;
+
+	IQRouter* router;
+	IDGenerator* idGenerator;
+	JID toJID;
+	boost::shared_ptr<ReadBytestream> readStream;
+	StreamInitiationFileInfo fileInfo;
+	IncrementalBytestreamHashCalculator *hashCalculator;
+
+	boost::shared_ptr<IBBSendSession> ibbSession;
+
+	JingleS5BTransportPayload::ref ourCandidateChoice;
+	JingleS5BTransportPayload::ref theirCandidateChoice;
+	CandidateMap ourCandidates;
+	CandidateMap theirCandidates;
+
+	SOCKS5BytestreamRegistry* s5bRegistry;
+	SOCKS5BytestreamProxy* s5bProxy;
+	SOCKS5BytestreamClientSession::ref clientSession;
+	SOCKS5BytestreamServerSession* serverSession;
+	JingleContentID contentID;
+	std::string s5bSessionID;
+
+	bool canceled;
+};
+
+}
diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
index 85ac505..dfcf028 100644
--- a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
+++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp
@@ -38,7 +38,7 @@ void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiat
 	}
 	else {
 		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
-			socksServer->addBytestream(id, from, to, bytestream); 
+			socksServer->addReadBytestream(id, from, to, bytestream);
 			Bytestreams::ref bytestreams(new Bytestreams());
 			bytestreams->setStreamID(id);
 			HostAddressPort addressPort = socksServer->getAddressPort();
@@ -67,7 +67,7 @@ void OutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
 		ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
 		ibbSession.reset();
 	}
-	socksServer->removeBytestream(id, from, to); 
+	socksServer->removeReadBytestream(id, from, to);
 	onFinished(error);
 }
 
diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h
index 2601192..9e070f7 100644
--- a/Swiften/FileTransfer/ReadBytestream.h
+++ b/Swiften/FileTransfer/ReadBytestream.h
@@ -9,11 +9,16 @@
 #include <vector>
 #include <cstring>
 
+#include <Swiften/Base/boost_bsignals.h>
+
 namespace Swift {
 	class ReadBytestream {
 		public:
 			virtual ~ReadBytestream();
 			virtual std::vector<unsigned char> read(size_t size) = 0;
 			virtual bool isFinished() const = 0;
+
+		public:
+			boost::signal<void (std::vector<unsigned char>)> onRead;
 	};
 }
diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
index b12b06b..f8df8f9 100644
--- a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
+++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h
@@ -8,8 +8,10 @@
 
 #include <Swiften/Base/boost_bsignals.h>
 
+#include <Swiften/JID/JID.h>
 #include <Swiften/Elements/JingleTransportPayload.h>
 #include <Swiften/FileTransfer/JingleTransport.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
 
 namespace Swift {
 	class RemoteJingleTransportCandidateSelector {
@@ -19,6 +21,8 @@ namespace Swift {
 			virtual void addRemoteTransportCandidates(JingleTransportPayload::ref) = 0;
 			virtual void selectCandidate() = 0;
 			virtual void setMinimumPriority(int) = 0;
+			virtual void setRequesterTargtet(const JID&, const JID&) {}
+			virtual SOCKS5BytestreamClientSession::ref getS5BSession() { return SOCKS5BytestreamClientSession::ref(); }
 
 			virtual bool isActualCandidate(JingleTransportPayload::ref) = 0;
 			virtual int getPriority(JingleTransportPayload::ref) = 0;
diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript
index 24fc9e8..ef02557 100644
--- a/Swiften/FileTransfer/SConscript
+++ b/Swiften/FileTransfer/SConscript
@@ -3,6 +3,9 @@ Import("swiften_env", "env")
 sources = [
 		"OutgoingFileTransfer.cpp",
 		"OutgoingSIFileTransfer.cpp",
+		"OutgoingJingleFileTransfer.cpp",
+		"OutgoingFileTransferManager.cpp",
+		"OutgoingFileTransferManager.cpp",
 		"IncomingFileTransfer.cpp",
 		"IncomingJingleFileTransfer.cpp",
 		"IncomingFileTransferManager.cpp",
@@ -10,23 +13,36 @@ sources = [
 		"RemoteJingleTransportCandidateSelectorFactory.cpp",
 		"LocalJingleTransportCandidateGenerator.cpp",
 		"LocalJingleTransportCandidateGeneratorFactory.cpp",
+		"DefaultRemoteJingleTransportCandidateSelectorFactory.cpp",
+		"DefaultLocalJingleTransportCandidateGeneratorFactory.cpp",
+		"DefaultRemoteJingleTransportCandidateSelector.cpp",
+		"DefaultLocalJingleTransportCandidateGenerator.cpp",
 		"JingleTransport.cpp",
 		"JingleIncomingIBBTransport.cpp",
 		"ReadBytestream.cpp",
 		"WriteBytestream.cpp",
 		"FileReadBytestream.cpp",
 		"FileWriteBytestream.cpp",
+		"SOCKS5BytestreamClientSession.cpp",
 		"SOCKS5BytestreamServer.cpp",
 		"SOCKS5BytestreamServerSession.cpp",
 		"SOCKS5BytestreamRegistry.cpp",
+		"SOCKS5BytestreamProxy.cpp",
 		"IBBSendSession.cpp",
 		"IBBReceiveSession.cpp",
+		"FileTransferManager.cpp",
+		"FileTransferManagerImpl.cpp",
+		"IncrementalBytestreamHashCalculator.cpp",
+		"ConnectivityManager.cpp",
 	]
 
 swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
 
 env.Append(UNITTEST_SOURCES = [
 			File("UnitTest/SOCKS5BytestreamServerSessionTest.cpp"),
+			File("UnitTest/SOCKS5BytestreamClientSessionTest.cpp"),
 			File("UnitTest/IBBSendSessionTest.cpp"),
 			File("UnitTest/IBBReceiveSessionTest.cpp"),
+			File("UnitTest/IncomingJingleFileTransferTest.cpp"),
+			File("UnitTest/OutgoingJingleFileTransferTest.cpp"),
 	])
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
new file mode 100644
index 0000000..f90b73b
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/FileTransfer/BytestreamException.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+SOCKS5BytestreamClientSession::SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort& addressPort, const std::string& destination, TimerFactory* timerFactory) :
+	connection(connection), addressPort(addressPort), destination(destination), state(Initial), chunkSize(131072) {
+	connection->onConnectFinished.connect(boost::bind(&SOCKS5BytestreamClientSession::handleConnectFinished, this, _1));
+	connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDisconnected, this, _1));
+	weFailedTimeout = timerFactory->createTimer(2000);
+	weFailedTimeout->onTick.connect(boost::bind(&SOCKS5BytestreamClientSession::handleWeFailedTimeout, this));
+}
+
+void SOCKS5BytestreamClientSession::start() {
+	assert(state == Initial);
+	SWIFT_LOG(debug) << "Trying to connect via TCP to " << addressPort.toString() << "." << std::endl;
+	weFailedTimeout->start();
+	connection->connect(addressPort);
+}
+
+void SOCKS5BytestreamClientSession::stop() {
+	connection->disconnect();
+	connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+	connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+	readBytestream.reset();
+	state = Finished;
+}
+
+void SOCKS5BytestreamClientSession::process() {
+	SWIFT_LOG(debug) << "unprocessedData.size(): " << unprocessedData.size() << std::endl;
+	ByteArray bndAddress;
+	switch(state) {
+		case Initial:
+			hello();
+			break;
+		case Hello:
+			if (unprocessedData.size() > 1) {
+				unsigned char version = unprocessedData[0];
+				unsigned char authMethod = unprocessedData[1];
+				if (version != 5 || authMethod != 0) {
+					// signal failure to upper level
+					finish(true);
+					return;
+				}
+				unprocessedData.clear();
+				authenticate();
+			}
+			break;
+		case Authenticating:
+			if (unprocessedData.size() < 5) {
+				// need more data to start progressing
+				break;
+			}
+			if (unprocessedData[0] != '\x05') {
+				// wrong version
+				// disconnect & signal failure
+				finish(true);
+				break;
+			}
+			if (unprocessedData[1] != '\x00') {
+				// no success
+				// disconnect & signal failure
+				finish(true);
+				break;
+			}
+			if (unprocessedData[3] != '\x03') {
+				// we expect x'03' = DOMAINNAME here
+				// discconect & signal failure
+				finish(true);
+				break;
+			}
+			if (static_cast<size_t>(unprocessedData[4]) + 1 > unprocessedData.size() + 5) {
+				// complete domainname and port not available yet
+				break;
+			}
+			bndAddress = createByteArray(&(unprocessedData.data()[5]), unprocessedData[4]);
+			if (unprocessedData[unprocessedData[4] + 5] != 0 && bndAddress == createByteArray(destination)) {
+				// we expect a 0 as port
+				// disconnect and fail
+				finish(true);
+			}
+			unprocessedData.clear();
+			state = Ready;
+			SWIFT_LOG(debug) << "session ready" << std::endl;
+			// issue ready signal so the bytestream can be used for reading or writing
+			weFailedTimeout->stop();
+			onSessionReady(false);
+			break;
+		case Ready:
+			SWIFT_LOG(debug) << "Received further data in Ready state." << std::endl;
+			break;
+		case Reading:
+		case Writing:
+		case Finished:
+			SWIFT_LOG(debug) << "Unexpected receive of data. Current state: " << state << std::endl;
+			SWIFT_LOG(debug) << "Data: " << Hexify::hexify(unprocessedData) << std::endl;
+			unprocessedData.clear();
+			//assert(false);
+	}
+}
+
+void SOCKS5BytestreamClientSession::hello() {
+	// Version 5, 1 auth method, No authentication
+	const SafeByteArray hello = createSafeByteArray("\x05\x01\x00", 3);
+	connection->write(hello);
+	state = Hello;
+}
+
+void SOCKS5BytestreamClientSession::authenticate() {
+	SWIFT_LOG(debug) << std::endl;
+	SafeByteArray header = createSafeByteArray("\x05\x01\x00\x03", 4);
+	SafeByteArray message = header;
+	append(message, createSafeByteArray(destination.size()));
+	authenticateAddress = createByteArray(destination);
+	append(message, authenticateAddress);
+	append(message, createSafeByteArray("\x00\x00", 2)); // 2 byte for port
+	connection->write(message);
+	state = Authenticating;
+}
+
+void SOCKS5BytestreamClientSession::startReceiving(boost::shared_ptr<WriteBytestream> writeStream) {
+	if (state == Ready) {
+		state = Reading;
+		writeBytestream = writeStream;
+		writeBytestream->write(unprocessedData);
+		onBytesReceived(unprocessedData.size());
+		unprocessedData.clear();
+	} else {
+		SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+	}
+}
+
+void SOCKS5BytestreamClientSession::startSending(boost::shared_ptr<ReadBytestream> readStream) {
+	if (state == Ready) {
+		state = Writing;
+		readBytestream = readStream;
+		connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+		sendData();
+	} else {
+		SWIFT_LOG(debug) << "Session isn't ready for transfer yet!" << std::endl;
+	}
+}
+
+HostAddressPort SOCKS5BytestreamClientSession::getAddressPort() const {
+	return addressPort;
+}
+
+void SOCKS5BytestreamClientSession::sendData() {
+	if (!readBytestream->isFinished()) {
+		try {
+			SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+			connection->write(dataToSend);
+			onBytesSent(dataToSend.size());
+		}
+		catch (const BytestreamException&) {
+			finish(true);
+		}
+	}
+	else {
+		finish(false);
+	}
+}
+
+void SOCKS5BytestreamClientSession::finish(bool error) {
+	weFailedTimeout->stop();
+	connection->disconnect();
+	connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamClientSession::sendData, this));
+	connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+	readBytestream.reset();
+	if (state == Initial || state == Hello || state == Authenticating) {
+		onSessionReady(true);
+	}
+	else {
+		state = Finished;
+		if (error) {
+			onFinished(boost::optional<FileTransferError>(FileTransferError::ReadError));
+		} else {
+			onFinished(boost::optional<FileTransferError>());
+		}
+	}
+}
+
+void SOCKS5BytestreamClientSession::handleConnectFinished(bool error) {
+	if (error) {
+		SWIFT_LOG(debug) << "Failed to connect via TCP to " << addressPort.toString() << "." << std::endl;
+		finish(true);
+	} else {
+		SWIFT_LOG(debug) << "Successfully connected via TCP" << addressPort.toString() << "." << std::endl;
+		weFailedTimeout->start();
+		connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSession::handleDataRead, this, _1));
+		process();
+	}
+}
+
+void SOCKS5BytestreamClientSession::handleDataRead(const SafeByteArray& data) {
+	SWIFT_LOG(debug) << "state: " << state << " data.size() = " << data.size() << std::endl;
+	if (state != Reading) {
+		append(unprocessedData, data);
+		process();
+	}
+	else {
+		writeBytestream->write(createByteArray(data.data(), data.size()));
+		onBytesReceived(data.size());
+	}
+}
+
+void SOCKS5BytestreamClientSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+	SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+	if (error) {
+		finish(true);
+	}
+}
+
+void SOCKS5BytestreamClientSession::handleWeFailedTimeout() {
+	SWIFT_LOG(debug) << "Failed due to timeout!" << std::endl;
+	finish(true);
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
new file mode 100644
index 0000000..894e977
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamClientSession.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/Timer.h>
+
+namespace Swift {
+
+class SOCKS5BytestreamRegistry;
+class Connection;
+class TimerFactory;
+
+/**
+ * A session which has been connected to a SOCKS5 server (requester).
+ *
+ */
+class SOCKS5BytestreamClientSession {
+public:
+	enum State {
+		Initial,
+		Hello,
+		Authenticating,
+		Ready,
+		Writing,
+		Reading,
+		Finished,
+	};
+
+public:
+	typedef boost::shared_ptr<SOCKS5BytestreamClientSession> ref;
+
+public:
+	SOCKS5BytestreamClientSession(boost::shared_ptr<Connection> connection, const HostAddressPort&, const std::string&, TimerFactory*);
+
+	void start();
+	void stop();
+
+	void startReceiving(boost::shared_ptr<WriteBytestream>);
+	void startSending(boost::shared_ptr<ReadBytestream>);
+
+	HostAddressPort getAddressPort() const;
+
+	boost::signal<void (bool /*error*/)> onSessionReady;
+
+	boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+	boost::signal<void (int)> onBytesSent;
+	boost::signal<void (int)> onBytesReceived;
+
+private:
+	void process();
+	void hello();
+	void authenticate();
+
+	void handleConnectFinished(bool error);
+	void handleDataRead(const SafeByteArray&);
+	void handleDisconnected(const boost::optional<Connection::Error>&);
+	void handleWeFailedTimeout();
+
+	void finish(bool error);
+	void sendData();
+
+private:
+	boost::shared_ptr<Connection> connection;
+	HostAddressPort addressPort;
+	std::string destination; // hexify(SHA1(sessionID + requester + target))
+
+	State state;
+	int destinationPort;
+
+	ByteArray unprocessedData;
+	ByteArray authenticateAddress;
+
+	int chunkSize;
+	boost::shared_ptr<WriteBytestream> writeBytestream;
+	boost::shared_ptr<ReadBytestream> readBytestream;
+
+	Timer::ref weFailedTimeout;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
new file mode 100644
index 0000000..9599fd1
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "SOCKS5BytestreamProxy.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+SOCKS5BytestreamProxy::SOCKS5BytestreamProxy(ConnectionFactory *connFactory, TimerFactory *timeFactory) : connectionFactory(connFactory), timerFactory(timeFactory) {
+
+}
+
+void SOCKS5BytestreamProxy::addS5BProxy(S5BProxyRequest::ref proxy) {
+	localS5BProxies.push_back(proxy);
+}
+
+const std::vector<S5BProxyRequest::ref>& SOCKS5BytestreamProxy::getS5BProxies() const {
+	return localS5BProxies;
+}
+
+void SOCKS5BytestreamProxy::connectToProxies(const std::string& sessionID) {
+	SWIFT_LOG(debug) << "session ID: " << sessionID << std::endl;
+	ProxyJIDClientSessionMap clientSessions;
+
+	foreach(S5BProxyRequest::ref proxy, localS5BProxies) {
+		boost::shared_ptr<Connection> conn = connectionFactory->createConnection();
+
+		boost::shared_ptr<SOCKS5BytestreamClientSession> session = boost::make_shared<SOCKS5BytestreamClientSession>(conn, proxy->getStreamHost().get().addressPort, sessionID, timerFactory);
+		clientSessions[proxy->getStreamHost().get().jid] = session;
+		session->start();
+	}
+
+	proxySessions[sessionID] = clientSessions;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID) {
+	// checking parameters
+	if (proxySessions.find(sessionID) == proxySessions.end()) {
+		return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+	}
+	if (proxySessions[sessionID].find(proxyJID) == proxySessions[sessionID].end()) {
+		return boost::shared_ptr<SOCKS5BytestreamClientSession>();
+	}
+
+	// get active session
+	boost::shared_ptr<SOCKS5BytestreamClientSession> activeSession = proxySessions[sessionID][proxyJID];
+	proxySessions[sessionID].erase(proxyJID);
+
+	// close other sessions
+	foreach(const ProxyJIDClientSessionMap::value_type& myPair, proxySessions[sessionID]) {
+		myPair.second->stop();
+	}
+
+	proxySessions.erase(sessionID);
+
+	return activeSession;
+}
+
+boost::shared_ptr<SOCKS5BytestreamClientSession> SOCKS5BytestreamProxy::createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr) {
+	SOCKS5BytestreamClientSession::ref connection = boost::make_shared<SOCKS5BytestreamClientSession>(connectionFactory->createConnection(), addressPort, destAddr, timerFactory);
+	return connection;
+}
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxy.h b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
new file mode 100644
index 0000000..8f80cea
--- /dev/null
+++ b/Swiften/FileTransfer/SOCKS5BytestreamProxy.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/TimerFactory.h>
+
+namespace Swift {
+
+/**
+ *	- manages list of working S5B proxies
+ *	- creates initial connections (for the candidates you provide)
+ */
+class SOCKS5BytestreamProxy {
+public:
+	SOCKS5BytestreamProxy(ConnectionFactory*, TimerFactory*);
+
+	void addS5BProxy(S5BProxyRequest::ref);
+	const std::vector<S5BProxyRequest::ref>& getS5BProxies() const;
+
+	void connectToProxies(const std::string& sessionID);
+	boost::shared_ptr<SOCKS5BytestreamClientSession> getProxySessionAndCloseOthers(const JID& proxyJID, const std::string& sessionID);
+
+	boost::shared_ptr<SOCKS5BytestreamClientSession> createSOCKS5BytestreamClientSession(HostAddressPort addressPort, const std::string& destAddr);
+
+private:
+	ConnectionFactory* connectionFactory;
+	TimerFactory* timerFactory;
+
+	typedef std::map<JID, boost::shared_ptr<SOCKS5BytestreamClientSession> > ProxyJIDClientSessionMap;
+	std::map<std::string, ProxyJIDClientSessionMap> proxySessions;
+
+	std::vector<S5BProxyRequest::ref> localS5BProxies;
+};
+
+}
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
index 429c7f2..ffc4298 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.cpp
@@ -6,27 +6,67 @@
 
 #include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/StringCodecs/SHA1.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
 namespace Swift {
 
 SOCKS5BytestreamRegistry::SOCKS5BytestreamRegistry() {
 }
 
-void SOCKS5BytestreamRegistry::addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
-	byteStreams[destination] = byteStream;
+void SOCKS5BytestreamRegistry::addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream) {
+	readBytestreams[destination] = byteStream;
 }
 
-void SOCKS5BytestreamRegistry::removeBytestream(const std::string& destination) {
-	byteStreams.erase(destination);
+void SOCKS5BytestreamRegistry::removeReadBytestream(const std::string& destination) {
+	readBytestreams.erase(destination);
 }
 
-boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getBytestream(const std::string& destination) const {
-	BytestreamMap::const_iterator i = byteStreams.find(destination);
-	if (i != byteStreams.end()) {
+boost::shared_ptr<ReadBytestream> SOCKS5BytestreamRegistry::getReadBytestream(const std::string& destination) const {
+	ReadBytestreamMap::const_iterator i = readBytestreams.find(destination);
+	if (i != readBytestreams.end()) {
 		return i->second;
 	}
 	return boost::shared_ptr<ReadBytestream>();
 }
 
+void SOCKS5BytestreamRegistry::addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream) {
+	writeBytestreams[destination] = byteStream;
+}
+
+void SOCKS5BytestreamRegistry::removeWriteBytestream(const std::string& destination) {
+	writeBytestreams.erase(destination);
+}
+
+boost::shared_ptr<WriteBytestream> SOCKS5BytestreamRegistry::getWriteBytestream(const std::string& destination) const {
+	WriteBytestreamMap::const_iterator i = writeBytestreams.find(destination);
+	if (i != writeBytestreams.end()) {
+		return i->second;
+	}
+	return boost::shared_ptr<WriteBytestream>();
+}
+
+std::string SOCKS5BytestreamRegistry::generateSessionID() {
+	return idGenerator.generateID();
+}
+
+SOCKS5BytestreamServerSession* SOCKS5BytestreamRegistry::getConnectedSession(const std::string& destination) {
+	if (serverSessions.find(destination) != serverSessions.end()) {
+		return serverSessions[destination];
+	} else {
+		SWIFT_LOG(debug) << "No active connction for stream ID " << destination << " found!" << std::endl;
+		return NULL;
+	}
+}
+
+std::string SOCKS5BytestreamRegistry::getHostname(const std::string& sessionID, const JID& requester, const JID& target) {
+	return Hexify::hexify(SHA1::getHash(createSafeByteArray(sessionID + requester.toString() + target.toString())));
+}
 
 }
 
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
index 955b900..779aabb 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamRegistry.h
@@ -7,23 +7,58 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
-#include <map>
 
+#include <map>
 #include <string>
+#include <vector>
+#include <set>
+
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/S5BProxyRequest.h>
 #include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/Network/HostAddressPort.h>
 
 namespace Swift {
 	class SOCKS5BytestreamRegistry {
 		public:
 			SOCKS5BytestreamRegistry();
 
-			boost::shared_ptr<ReadBytestream> getBytestream(const std::string& destination) const;
-			void addBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
-			void removeBytestream(const std::string& destination);
+			boost::shared_ptr<ReadBytestream> getReadBytestream(const std::string& destination) const;
+			void addReadBytestream(const std::string& destination, boost::shared_ptr<ReadBytestream> byteStream);
+			void removeReadBytestream(const std::string& destination);
+
+			boost::shared_ptr<WriteBytestream> getWriteBytestream(const std::string& destination) const;
+			void addWriteBytestream(const std::string& destination, boost::shared_ptr<WriteBytestream> byteStream);
+			void removeWriteBytestream(const std::string& destination);
+
+			/**
+			 * Generate a new session ID to use for new S5B streams.
+			 */
+			std::string generateSessionID();
+
+			/**
+			 *	Start an actual transfer.
+			 */
+			SOCKS5BytestreamServerSession* getConnectedSession(const std::string& destination);
+
+		public:
+			static std::string getHostname(const std::string& sessionID, const JID& requester, const JID& target);
 
 		private:
-			typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > BytestreamMap;
-			BytestreamMap byteStreams;
+			friend class SOCKS5BytestreamServerSession;
+
+			typedef std::map<std::string, boost::shared_ptr<ReadBytestream> > ReadBytestreamMap;
+			ReadBytestreamMap readBytestreams;
+
+			typedef std::map<std::string, boost::shared_ptr<WriteBytestream> > WriteBytestreamMap;
+			WriteBytestreamMap writeBytestreams;
+
+			std::map<std::string, SOCKS5BytestreamServerSession*> serverSessions;
+
+			IDGenerator idGenerator;
 	};
 }
-
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
index 9731d2d..90fed7a 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.cpp
@@ -8,13 +8,15 @@
 
 #include <boost/bind.hpp>
 
+#include <Swiften/Base/Log.h>
 #include <Swiften/StringCodecs/Hexify.h>
 #include <Swiften/StringCodecs/SHA1.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
 namespace Swift {
 
-SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer) : connectionServer(connectionServer) {
+SOCKS5BytestreamServer::SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry) : connectionServer(connectionServer), registry(registry) {
 }
 
 void SOCKS5BytestreamServer::start() {
@@ -25,12 +27,12 @@ void SOCKS5BytestreamServer::stop() {
 	connectionServer->onNewConnection.disconnect(boost::bind(&SOCKS5BytestreamServer::handleNewConnection, this, _1));
 }
 
-void SOCKS5BytestreamServer::addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
-	bytestreams.addBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
+void SOCKS5BytestreamServer::addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream) {
+	registry->addReadBytestream(getSOCKSDestinationAddress(id, from, to), byteStream);
 }
 
-void SOCKS5BytestreamServer::removeBytestream(const std::string& id, const JID& from, const JID& to) {
-	bytestreams.removeBytestream(getSOCKSDestinationAddress(id, from, to));
+void SOCKS5BytestreamServer::removeReadBytestream(const std::string& id, const JID& from, const JID& to) {
+	registry->removeReadBytestream(getSOCKSDestinationAddress(id, from, to));
 }
 
 std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string& id, const JID& from, const JID& to) {
@@ -38,7 +40,7 @@ std::string SOCKS5BytestreamServer::getSOCKSDestinationAddress(const std::string
 }
 
 void SOCKS5BytestreamServer::handleNewConnection(boost::shared_ptr<Connection> connection) {
-	boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, &bytestreams));
+	boost::shared_ptr<SOCKS5BytestreamServerSession> session(new SOCKS5BytestreamServerSession(connection, registry));
 	sessions.push_back(session);
 	session->start();
 }
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServer.h b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
index 7fa709e..6bb598e 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServer.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServer.h
@@ -20,15 +20,15 @@ namespace Swift {
 
 	class SOCKS5BytestreamServer {
 		public:
-			SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer);
+			SOCKS5BytestreamServer(boost::shared_ptr<ConnectionServer> connectionServer, SOCKS5BytestreamRegistry* registry);
 
 			HostAddressPort getAddressPort() const;
 
 			void start();
 			void stop();
 
-			void addBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
-			void removeBytestream(const std::string& id, const JID& from, const JID& to);
+			void addReadBytestream(const std::string& id, const JID& from, const JID& to, boost::shared_ptr<ReadBytestream> byteStream);
+			void removeReadBytestream(const std::string& id, const JID& from, const JID& to);
 
 		/*protected:
 			boost::shared_ptr<ReadBytestream> getBytestream(const std::string& dest);*/
@@ -42,7 +42,7 @@ namespace Swift {
 			friend class SOCKS5BytestreamServerSession;
 
 			boost::shared_ptr<ConnectionServer> connectionServer;
-			SOCKS5BytestreamRegistry bytestreams;
+			SOCKS5BytestreamRegistry* registry;
 			std::vector<boost::shared_ptr<SOCKS5BytestreamServerSession> > sessions;
 	};
 }
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
index 477af4b..6f33862 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp
@@ -13,12 +13,14 @@
 #include <Swiften/Base/SafeByteArray.h>
 #include <Swiften/Base/Algorithm.h>
 #include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
 #include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 #include <Swiften/FileTransfer/BytestreamException.h>
 
 namespace Swift {
 
-SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(4096) {
+SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(131072) {
+	connection->onDisconnected.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
 }
 
 SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
@@ -29,17 +31,55 @@ SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() {
 }
 
 void SOCKS5BytestreamServerSession::start() {
+	SWIFT_LOG(debug) << std::endl;
 	connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
 	state = WaitingForAuthentication;
 }
 
 void SOCKS5BytestreamServerSession::stop() {
-	finish(false);
+	connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+	connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
+	connection->disconnect();
+	state = Finished;
+}
+
+void SOCKS5BytestreamServerSession::startTransfer() {
+	if (state == ReadyForTransfer) {
+		if (readBytestream) {
+			state = WritingData;
+			connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+			sendData();
+		}
+		else if(writeBytestream) {
+			state = ReadingData;
+			writeBytestream->write(unprocessedData);
+			onBytesReceived(unprocessedData.size());
+			unprocessedData.clear();
+		}
+	} else {
+		SWIFT_LOG(debug) << "Not ready for transfer!" << std::endl;
+	}
+}
+
+HostAddressPort SOCKS5BytestreamServerSession::getAddressPort() const {
+	return connection->getLocalAddress();
 }
 
 void SOCKS5BytestreamServerSession::handleDataRead(const SafeByteArray& data) {
-	append(unprocessedData, data);
-	process();
+	if (state != ReadingData) {
+		append(unprocessedData, data);
+		process();
+	} else {
+		writeBytestream->write(createByteArray(data.data(), data.size()));
+		onBytesReceived(data.size());
+	}
+}
+
+void SOCKS5BytestreamServerSession::handleDisconnected(const boost::optional<Connection::Error>& error) {
+	SWIFT_LOG(debug) << (error ? (error == Connection::ReadError ? "Read Error" : "Write Error") : "No Error") << std::endl;
+	if (error) {
+		finish(true);
+	}
 }
 
 void SOCKS5BytestreamServerSession::process() {
@@ -54,7 +94,7 @@ void SOCKS5BytestreamServerSession::process() {
 			if (i == 2 + authCount) {
 				// Authentication message is complete
 				if (i != unprocessedData.size()) {
-					std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+					SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
 				}
 				unprocessedData.clear();
 				connection->write(createSafeByteArray("\x05\x00", 2));
@@ -71,26 +111,31 @@ void SOCKS5BytestreamServerSession::process() {
 				requestID.push_back(unprocessedData[i]);
 				++i;
 			}
-			// Skip the port:
+			// Skip the port: 2 byte large, one already skipped. Add one for comparison with size
 			i += 2;
-			if (i >= unprocessedData.size()) {
+			if (i <= unprocessedData.size()) {
 				if (i != unprocessedData.size()) {
-					std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism";
+					SWIFT_LOG(debug) << "Junk after authentication mechanism" << std::endl;
 				}
-				bytestream = bytestreams->getBytestream(byteArrayToString(requestID));
+				unprocessedData.clear();
+				std::string streamID = byteArrayToString(requestID);
+				readBytestream = bytestreams->getReadBytestream(streamID);
+				writeBytestream = bytestreams->getWriteBytestream(streamID);
 				SafeByteArray result = createSafeByteArray("\x05", 1);
-				result.push_back(bytestream ? 0x0 : 0x4);
+				result.push_back((readBytestream || writeBytestream) ? 0x0 : 0x4);
 				append(result, createByteArray("\x00\x03", 2));
 				result.push_back(static_cast<char>(requestID.size()));
 				append(result, concat(requestID, createByteArray("\x00\x00", 2)));
-				if (!bytestream) {
+				if (!readBytestream && !writeBytestream) {
+					SWIFT_LOG(debug) << "Readstream or Wrtiestream with ID " << streamID << " not found!" << std::endl;
 					connection->write(result);
 					finish(true);
 				}
 				else {
-					state = SendingData;
-					connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
+					SWIFT_LOG(deubg) << "Found " << (readBytestream ? "Readstream" : "Writestream") << ". Sent OK." << std::endl;
 					connection->write(result);
+					bytestreams->serverSessions[streamID] = this;
+					state = ReadyForTransfer;
 				}
 			}
 		}
@@ -98,9 +143,11 @@ void SOCKS5BytestreamServerSession::process() {
 }
 
 void SOCKS5BytestreamServerSession::sendData() {
-	if (!bytestream->isFinished()) {
+	if (!readBytestream->isFinished()) {
 		try {
-			connection->write(createSafeByteArray(bytestream->read(chunkSize)));
+			SafeByteArray dataToSend = createSafeByteArray(readBytestream->read(chunkSize));
+			connection->write(dataToSend);
+			onBytesSent(dataToSend.size());
 		}
 		catch (const BytestreamException&) {
 			finish(true);
@@ -114,9 +161,14 @@ void SOCKS5BytestreamServerSession::sendData() {
 void SOCKS5BytestreamServerSession::finish(bool error) {
 	connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this));
 	connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1));
-	bytestream.reset();
+	connection->onDisconnected.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDisconnected, this, _1));
+	readBytestream.reset();
 	state = Finished;
-	onFinished(error);
+	if (error) {
+		onFinished(boost::optional<FileTransferError>(FileTransferError::PeerError));
+	} else {
+		onFinished(boost::optional<FileTransferError>());
+	}
 }
 
 }
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
index 761a365..3e1018f 100644
--- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
+++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.h
@@ -11,17 +11,24 @@
 #include <Swiften/Base/boost_bsignals.h>
 #include <Swiften/Network/Connection.h>
 #include <Swiften/FileTransfer/ReadBytestream.h>
+#include <Swiften/FileTransfer/WriteBytestream.h>
+#include <Swiften/FileTransfer/FileTransferError.h>
 
 namespace Swift {
 	class SOCKS5BytestreamRegistry;
 
 	class SOCKS5BytestreamServerSession {
 		public:
+		typedef boost::shared_ptr<SOCKS5BytestreamServerSession> ref;
+
+		public:
 			enum State {
 				Initial,
 				WaitingForAuthentication,
 				WaitingForRequest,
-				SendingData,
+				ReadyForTransfer,
+				ReadingData,
+				WritingData,
 				Finished,
 			};
 
@@ -35,12 +42,18 @@ namespace Swift {
 			void start();
 			void stop();
 
-			boost::signal<void (bool /* error */)> onFinished;
+			void startTransfer();
+			HostAddressPort getAddressPort() const;
+
+			boost::signal<void (boost::optional<FileTransferError>)> onFinished;
+			boost::signal<void (int)> onBytesSent;
+			boost::signal<void (int)> onBytesReceived;
 
 		private:
 			void finish(bool error);
 			void process();
 			void handleDataRead(const SafeByteArray&);
+			void handleDisconnected(const boost::optional<Connection::Error>&);
 			void sendData();
 
 		private:
@@ -49,6 +62,7 @@ namespace Swift {
 			ByteArray unprocessedData;
 			State state;
 			int chunkSize;
-			boost::shared_ptr<ReadBytestream> bytestream;
+			boost::shared_ptr<ReadBytestream> readBytestream;
+			boost::shared_ptr<WriteBytestream> writeBytestream;
 	};
 }
diff --git a/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
new file mode 100644
index 0000000..ae06cd3
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <boost/filesystem.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <Swiften/FileTransfer/FileTransferManager.h>
+
+namespace Swift {
+	class DummyFileTransferManager : public FileTransferManager {
+		public:
+			DummyFileTransferManager() : FileTransferManager() {
+			}
+
+			virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const boost::filesystem::path&, const std::string&, boost::shared_ptr<ReadBytestream>) {
+				return OutgoingFileTransfer::ref();
+			}
+
+			virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const std::string&, const std::string&, const boost::uintmax_t, const boost::posix_time::ptime&, boost::shared_ptr<ReadBytestream>) {
+				return OutgoingFileTransfer::ref();
+			}
+
+			virtual void startListeningOnPort(int) {
+			}
+
+			virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest>) {
+			}
+
+	};
+}
diff --git a/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..7407f44
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Queries/IQRouter.h>
+
+#include <iostream>
+
+using namespace Swift;
+using namespace boost;
+
+class FakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+		void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+			candidate = cand;
+		}
+
+		void selectCandidate() {
+		    boost::shared_ptr<JingleS5BTransportPayload> payload = make_shared<JingleS5BTransportPayload>();
+		    payload->setCandidateError(true);
+		    payload->setSessionID(candidate->getSessionID());
+		    onRemoteTransportCandidateSelectFinished(payload);
+		}
+
+		void setMinimumPriority(int) {
+
+		}
+
+		bool isActualCandidate(JingleTransportPayload::ref) {
+			return false;
+		}
+
+		int getPriority(JingleTransportPayload::ref) {
+			return 0;
+		}
+
+		JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+			return JingleTransport::ref();
+		}
+
+private:
+	JingleTransportPayload::ref candidate;
+};
+
+class FakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+	virtual ~FakeRemoteJingleTransportCandidateSelectorFactory() {
+
+	}
+
+	virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+		return new FakeRemoteJingleTransportCandidateSelector();
+	}
+};
+
+class FakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+	virtual void generateLocalTransportCandidates(JingleTransportPayload::ref payload) {
+		JingleS5BTransportPayload::ref payL = make_shared<JingleS5BTransportPayload>();
+		payL->setSessionID(payload->getSessionID());
+		onLocalTransportCandidatesGenerated(payL);
+	}
+
+	void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+		onLocalTransportCandidatesGenerated(payload);
+	}
+
+	virtual bool isActualCandidate(JingleTransportPayload::ref) {
+		return false;
+	}
+
+	virtual int getPriority(JingleTransportPayload::ref) {
+		return 0;
+	}
+
+	virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+		return JingleTransport::ref();
+	}
+};
+
+class FakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+	virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+		return new FakeLocalJingleTransportCandidateGenerator();
+	}
+};
+
+class IncomingJingleFileTransferTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(IncomingJingleFileTransferTest);
+		CPPUNIT_TEST(test_AcceptOnyIBBSendsSessionAccept);
+		CPPUNIT_TEST(test_OnlyIBBTransferReceiveWorks);
+		CPPUNIT_TEST(test_AcceptFailingS5BFallsBackToIBB);
+		CPPUNIT_TEST_SUITE_END();
+public:
+		shared_ptr<IncomingJingleFileTransfer> createTestling() {
+			JID ourJID("our@jid.org/full");
+			return make_shared<IncomingJingleFileTransfer>(ourJID, shared_ptr<JingleSession>(fakeJingleSession), jingleContentPayload, fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, bytestreamRegistry, bytestreamProxy, timerFactory);
+		}
+
+		IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+			IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+			request->setFrom(from);
+			return request;
+		}
+
+		void setUp() {
+			eventLoop = new DummyEventLoop();
+			fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+			jingleContentPayload = make_shared<JingleContentPayload>();
+			fakeRJTCSF = make_shared<FakeRemoteJingleTransportCandidateSelectorFactory>();
+			fakeLJTCF = make_shared<FakeLocalJingleTransportCandidateGeneratorFactory>();
+			stanzaChannel = new DummyStanzaChannel();
+			iqRouter = new IQRouter(stanzaChannel);
+			bytestreamRegistry = new SOCKS5BytestreamRegistry();
+			timerFactory = new DummyTimerFactory();
+			connectionFactory = new DummyConnectionFactory(eventLoop);
+			bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+		}
+
+		void tearDown() {
+			delete bytestreamProxy;
+			delete connectionFactory;
+			delete timerFactory;
+			delete bytestreamRegistry;
+			delete iqRouter;
+			delete stanzaChannel;
+			delete eventLoop;
+		}
+
+		// Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer.
+		void test_AcceptOnyIBBSendsSessionAccept() {
+			//1. create your test incoming file transfer
+			shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+			desc->addOffer(StreamInitiationFileInfo());
+			jingleContentPayload->addDescription(desc);
+			JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+			tpRef->setSessionID("mysession");
+			jingleContentPayload->addTransport(tpRef);
+
+			shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+			//2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+			shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+			fileTransfer->accept(byteStream);
+
+			// check whether accept has been called
+			getCall<FakeJingleSession::AcceptCall>(0);
+		}
+
+		void test_OnlyIBBTransferReceiveWorks() {
+			//1. create your test incoming file transfer
+			shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+			desc->addOffer(StreamInitiationFileInfo());
+			jingleContentPayload->addDescription(desc);
+			JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>();
+			tpRef->setSessionID("mysession");
+			jingleContentPayload->addTransport(tpRef);
+
+			shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+			//2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+			shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+			fileTransfer->accept(byteStream);
+
+			// check whether accept has been called
+			getCall<FakeJingleSession::AcceptCall>(0);
+			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+			CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+		}
+
+		void test_AcceptFailingS5BFallsBackToIBB() {
+			//1. create your test incoming file transfer
+			addFileTransferDescription();
+
+			// add SOCKS5BytestreamTransportPayload
+			JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+
+			shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling();
+
+			//2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one)
+			shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>();
+			fileTransfer->accept(byteStream);
+
+			// check whether accept has been called
+			FakeJingleSession::AcceptCall acceptCall = getCall<FakeJingleSession::AcceptCall>(0);
+			CPPUNIT_ASSERT_EQUAL(payLoad->getSessionID(), acceptCall.payload->getSessionID());
+
+			// check for candidate error
+			FakeJingleSession::InfoTransportCall infoTransportCall = getCall<FakeJingleSession::InfoTransportCall>(1);
+			JingleS5BTransportPayload::ref s5bPayload = dynamic_pointer_cast<JingleS5BTransportPayload>(infoTransportCall.payload);
+			CPPUNIT_ASSERT(s5bPayload->hasCandidateError());
+
+			// indicate transport replace (Romeo)
+			fakeJingleSession->onTransportReplaceReceived(getContentID(), addJingleIBBPayload());
+
+			FakeJingleSession::AcceptTransportCall acceptTransportCall = getCall<FakeJingleSession::AcceptTransportCall>(2);
+
+			// send a bit of data
+			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open"));
+			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a"));
+			CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData());
+		}
+
+		void test_S5BTransferReceiveTest() {
+			addFileTransferDescription();
+			JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload();
+		}
+
+private:
+	void addFileTransferDescription() {
+		shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>();
+		desc->addOffer(StreamInitiationFileInfo());
+		jingleContentPayload->addDescription(desc);
+	}
+
+	shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+		JingleS5BTransportPayload::ref payLoad = make_shared<JingleS5BTransportPayload>();
+		payLoad->setSessionID("mysession");
+		jingleContentPayload->addTransport(payLoad);
+		return payLoad;
+	}
+
+	shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+		JingleIBBTransportPayload::ref payLoad = make_shared<JingleIBBTransportPayload>();
+		payLoad->setSessionID("mysession");
+		jingleContentPayload->addTransport(payLoad);
+		return payLoad;
+	}
+	
+	JingleContentID getContentID() const  {
+		return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+	}
+
+	template <typename T> T getCall(int i) const {
+		size_t index = static_cast<size_t>(i);
+		CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+		T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+		CPPUNIT_ASSERT(cmd);
+		return *cmd;
+	}
+
+private:
+	EventLoop* eventLoop;
+	FakeJingleSession* fakeJingleSession;
+	shared_ptr<JingleContentPayload> jingleContentPayload;
+	shared_ptr<FakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+	shared_ptr<FakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+	DummyStanzaChannel* stanzaChannel;
+	IQRouter* iqRouter;
+	SOCKS5BytestreamRegistry* bytestreamRegistry;
+	DummyConnectionFactory* connectionFactory;
+	SOCKS5BytestreamProxy* bytestreamProxy;
+	DummyTimerFactory* timerFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IncomingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
new file mode 100644
index 0000000..582c3e5
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/optional.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
+#include <Swiften/Jingle/FakeJingleSession.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h>
+#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h>
+#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h>
+#include <Swiften/Queries/IQRouter.h>
+#include <Swiften/Client/DummyStanzaChannel.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
+
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/IBB.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/IDGenerator.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/DummyConnectionFactory.h>
+
+#include <Swiften/Base/Log.h>
+
+#include <iostream>
+
+using namespace Swift;
+
+class OFakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector {
+		void addRemoteTransportCandidates(JingleTransportPayload::ref cand) {
+			candidate = cand;
+		}
+
+		void selectCandidate() {
+			JingleS5BTransportPayload::ref payload = boost::make_shared<JingleS5BTransportPayload>();
+		    payload->setCandidateError(true);
+		    payload->setSessionID(candidate->getSessionID());
+		    onRemoteTransportCandidateSelectFinished(payload);
+		}
+
+		void setMinimumPriority(int) {
+
+		}
+
+		bool isActualCandidate(JingleTransportPayload::ref) {
+			return false;
+		}
+
+		int getPriority(JingleTransportPayload::ref) {
+			return 0;
+		}
+
+		JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+			return JingleTransport::ref();
+		}
+
+private:
+	JingleTransportPayload::ref candidate;
+};
+
+class OFakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory {
+public:
+	virtual ~OFakeRemoteJingleTransportCandidateSelectorFactory() {
+
+	}
+
+	virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() {
+		return new OFakeRemoteJingleTransportCandidateSelector();
+	}
+};
+
+class OFakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator {
+public:
+	virtual void generateLocalTransportCandidates(JingleTransportPayload::ref /* payload */) {
+		//JingleTransportPayload::ref payL = make_shared<JingleTransportPayload>();
+		//payL->setSessionID(payload->getSessionID());
+		JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>();
+
+		onLocalTransportCandidatesGenerated(payL);
+	}
+
+	void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) {
+		onLocalTransportCandidatesGenerated(payload);
+	}
+
+	virtual bool isActualCandidate(JingleTransportPayload::ref) {
+		return false;
+	}
+
+	virtual int getPriority(JingleTransportPayload::ref) {
+		return 0;
+	}
+
+	virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) {
+		return JingleTransport::ref();
+	}
+};
+
+class OFakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory {
+public:
+	virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() {
+		return new OFakeLocalJingleTransportCandidateGenerator();
+	}
+};
+
+class OutgoingJingleFileTransferTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest);
+		CPPUNIT_TEST(test_SendSessionInitiateOnStart);
+		CPPUNIT_TEST(test_IBBStartsAfterSendingSessionAccept);
+		CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate);
+		CPPUNIT_TEST_SUITE_END();
+
+		class FTStatusHelper {
+		public:
+			bool finishedCalled;
+			FileTransferError::Type error;
+			void handleFileTransferFinished(boost::optional<FileTransferError> error) {
+				finishedCalled = true;
+				if (error.is_initialized()) this->error = error.get().getType();
+			}
+		};
+public:
+
+		boost::shared_ptr<OutgoingJingleFileTransfer> createTestling() {
+			JID to("test@foo.com/bla");
+			StreamInitiationFileInfo fileInfo;
+			fileInfo.setDescription("some file");
+			fileInfo.setName("test.bin");
+			fileInfo.setHash("asdjasdas");
+			fileInfo.setSize(1024 * 1024);
+			return boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(boost::shared_ptr<JingleSession>(fakeJingleSession), fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, idGen, to, stream, fileInfo, s5bRegistry, s5bProxy));
+		}
+
+		IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) {
+			IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb);
+			request->setFrom(from);
+			return request;
+		}
+
+		void setUp() {
+			fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession");
+			jingleContentPayload = boost::make_shared<JingleContentPayload>();
+			fakeRJTCSF = boost::make_shared<OFakeRemoteJingleTransportCandidateSelectorFactory>();
+			fakeLJTCF = boost::make_shared<OFakeLocalJingleTransportCandidateGeneratorFactory>();
+			stanzaChannel = new DummyStanzaChannel();
+			iqRouter = new IQRouter(stanzaChannel);
+			eventLoop = new DummyEventLoop();
+			timerFactory = new DummyTimerFactory();
+			connectionFactory = new DummyConnectionFactory(eventLoop);
+			s5bRegistry = new SOCKS5BytestreamRegistry();
+			s5bProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory);
+
+			data.clear();
+			for (int n=0; n < 1024 * 1024; ++n) {
+				data.push_back(34);
+			}
+			
+			stream = boost::make_shared<ByteArrayReadBytestream>(data);
+
+			idGen = new IDGenerator();
+		}
+
+		void tearDown() {
+			delete idGen;
+			delete s5bRegistry;
+			delete connectionFactory;
+			delete timerFactory;
+			delete eventLoop;
+			delete iqRouter;
+			delete stanzaChannel;
+		}
+
+		
+		void test_SendSessionInitiateOnStart() {
+			boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+			transfer->start();
+			FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+			JingleFileTransferDescription::ref description = boost::dynamic_pointer_cast<JingleFileTransferDescription>(call.description);
+			CPPUNIT_ASSERT(description);
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), description->getOffers().size());
+			CPPUNIT_ASSERT(static_cast<size_t>(1048576) == description->getOffers()[0].getSize());
+
+			JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload);
+			CPPUNIT_ASSERT(transport);
+		}
+		
+		void test_IBBStartsAfterSendingSessionAccept() {
+			boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+			transfer->start();
+
+			FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0);
+			// FIXME: we initiate with SOCSK5 now and not IBB, needs to be fixed.
+			/*
+			fakeJingleSession->onSessionAcceptReceived(call.id, call.description, call.payload);
+			IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex<IQ>(0);
+			CPPUNIT_ASSERT(iqOpenStanza);
+			*/
+		}
+		
+		void test_ReceiveSessionTerminateAfterSessionInitiate() {
+			boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling();
+			transfer->start();
+			
+			getCall<FakeJingleSession::InitiateCall>(0);
+
+			FTStatusHelper helper;
+			helper.finishedCalled = false;
+			transfer->onFinished.connect(bind(&FTStatusHelper::handleFileTransferFinished, &helper, _1));
+			fakeJingleSession->onSessionTerminateReceived(JinglePayload::Reason(JinglePayload::Reason::Busy));
+			CPPUNIT_ASSERT_EQUAL(true, helper.finishedCalled);
+			CPPUNIT_ASSERT(FileTransferError::PeerError == helper.error);
+		}
+		
+
+//TODO: some more testcases
+
+private:
+	void addFileTransferDescription() {
+		boost::shared_ptr<JingleFileTransferDescription> desc = boost::make_shared<JingleFileTransferDescription>();
+		desc->addOffer(StreamInitiationFileInfo());
+		jingleContentPayload->addDescription(desc);
+	}
+
+	boost::shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() {
+		JingleS5BTransportPayload::ref payLoad = boost::make_shared<JingleS5BTransportPayload>();
+		payLoad->setSessionID("mysession");
+		jingleContentPayload->addTransport(payLoad);
+		return payLoad;
+	}
+
+	boost::shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() {
+		JingleIBBTransportPayload::ref payLoad = boost::make_shared<JingleIBBTransportPayload>();
+		payLoad->setSessionID("mysession");
+		jingleContentPayload->addTransport(payLoad);
+		return payLoad;
+	}
+	
+	JingleContentID getContentID() const  {
+		return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator());
+	}
+
+	template <typename T> T getCall(int i) const {
+		size_t index = static_cast<size_t>(i);
+		CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size());
+		T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]);
+		CPPUNIT_ASSERT(cmd);
+		return *cmd;
+	}
+
+private:
+	std::vector<unsigned char> data;
+	boost::shared_ptr<ByteArrayReadBytestream> stream;
+	FakeJingleSession* fakeJingleSession;
+	boost::shared_ptr<JingleContentPayload> jingleContentPayload;
+	boost::shared_ptr<OFakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF;
+	boost::shared_ptr<OFakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF;
+	DummyStanzaChannel* stanzaChannel;
+	IQRouter* iqRouter;
+	IDGenerator* idGen;
+	EventLoop *eventLoop;
+	SOCKS5BytestreamRegistry* s5bRegistry;
+	SOCKS5BytestreamProxy* s5bProxy;
+	DummyTimerFactory* timerFactory;
+	DummyConnectionFactory* connectionFactory;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(OutgoingJingleFileTransferTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
new file mode 100644
index 0000000..35580bf
--- /dev/null
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Base/ByteArray.h>
+#include <QA/Checker/IO.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/bind.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Concat.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/StartStopper.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/FileTransfer/ByteArrayReadBytestream.h>
+#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h>
+#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/DummyConnection.h>
+#include <Swiften/Network/DummyTimerFactory.h>
+#include <Swiften/StringCodecs/Hexify.h>
+
+using namespace Swift;
+
+boost::mt19937 randomGen;
+
+class SOCKS5BytestreamClientSessionTest : public CppUnit::TestFixture {
+	CPPUNIT_TEST_SUITE(SOCKS5BytestreamClientSessionTest);
+	CPPUNIT_TEST(testForSessionReady);
+	CPPUNIT_TEST(testErrorHandlingHello);
+	CPPUNIT_TEST(testErrorHandlingRequest);
+	CPPUNIT_TEST(testWriteBytestream);
+	CPPUNIT_TEST(testReadBytestream);
+	CPPUNIT_TEST_SUITE_END();
+
+	const HostAddressPort destinationAddressPort;
+	const std::string destination;
+
+public:
+	SOCKS5BytestreamClientSessionTest() : destinationAddressPort(HostAddressPort(HostAddress("127.0.0.1"), 8888)),
+		destination(SOCKS5BytestreamRegistry::getHostname("foo", JID("requester@example.com/test"), JID("target@example.com/test"))), eventLoop(NULL), timerFactory(NULL) { }
+
+	void setUp() {
+		randomGen.seed(time(NULL));
+		eventLoop = new DummyEventLoop();
+		timerFactory = new DummyTimerFactory();
+		connection = boost::make_shared<MockeryConnection>(failingPorts, true, eventLoop);
+		//connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
+		//stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
+//		connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSessionTest::handleDataRead, this, _1));
+	}
+
+	void tearDown() {
+		//connection.reset();
+		delete timerFactory;
+		delete eventLoop;
+	}
+
+	void testForSessionReady() {
+		TestHelper helper;
+		connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+		SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+		clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+		clientSession->start();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT(createByteArray("\x05\x01\x00", 3) == helper.unprocessedInput);
+
+		helper.unprocessedInput.clear();
+		serverRespondHelloOK();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+		CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+		CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+		helper.unprocessedInput.clear();
+		serverRespondRequestOK();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+		CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+	}
+
+	void testErrorHandlingHello() {
+		TestHelper helper;
+		connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+		SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+		clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+		clientSession->start();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+		helper.unprocessedInput.clear();
+		serverRespondHelloAuthFail();
+		eventLoop->processEvents();
+
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+		CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+	}
+
+	void testErrorHandlingRequest() {
+		TestHelper helper;
+		connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+		SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+		clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+		clientSession->start();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput);
+
+		helper.unprocessedInput.clear();
+		serverRespondHelloOK();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4));
+		CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4]));
+		CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size()));
+		CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1));
+
+		helper.unprocessedInput.clear();
+		serverRespondRequestFail();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError);
+		CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled);
+	}
+
+	void testWriteBytestream() {
+		TestHelper helper;
+		connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+		SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+		clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+		clientSession->start();
+		eventLoop->processEvents();
+
+		helper.unprocessedInput.clear();
+		serverRespondHelloOK();
+		eventLoop->processEvents();
+
+		helper.unprocessedInput.clear();
+		serverRespondRequestOK();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+		CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+		boost::shared_ptr<ByteArrayWriteBytestream> output = boost::make_shared<ByteArrayWriteBytestream>();
+		clientSession->startReceiving(output);
+
+		ByteArray transferData = generateRandomByteArray(1024);
+		connection->onDataRead(createSafeByteArray(transferData.data(), transferData.size()));
+		CPPUNIT_ASSERT_EQUAL(transferData, output->getData());
+	}
+
+	void testReadBytestream() {
+		TestHelper helper;
+		connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1));
+
+		SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory);
+		clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1));
+
+		clientSession->start();
+		eventLoop->processEvents();
+
+		helper.unprocessedInput.clear();
+		serverRespondHelloOK();
+		eventLoop->processEvents();
+
+		helper.unprocessedInput.clear();
+		serverRespondRequestOK();
+		eventLoop->processEvents();
+		CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled);
+		CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError);
+
+		helper.unprocessedInput.clear();
+		ByteArray transferData = generateRandomByteArray(1024 * 1024);
+		boost::shared_ptr<ByteArrayReadBytestream> input = boost::make_shared<ByteArrayReadBytestream>(transferData);
+		clientSession->startSending(input);
+		eventLoop->processEvents();
+
+		CPPUNIT_ASSERT_EQUAL(createByteArray(transferData.data(), transferData.size()), helper.unprocessedInput);
+	}
+
+
+
+private:
+	static ByteArray generateRandomByteArray(size_t len) {
+		boost::uniform_int<> dist(0, 255);
+		boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomByte(randomGen, dist);
+		ByteArray result(len);
+		for (size_t i=0; i < len; ++i ) {
+			result[i] = randomByte();
+		}
+		return result;
+	}
+
+	// Server responses
+	void serverRespondHelloOK() {
+		connection->onDataRead(createSafeByteArray("\x05\00", 2));
+	}
+
+	void serverRespondHelloAuthFail() {
+		connection->onDataRead(createSafeByteArray("\x05\xFF", 2));
+	}
+
+	void serverRespondRequestOK() {
+		SafeByteArray dataToSend = createSafeByteArray("\x05\x00\x00\x03", 4);
+		append(dataToSend, createSafeByteArray(destination.size()));
+		append(dataToSend, createSafeByteArray(destination));
+		append(dataToSend, createSafeByteArray("\x00", 1));
+		connection->onDataRead(dataToSend);
+	}
+
+	void serverRespondRequestFail() {
+		SafeByteArray correctData = createSafeByteArray("\x05\x00\x00\x03", 4);
+		append(correctData, createSafeByteArray(destination.size()));
+		append(correctData, createSafeByteArray(destination));
+		append(correctData, createSafeByteArray("\x00", 1));
+
+		SafeByteArray dataToSend;
+		//ByteArray failingData = Hexify::unhexify("8417947d1d305c72c11520ea7d2c6e787396705e72c312c6ccc3f66613d7cae1b91b7ab48e8b59a17d559c15fb51");
+		//append(dataToSend, failingData);
+		//SWIFT_LOG(debug) << "hexed: " << Hexify::hexify(failingData) << std::endl;
+		do {
+			ByteArray rndArray = generateRandomByteArray(correctData.size());
+			dataToSend = createSafeByteArray(rndArray.data(), rndArray.size());
+		} while (dataToSend == correctData);
+		connection->onDataRead(dataToSend);
+	}
+
+private:
+	struct TestHelper {
+		TestHelper() : sessionReadyCalled(false), sessionReadyError(false) {}
+		ByteArray unprocessedInput;
+		bool sessionReadyCalled;
+		bool sessionReadyError;
+
+		void handleConnectionDataWritten(const SafeByteArray& data) {
+			append(unprocessedInput, data);
+			//SWIFT_LOG(debug) << "unprocessedInput (" << unprocessedInput.size() <<  "): " << Hexify::hexify(unprocessedInput) << std::endl;
+		}
+
+		void handleSessionReady(bool error) {
+			sessionReadyCalled = true;
+			sessionReadyError = error;
+		}
+	};
+
+
+private:
+	struct MockeryConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<MockeryConnection> {
+		public:
+		MockeryConnection(const std::vector<HostAddressPort>& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive), disconnectCalled(false) {}
+
+			void listen() { assert(false); }
+			void connect(const HostAddressPort& address) {
+				hostAddressPort = address;
+				if (isResponsive) {
+					bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end();
+					eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail));
+				}
+			}
+
+			HostAddressPort getLocalAddress() const { return HostAddressPort(); }
+			void disconnect() {
+				disconnectCalled = true;
+			}
+
+			void write(const SafeByteArray& data) {
+				eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this());
+				onDataSent(data);
+			}
+
+			boost::signal<void (const SafeByteArray&)> onDataSent;
+
+			EventLoop* eventLoop;
+			boost::optional<HostAddressPort> hostAddressPort;
+			std::vector<HostAddressPort> failingPorts;
+			bool isResponsive;
+			bool disconnectCalled;
+	};
+
+private:
+	DummyEventLoop* eventLoop;
+	DummyTimerFactory* timerFactory;
+	boost::shared_ptr<MockeryConnection> connection;
+	const std::vector<HostAddressPort> failingPorts;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamClientSessionTest);
diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
index 06bc98f..cd480f0 100644
--- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
+++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp
@@ -34,6 +34,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 		void setUp() {
 			receivedDataChunks = 0;
 			eventLoop = new DummyEventLoop();
+			bytestreams = new SOCKS5BytestreamRegistry();
 			connection = boost::shared_ptr<DummyConnection>(new DummyConnection(eventLoop));
 			connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1));
 			stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg")));
@@ -41,6 +42,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 
 		void tearDown() {
 			connection.reset();
+			delete bytestreams;
 			delete eventLoop;
 		}
 
@@ -67,7 +69,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 		void testRequest() {
 			boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
 			StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
-			bytestreams.addBytestream("abcdef", stream1);
+			bytestreams->addReadBytestream("abcdef", stream1);
 			authenticate();
 
 			ByteArray hostname(createByteArray("abcdef"));
@@ -88,10 +90,11 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 		void testReceiveData() {
 			boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
 			StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
-			bytestreams.addBytestream("abcdef", stream1);
+			bytestreams->addReadBytestream("abcdef", stream1);
 			authenticate();
 			request("abcdef");
 			eventLoop->processEvents();
+			testling->startTransfer();
 			skipHeader("abcdef");
 
 			CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
@@ -102,11 +105,12 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 			boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession());
 			testling->setChunkSize(3);
 			StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get());
-			bytestreams.addBytestream("abcdef", stream1);
+			bytestreams->addReadBytestream("abcdef", stream1);
 			authenticate();
 			request("abcdef");
 			eventLoop->processEvents();
-
+			testling->startTransfer();
+			eventLoop->processEvents();
 			skipHeader("abcdef");
 			CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData);
 			CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);
@@ -141,13 +145,13 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {
 
 	private:
 		SOCKS5BytestreamServerSession* createSession() {
-			SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, &bytestreams);
+			SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, bytestreams);
 			return session;
 		}
 
 	private:
 		DummyEventLoop* eventLoop;
-		SOCKS5BytestreamRegistry bytestreams;
+		SOCKS5BytestreamRegistry* bytestreams;
 		boost::shared_ptr<DummyConnection> connection;
 		std::vector<unsigned char> receivedData;
 		int receivedDataChunks;
diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h
index c27aeff..fb6f2f1 100644
--- a/Swiften/FileTransfer/WriteBytestream.h
+++ b/Swiften/FileTransfer/WriteBytestream.h
@@ -9,6 +9,8 @@
 #include <boost/shared_ptr.hpp>
 #include <vector>
 
+#include <Swiften/Base/boost_bsignals.h>
+
 namespace Swift {
 	class WriteBytestream {
 		public:
@@ -17,5 +19,7 @@ namespace Swift {
 			virtual ~WriteBytestream();
 
 			virtual void write(const std::vector<unsigned char>&) = 0;
+
+			boost::signal<void (const std::vector<unsigned char>&)> onWrite;
 	};
 }
diff --git a/Swiften/Jingle/FakeJingleSession.cpp b/Swiften/Jingle/FakeJingleSession.cpp
new file mode 100644
index 0000000..82ec631
--- /dev/null
+++ b/Swiften/Jingle/FakeJingleSession.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Jingle/FakeJingleSession.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+
+FakeJingleSession::FakeJingleSession(const JID& initiator, const std::string& id) : JingleSession(initiator, id) {
+
+}
+
+FakeJingleSession::~FakeJingleSession() {
+}
+
+void FakeJingleSession::sendInitiate(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(InitiateCall(id, desc, payload));
+}
+
+void FakeJingleSession::sendTerminate(JinglePayload::Reason::Type reason) {
+	calledCommands.push_back(TerminateCall(reason));
+}
+
+void FakeJingleSession::sendInfo(boost::shared_ptr<Payload> payload) {
+	calledCommands.push_back(InfoCall(payload));
+}
+
+void FakeJingleSession::sendAccept(const JingleContentID& id, JingleDescription::ref desc, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(AcceptCall(id, desc, payload));
+}
+
+void FakeJingleSession::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(InfoTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(AcceptTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportReject(const JingleContentID& id, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(RejectTransportCall(id, payload));
+}
+
+void FakeJingleSession::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref payload) {
+	calledCommands.push_back(ReplaceTransportCall(id, payload));
+}
+
+}
diff --git a/Swiften/Jingle/FakeJingleSession.h b/Swiften/Jingle/FakeJingleSession.h
new file mode 100644
index 0000000..fd3d7b7
--- /dev/null
+++ b/Swiften/Jingle/FakeJingleSession.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+#include <vector>
+#include <boost/variant.hpp>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Jingle/JingleSession.h>
+#include <Swiften/Jingle/JingleContentID.h>
+
+namespace Swift {
+	class JingleContentID;
+
+	class FakeJingleSession : public JingleSession {
+		public:
+		struct InitiateCall {
+			InitiateCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {}
+			JingleContentID id;
+			JingleDescription::ref description;
+			JingleTransportPayload::ref payload;
+		};
+
+		struct  TerminateCall {
+			TerminateCall(JinglePayload::Reason::Type r) : reason(r) {}
+			JinglePayload::Reason::Type reason;
+		};
+
+		struct InfoCall {
+			InfoCall(boost::shared_ptr<Payload> info) : payload(info) {}
+			boost::shared_ptr<Payload> payload;
+		};
+
+		struct AcceptCall {
+			AcceptCall(JingleContentID contentId, JingleDescription::ref desc, JingleTransportPayload::ref payL) : id(contentId), description(desc), payload(payL) {}
+			JingleContentID id;
+			JingleDescription::ref description;
+			JingleTransportPayload::ref payload;
+		};
+
+		struct InfoTransportCall {
+			InfoTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+			JingleContentID id;
+			JingleTransportPayload::ref payload;
+		};
+
+		struct AcceptTransportCall {
+			AcceptTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+			JingleContentID id;
+			JingleTransportPayload::ref payload;
+		};
+
+		struct RejectTransportCall {
+			RejectTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+			JingleContentID id;
+			JingleTransportPayload::ref payload;
+		};
+		
+		struct ReplaceTransportCall {
+			ReplaceTransportCall(JingleContentID contentId, JingleTransportPayload::ref payL) : id(contentId), payload(payL) {}
+			JingleContentID id;
+			JingleTransportPayload::ref payload;
+		};
+
+		typedef boost::variant<InitiateCall, TerminateCall, AcceptCall, InfoCall, InfoTransportCall, AcceptTransportCall, RejectTransportCall, ReplaceTransportCall> Command;
+
+		public:
+			typedef boost::shared_ptr<FakeJingleSession> ref;
+
+			FakeJingleSession(const JID& initiator, const std::string& id);
+			virtual ~FakeJingleSession();
+
+			virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+			virtual void sendTerminate(JinglePayload::Reason::Type reason);
+			virtual void sendInfo(boost::shared_ptr<Payload>);
+			virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref());
+			virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref);
+
+		public:
+			std::vector<Command> calledCommands;
+	};
+}
diff --git a/Swiften/Jingle/JingleContentID.h b/Swiften/Jingle/JingleContentID.h
index 8d75581..fc0cc8f 100644
--- a/Swiften/Jingle/JingleContentID.h
+++ b/Swiften/Jingle/JingleContentID.h
@@ -15,6 +15,14 @@ namespace Swift {
 		public:
 			JingleContentID(const std::string& name, JingleContentPayload::Creator creator) : name(name), creator(creator) {
 			}
+			
+			const std::string getName() const {
+				return this->name;
+			}
+			
+			JingleContentPayload::Creator getCreator() const {
+				return this->creator;
+			}
 
 		private:
 			std::string name;
diff --git a/Swiften/Jingle/JingleResponder.cpp b/Swiften/Jingle/JingleResponder.cpp
index 198f9a2..63f108e 100644
--- a/Swiften/Jingle/JingleResponder.cpp
+++ b/Swiften/Jingle/JingleResponder.cpp
@@ -11,9 +11,14 @@
 #include <Swiften/Jingle/JingleSessionManager.h>
 #include <Swiften/Jingle/JingleSessionImpl.h>
 
+#include <Swiften/Base/Log.h>
+
 namespace Swift {
 
-JingleResponder::JingleResponder(JingleSessionManager* sessionManager, IQRouter* router) : SetResponder<JinglePayload>(router), sessionManager(sessionManager) {
+JingleResponder::JingleResponder(JingleSessionManager* sessionManager, IQRouter* router) : SetResponder<JinglePayload>(router), sessionManager(sessionManager), router(router) {
+}
+
+JingleResponder::~JingleResponder() {
 }
 
 bool JingleResponder::handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<JinglePayload> payload) {
@@ -24,17 +29,29 @@ bool JingleResponder::handleSetRequest(const JID& from, const JID&, const std::s
 		}
 		else {
 			sendResponse(from, id, boost::shared_ptr<JinglePayload>());
-			JingleSessionImpl::ref session = boost::make_shared<JingleSessionImpl>(payload->getInitiator(), payload->getSessionID());
-			sessionManager->handleIncomingSession(from, session, payload->getContents());
+			if (!payload->getInitiator().isBare()) {
+				JingleSessionImpl::ref session = boost::make_shared<JingleSessionImpl>(payload->getInitiator(), from, payload->getSessionID(), router);
+				sessionManager->handleIncomingSession(from, session, payload->getContents());
+			} else {
+				SWIFT_LOG(debug) << "Unable to create Jingle session due to initiator not being a full JID." << std::endl;
+			}
 		}
 	}
 	else {
-		JingleSessionImpl::ref session = sessionManager->getSession(from, payload->getSessionID());
+		JingleSessionImpl::ref session;
+		if (payload->getInitiator().isValid()) {
+			SWIFT_LOG(debug) << "Lookup session by initiator." << std::endl;
+			session = sessionManager->getSession(payload->getInitiator(), payload->getSessionID());
+		} else {
+			SWIFT_LOG(debug) << "Lookup session by from attribute." << std::endl;
+			session = sessionManager->getSession(from, payload->getSessionID());
+		}
 		if (session) {
 			session->handleIncomingAction(payload);
 			sendResponse(from, id, boost::shared_ptr<JinglePayload>());
 		}
 		else {
+			std::cerr << "WARN: Didn't find jingle session!" << std::endl;
 			// TODO: Add jingle-specific error
 			sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
 		}
diff --git a/Swiften/Jingle/JingleResponder.h b/Swiften/Jingle/JingleResponder.h
index 6e1965c..6f4d688 100644
--- a/Swiften/Jingle/JingleResponder.h
+++ b/Swiften/Jingle/JingleResponder.h
@@ -16,11 +16,12 @@ namespace Swift {
 	class JingleResponder : public SetResponder<JinglePayload> {
 		public:
 			JingleResponder(JingleSessionManager* sessionManager, IQRouter* router);
-
+			virtual ~JingleResponder();
 		private:
 			virtual bool handleSetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr<JinglePayload> payload);
 
 		private:
 			JingleSessionManager* sessionManager;
+			IQRouter* router;
 	};
 }
diff --git a/Swiften/Jingle/JingleSession.cpp b/Swiften/Jingle/JingleSession.cpp
index 1366191..eb649f3 100644
--- a/Swiften/Jingle/JingleSession.cpp
+++ b/Swiften/Jingle/JingleSession.cpp
@@ -11,7 +11,9 @@
 namespace Swift {
 
 JingleSession::JingleSession(const JID& initiator, const std::string& id) : initiator(initiator), id(id) {
-
+	// initiator must always be a full JID; session lookup based on it wouldn't work otherwise
+	// this is checked on an upper level so that the assert never fails
+	assert(!initiator.isBare());
 }
 
 JingleSession::~JingleSession() {
diff --git a/Swiften/Jingle/JingleSession.h b/Swiften/Jingle/JingleSession.h
index b8117bb..150ad79 100644
--- a/Swiften/Jingle/JingleSession.h
+++ b/Swiften/Jingle/JingleSession.h
@@ -7,6 +7,8 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
 #include <string>
 
 #include <Swiften/Base/boost_bsignals.h>
@@ -30,16 +32,22 @@ namespace Swift {
 			const std::string& getID() const {
 				return id;
 			}
-
-			virtual void terminate(JinglePayload::Reason::Type reason) = 0;
-			virtual void accept(JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0;
+			virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref) = 0;
+			virtual void sendTerminate(JinglePayload::Reason::Type reason) = 0;
+			virtual void sendInfo(boost::shared_ptr<Payload>) = 0;
+			virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0;
 			virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) = 0;
-			virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref) = 0;
-			virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref) = 0;
+			virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref) = 0;
+			virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref) = 0;
+			virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref) = 0;
 
 		public:
-			boost::signal<void ()> onSessionTerminateReceived;
+			boost::signal<void (const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref)> onSessionAcceptReceived;
+			boost::signal<void (JinglePayload::ref)> onSessionInfoReceived;
+			boost::signal<void (boost::optional<JinglePayload::Reason>)> onSessionTerminateReceived;
+			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportAcceptReceived;
 			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportInfoReceived;
+			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportRejectReceived;
 			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportReplaceReceived;
 
 		private:
diff --git a/Swiften/Jingle/JingleSessionImpl.cpp b/Swiften/Jingle/JingleSessionImpl.cpp
index cbb2b42..98c5196 100644
--- a/Swiften/Jingle/JingleSessionImpl.cpp
+++ b/Swiften/Jingle/JingleSessionImpl.cpp
@@ -8,33 +8,176 @@
 
 #include <boost/smart_ptr/make_shared.hpp>
 
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+#include <Swiften/Jingle/JingleContentID.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Queries/Request.h>
+#include <Swiften/Queries/GenericRequest.h>
+
+#include <Swiften/Base/Log.h>
+
+#include "Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h"
+#include "Swiften/FileTransfer/JingleTransport.h"
+
 namespace Swift {
 
-JingleSessionImpl::JingleSessionImpl(const JID& initiator, const std::string& id) : JingleSession(initiator, id) {
+JingleSessionImpl::JingleSessionImpl(const JID& initiator, const JID& peerJID, const std::string& id, IQRouter* router) : JingleSession(initiator, id), iqRouter(router), peerJID(peerJID) {
+	SWIFT_LOG(debug) << "initiator: " << initiator << ", peerJID: " << peerJID << std::endl;
+}
+
+void JingleSessionImpl::handleIncomingAction(JinglePayload::ref action) {
+	if (action->getAction() == JinglePayload::SessionTerminate) {
+		onSessionTerminateReceived(action->getReason());
+		return;
+	}
+	if (action->getAction() == JinglePayload::SessionInfo) {
+		onSessionInfoReceived(action);
+		return;
+	}
+
+	JingleContentPayload::ref content = action->getPayload<JingleContentPayload>();
+	if (!content) {
+		SWIFT_LOG(debug) << "no content payload!" << std::endl;
+		return;
+	}
+	JingleContentID contentID(content->getName(), content->getCreator());
+	JingleDescription::ref description = content->getDescriptions().empty() ? JingleDescription::ref() : content->getDescriptions()[0];
+	JingleTransportPayload::ref transport = content->getTransports().empty() ? JingleTransportPayload::ref() : content->getTransports()[0];
+	switch(action->getAction()) {
+		case JinglePayload::SessionAccept:
+			onSessionAcceptReceived(contentID, description, transport);
+			return;
+		case JinglePayload::TransportAccept:
+			onTransportAcceptReceived(contentID, transport);
+			return;
+		case JinglePayload::TransportInfo:
+			onTransportInfoReceived(contentID, transport);
+			return;
+		case JinglePayload::TransportReject:
+			onTransportRejectReceived(contentID, transport);
+			return;
+		case JinglePayload::TransportReplace:
+			onTransportReplaceReceived(contentID, transport);
+			return;
+		// following unused Jingle actions
+		case JinglePayload::ContentAccept:
+		case JinglePayload::ContentAdd:
+		case JinglePayload::ContentModify:
+		case JinglePayload::ContentReject:
+		case JinglePayload::ContentRemove:
+		case JinglePayload::DescriptionInfo:
+		case JinglePayload::SecurityInfo:
+
+		// handled elsewhere
+		case JinglePayload::SessionInitiate:
+		case JinglePayload::SessionInfo:
+		case JinglePayload::SessionTerminate:
+
+		case JinglePayload::UnknownAction:
+			return;
+	}
+	std::cerr << "Unhandled Jingle action!!! ACTION: " << action->getAction() << std::endl;
 }
 
-void JingleSessionImpl::handleIncomingAction(JinglePayload::ref) {
+void JingleSessionImpl::sendInitiate(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transport) {
+	JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionInitiate, getID());
+	payload->setInitiator(getInitiator());
+	JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+	content->setCreator(id.getCreator());
+	content->setName(id.getName());
+	content->addDescription(description);
+	content->addTransport(transport);
+	payload->addPayload(content);
+
+	sendSetRequest(payload);
 }
 
-void JingleSessionImpl::terminate(JinglePayload::Reason::Type reason) {
+void JingleSessionImpl::sendTerminate(JinglePayload::Reason::Type reason) {
 	JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionTerminate, getID());
 	payload->setReason(JinglePayload::Reason(reason));
-	//onAction(payload)
+	payload->setInitiator(getInitiator());
+	sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendInfo(boost::shared_ptr<Payload> info) {
+	JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionInfo, getID());
+	payload->addPayload(info);
+
+	sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendAccept(const JingleContentID& id, JingleDescription::ref description, JingleTransportPayload::ref transPayload) {
+	JinglePayload::ref payload = createPayload();
+	
+	JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+	content->setCreator(id.getCreator());
+	content->setName(id.getName());
+	content->addTransport(transPayload);
+	content->addDescription(description);
+	payload->setAction(JinglePayload::SessionAccept);
+	payload->addPayload(content);
+	
+	// put into IQ:set and send it away
+	sendSetRequest(payload);
 }
 
-void JingleSessionImpl::acceptTransport(const JingleContentID&, JingleTransportPayload::ref) {
 
+void JingleSessionImpl::sendTransportAccept(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+	JinglePayload::ref payload = createPayload();
+
+	JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+	content->setCreator(id.getCreator());
+	content->setName(id.getName());
+	content->addTransport(transPayload);
+	payload->setAction(JinglePayload::TransportAccept);
+	payload->addPayload(content);
+
+	// put into IQ:set and send it away
+	sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendTransportInfo(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+	JinglePayload::ref payload = createPayload();
+
+	JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+	content->setCreator(id.getCreator());
+	content->setName(id.getName());
+	content->addTransport(transPayload);
+	payload->setAction(JinglePayload::TransportInfo);
+	payload->addPayload(content);
+
+	sendSetRequest(payload);
+}
+
+void JingleSessionImpl::sendTransportReject(const JingleContentID& /* id */, JingleTransportPayload::ref /* transPayload */) {
+	SWIFT_LOG(debug) << "NOT IMPLEMENTED YET!!!!" << std::endl;
 }
 
-void JingleSessionImpl::rejectTransport(const JingleContentID&, JingleTransportPayload::ref) {
+void JingleSessionImpl::sendTransportReplace(const JingleContentID& id, JingleTransportPayload::ref transPayload) {
+	JinglePayload::ref payload = createPayload();
 
+	JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+	content->setCreator(id.getCreator());
+	content->setName(id.getName());
+	content->addTransport(transPayload);
+	payload->setAction(JinglePayload::TransportReplace);
+	payload->addContent(content);
+
+	sendSetRequest(payload);
 }
 
-void JingleSessionImpl::accept(JingleTransportPayload::ref) {
+
+void JingleSessionImpl::sendSetRequest(JinglePayload::ref payload) {
+	boost::shared_ptr<GenericRequest<JinglePayload> > request = boost::make_shared<GenericRequest<JinglePayload> >(IQ::Set, peerJID, payload, iqRouter);
+	request->send();
 }
 
-void JingleSessionImpl::sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) {
 
+JinglePayload::ref JingleSessionImpl::createPayload() const {
+	JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+	payload->setSessionID(getID());
+	payload->setInitiator(getInitiator());
+	return payload;
 }
 
 
diff --git a/Swiften/Jingle/JingleSessionImpl.h b/Swiften/Jingle/JingleSessionImpl.h
index a254ead..3c1559a 100644
--- a/Swiften/Jingle/JingleSessionImpl.h
+++ b/Swiften/Jingle/JingleSessionImpl.h
@@ -11,20 +11,32 @@
 #include <Swiften/Jingle/JingleSession.h>
 
 namespace Swift {
+	class IQRouter;
+
 	class JingleSessionImpl : public JingleSession {
 			friend class JingleResponder;
 		public:
 			typedef boost::shared_ptr<JingleSessionImpl> ref;
 
-			JingleSessionImpl(const JID& initiator, const std::string& id);
+			JingleSessionImpl(const JID& initiator, const JID&, const std::string& id, IQRouter* router);
 
-			virtual void terminate(JinglePayload::Reason::Type reason);
-			virtual void accept(JingleTransportPayload::ref);
+			virtual void sendInitiate(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
+			virtual void sendTerminate(JinglePayload::Reason::Type reason);
+			virtual void sendInfo(boost::shared_ptr<Payload>);
+			virtual void sendAccept(const JingleContentID&, JingleDescription::ref, JingleTransportPayload::ref);
 			virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref);
-			virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref);
-			virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportAccept(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportReject(const JingleContentID&, JingleTransportPayload::ref);
+			virtual void sendTransportReplace(const JingleContentID&, JingleTransportPayload::ref);
 
 		private:
 			void handleIncomingAction(JinglePayload::ref);
+			
+			void sendSetRequest(JinglePayload::ref payload);
+			JinglePayload::ref createPayload() const;
+			
+		private:
+			IQRouter *iqRouter;
+			JID peerJID;
 	};
 }
diff --git a/Swiften/Jingle/JingleSessionManager.cpp b/Swiften/Jingle/JingleSessionManager.cpp
index f9a94f3..4299a1e 100644
--- a/Swiften/Jingle/JingleSessionManager.cpp
+++ b/Swiften/Jingle/JingleSessionManager.cpp
@@ -14,15 +14,17 @@ namespace Swift {
 
 JingleSessionManager::JingleSessionManager(IQRouter* router) : router(router) {
 	responder = new JingleResponder(this, router);
+	responder->start();
 }
 
 JingleSessionManager::~JingleSessionManager() {
+	responder->stop();
 	delete responder;
 }
 
 JingleSessionImpl::ref JingleSessionManager::getSession(const JID& jid, const std::string& id) const {
-	SessionMap::const_iterator i = incomingSessions.find(JIDSession(jid, id));
-	return i != incomingSessions.end() ? i->second : JingleSessionImpl::ref();
+	SessionMap::const_iterator i = sessions.find(JIDSession(jid, id));
+	return i != sessions.end() ? i->second : JingleSessionImpl::ref();
 }
 
 void JingleSessionManager::addIncomingSessionHandler(IncomingJingleSessionHandler* handler) {
@@ -33,8 +35,13 @@ void JingleSessionManager::removeIncomingSessionHandler(IncomingJingleSessionHan
 	erase(incomingSessionHandlers, handler);
 }
 
-void JingleSessionManager::handleIncomingSession(const JID& from, JingleSessionImpl::ref session, const std::vector<JingleContentPayload::ref>& contents) {
-	incomingSessions.insert(std::make_pair(JIDSession(from, session->getID()), session));
+void JingleSessionManager::registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref session) {
+	sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session));
+	SWIFT_LOG(debug) << "Added session " << session->getID() << " for initiator " << initiator.toString() << std::endl;
+}
+
+void JingleSessionManager::handleIncomingSession(const JID& initiator, JingleSessionImpl::ref session, const std::vector<JingleContentPayload::ref>& contents) {
+	sessions.insert(std::make_pair(JIDSession(initiator, session->getID()), session));
 	foreach (IncomingJingleSessionHandler* handler, incomingSessionHandlers) {
 		if (handler->handleIncomingJingleSession(session, contents)) {
 			return;
diff --git a/Swiften/Jingle/JingleSessionManager.h b/Swiften/Jingle/JingleSessionManager.h
index 3b23fb0..50c429b 100644
--- a/Swiften/Jingle/JingleSessionManager.h
+++ b/Swiften/Jingle/JingleSessionManager.h
@@ -28,22 +28,23 @@ namespace Swift {
 			void addIncomingSessionHandler(IncomingJingleSessionHandler* handler);
 			void removeIncomingSessionHandler(IncomingJingleSessionHandler* handler);
 
+			void registerOutgoingSession(const JID& initiator, JingleSessionImpl::ref);
 		protected:
-			void handleIncomingSession(const JID& from, JingleSessionImpl::ref, const std::vector<JingleContentPayload::ref>& contents);
+			void handleIncomingSession(const JID& initiator, JingleSessionImpl::ref, const std::vector<JingleContentPayload::ref>& contents);
 
 		private:
 			IQRouter* router;
 			JingleResponder* responder;
 			std::vector<IncomingJingleSessionHandler*> incomingSessionHandlers;
 			struct JIDSession {
-				JIDSession(const JID& jid, const std::string& session) : jid(jid), session(session) {}
+				JIDSession(const JID& initiator, const std::string& session) : initiator(initiator), session(session) {}
 				bool operator<(const JIDSession& o) const {
-					return jid == o.jid ? session < o.session : jid < o.jid;
+					return initiator == o.initiator ? session < o.session : initiator < o.initiator;
 				}
-				JID jid;
+				JID initiator;
 				std::string session;
 			};
 			typedef std::map<JIDSession, JingleSessionImpl::ref> SessionMap;
-			SessionMap incomingSessions;
+			SessionMap sessions;
 	};
 }
diff --git a/Swiften/Jingle/SConscript b/Swiften/Jingle/SConscript
index 6b3cfd3..5dcf293 100644
--- a/Swiften/Jingle/SConscript
+++ b/Swiften/Jingle/SConscript
@@ -6,6 +6,7 @@ sources = [
 		"IncomingJingleSessionHandler.cpp",
 		"JingleSessionManager.cpp",	
 		"JingleResponder.cpp",
+		"FakeJingleSession.cpp",
 	]
 
 swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
diff --git a/Swiften/Network/BoostNetworkFactories.cpp b/Swiften/Network/BoostNetworkFactories.cpp
index cc80197..c13270f 100644
--- a/Swiften/Network/BoostNetworkFactories.cpp
+++ b/Swiften/Network/BoostNetworkFactories.cpp
@@ -9,6 +9,7 @@
 #include <Swiften/Network/BoostConnectionFactory.h>
 #include <Swiften/Network/PlatformDomainNameResolver.h>
 #include <Swiften/Network/BoostConnectionServerFactory.h>
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
 
 namespace Swift {
 
@@ -17,9 +18,11 @@ BoostNetworkFactories::BoostNetworkFactories(EventLoop* eventLoop) {
 	connectionFactory = new BoostConnectionFactory(ioServiceThread.getIOService(), eventLoop);
 	domainNameResolver = new PlatformDomainNameResolver(eventLoop);
 	connectionServerFactory = new BoostConnectionServerFactory(ioServiceThread.getIOService(), eventLoop);
+	platformNATTraversalWorker = new PlatformNATTraversalWorker(eventLoop);
 }
 
 BoostNetworkFactories::~BoostNetworkFactories() {
+	delete platformNATTraversalWorker;
 	delete connectionServerFactory;
 	delete domainNameResolver;
 	delete connectionFactory;
diff --git a/Swiften/Network/BoostNetworkFactories.h b/Swiften/Network/BoostNetworkFactories.h
index 96bcc6c..a1cf9ae 100644
--- a/Swiften/Network/BoostNetworkFactories.h
+++ b/Swiften/Network/BoostNetworkFactories.h
@@ -37,11 +37,16 @@ namespace Swift {
 				return connectionServerFactory;
 			}
 
+			PlatformNATTraversalWorker* getPlatformNATTraversalWorker() const {
+				return platformNATTraversalWorker;
+			}
+
 		private:
 			BoostIOServiceThread ioServiceThread;
 			TimerFactory* timerFactory;
 			ConnectionFactory* connectionFactory;
 			DomainNameResolver* domainNameResolver;
 			ConnectionServerFactory* connectionServerFactory;
+			PlatformNATTraversalWorker* platformNATTraversalWorker;
 	};
 }
diff --git a/Swiften/Network/DummyConnectionFactory.h b/Swiften/Network/DummyConnectionFactory.h
new file mode 100644
index 0000000..e8a294e
--- /dev/null
+++ b/Swiften/Network/DummyConnectionFactory.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/DummyConnection.h>
+
+namespace Swift {
+
+class EventLoop;
+
+class DummyConnectionFactory : public ConnectionFactory {
+public:
+	DummyConnectionFactory(EventLoop *eventLoop) : eventLoop(eventLoop) {}
+	virtual ~DummyConnectionFactory() {}
+	virtual boost::shared_ptr<Connection> createConnection() {
+		return boost::make_shared<DummyConnection>(eventLoop);
+	}
+private:
+	EventLoop* eventLoop;
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..69b325c
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalForwardPortRequest.h"
+
+#include <natpmp.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+NATPMPNATTraversalForwardPortRequest::NATPMPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalForwardPortRequest(worker), mapping(mapping) {
+
+}
+
+NATPMPNATTraversalForwardPortRequest::~NATPMPNATTraversalForwardPortRequest() {
+
+}
+
+void NATPMPNATTraversalForwardPortRequest::runBlocking() {
+	boost::optional<PortMapping> result;
+
+	natpmp_t natpmp;
+	natpmpresp_t response;
+	initnatpmp(&natpmp, 0, 0);
+
+	do {
+		if (sendnewportmappingrequest(&natpmp, mapping.protocol == PortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, mapping.leaseInSeconds, mapping.publicPort, mapping.localPort) != 2) {
+			SWIFT_LOG(debug) << "Failed to send NAT-PMP port forwarding request!" << std::endl;
+			break;
+		}
+		int r = 0;
+
+		do {
+		  fd_set fds;
+		  struct timeval timeout;
+		  FD_ZERO(&fds);
+		  FD_SET(natpmp.s, &fds);
+		  getnatpmprequesttimeout(&natpmp, &timeout);
+		  select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+		  r = readnatpmpresponseorretry(&natpmp, &response);
+		} while(r == NATPMP_TRYAGAIN);
+
+		if (r == 0) {
+			if (response.pnu.newportmapping.privateport == mapping.localPort &&
+				response.pnu.newportmapping.mappedpublicport == mapping.publicPort) {
+				mapping.leaseInSeconds = response.pnu.newportmapping.lifetime;
+				result = boost::optional<PortMapping>(mapping);
+			}
+		} else {
+			SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+		}
+	} while(false);
+	closenatpmp(&natpmp);
+
+	onResult(result);
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..71d8621
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalForwardPortRequest.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalForwardPortRequest : public PlatformNATTraversalForwardPortRequest {
+public:
+	NATPMPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping, PlatformNATTraversalWorker*);
+	virtual ~NATPMPNATTraversalForwardPortRequest();
+
+	virtual void runBlocking();
+
+private:
+	PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..06a21a3
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalGetPublicIPRequest.h"
+
+#include <natpmp.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+NATPMPNATTraversalGetPublicIPRequest::NATPMPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalGetPublicIPRequest(worker) {
+
+}
+
+NATPMPNATTraversalGetPublicIPRequest::~NATPMPNATTraversalGetPublicIPRequest() {
+
+}
+
+/*
+TODO: a non-blocking solution should be possible too here
+void NATPMPNATTraversalGetPublicIPRequest::run() {
+	// we can run directly since libnatpmp's API is asynchronous
+	runBlocking();
+}*/
+
+void NATPMPNATTraversalGetPublicIPRequest::runBlocking() {
+	boost::optional<HostAddress> result;
+
+	natpmp_t natpmp;
+	natpmpresp_t response;
+	initnatpmp(&natpmp, 0, 0);
+
+	do {
+		if (sendpublicaddressrequest(&natpmp) != 2) {
+			SWIFT_LOG(debug) << "Failed to send NAT-PMP public address request!" << std::endl;
+			break;
+		}
+		int r = 0;
+
+		do {
+		  fd_set fds;
+		  struct timeval timeout;
+		  FD_ZERO(&fds);
+		  FD_SET(natpmp.s, &fds);
+		  getnatpmprequesttimeout(&natpmp, &timeout);
+		  select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+		  r = readnatpmpresponseorretry(&natpmp, &response);
+		} while(r == NATPMP_TRYAGAIN);
+
+		if (r == 0) {
+			result = boost::optional<HostAddress>(HostAddress(reinterpret_cast<const unsigned char*>(&(response.pnu.publicaddress.addr)), 4));
+		} else {
+			SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+		}
+	} while(false);
+	closenatpmp(&natpmp);
+	onResult(result);
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..6112091
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalGetPublicIPRequest : public PlatformNATTraversalGetPublicIPRequest {
+public:
+	NATPMPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker*);
+	virtual ~NATPMPNATTraversalGetPublicIPRequest();
+
+	//virtual void run();
+	virtual void runBlocking();
+};
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..c99ac92
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "NATPMPNATTraversalRemovePortForwardingRequest.h"
+
+#include <boost/format.hpp>
+
+#include <natpmp.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+NATPMPNATTraversalRemovePortForwardingRequest::NATPMPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalRemovePortForwardingRequest(worker), mapping(mapping) {
+
+}
+
+NATPMPNATTraversalRemovePortForwardingRequest::~NATPMPNATTraversalRemovePortForwardingRequest() {
+
+}
+
+void NATPMPNATTraversalRemovePortForwardingRequest::runBlocking() {
+	boost::optional<bool> result;
+
+	natpmp_t natpmp;
+	natpmpresp_t response;
+	initnatpmp(&natpmp, 0, 0);
+
+	do {
+		if (sendnewportmappingrequest(&natpmp, mapping.protocol == PortMapping::TCP ? NATPMP_PROTOCOL_TCP : NATPMP_PROTOCOL_UDP, 0, 0, mapping.localPort) != 2) {
+			SWIFT_LOG(debug) << "Failed to send NAT-PMP remove forwarding request!" << std::endl;
+			break;
+		}
+		int r = 0;
+
+		do {
+		  fd_set fds;
+		  struct timeval timeout;
+		  FD_ZERO(&fds);
+		  FD_SET(natpmp.s, &fds);
+		  getnatpmprequesttimeout(&natpmp, &timeout);
+		  select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
+		  r = readnatpmpresponseorretry(&natpmp, &response);
+		} while(r == NATPMP_TRYAGAIN);
+
+		if (r == 0) {
+			if (response.pnu.newportmapping.privateport == mapping.localPort &&
+				response.pnu.newportmapping.mappedpublicport == mapping.publicPort) {
+				mapping.leaseInSeconds = response.pnu.newportmapping.lifetime;
+				result = boost::optional<bool>(true);
+			}
+		} else {
+			result = boost::optional<bool>(false);
+			SWIFT_LOG(debug) << "Inavlid NAT-PMP response." << std::endl;
+		}
+	} while(false);
+	closenatpmp(&natpmp);
+
+	onResult(result);
+}
+
+HostAddress NATPMPNATTraversalRemovePortForwardingRequest::getLocalClient() {
+	PlatformNetworkEnvironment env;
+
+	foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+		if (!iface->isLoopback()) {
+			foreach (HostAddress address, iface->getAddresses()) {
+				if (address.getRawAddress().is_v4()) {
+					return address;
+				}
+			}
+		}
+	}
+	return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..c4ffcf3
--- /dev/null
+++ b/Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+class NATPMPNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRemovePortForwardingRequest {
+public:
+	NATPMPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping, PlatformNATTraversalWorker*);
+	virtual ~NATPMPNATTraversalRemovePortForwardingRequest();
+
+	virtual void runBlocking();
+
+private:
+	HostAddress getLocalClient();
+
+private:
+	PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/NetworkEnvironment.h b/Swiften/Network/NetworkEnvironment.h
new file mode 100644
index 0000000..348bdb9
--- /dev/null
+++ b/Swiften/Network/NetworkEnvironment.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class NetworkEnvironment {
+public:
+	virtual ~NetworkEnvironment() {};
+	virtual std::vector<NetworkInterface::ref> getNetworkInterfaces() = 0;
+
+	boost::signal <void (NetworkInterface::ref)> onNetworkInterfaceChange;
+};
+
+}
diff --git a/Swiften/Network/NetworkFactories.h b/Swiften/Network/NetworkFactories.h
index d0d2299..e7d2ff0 100644
--- a/Swiften/Network/NetworkFactories.h
+++ b/Swiften/Network/NetworkFactories.h
@@ -11,6 +11,7 @@ namespace Swift {
 	class ConnectionFactory;
 	class DomainNameResolver;
 	class ConnectionServerFactory;
+	class PlatformNATTraversalWorker;
 
 	/**
 	 * An interface collecting network factories.
@@ -23,5 +24,6 @@ namespace Swift {
 			virtual ConnectionFactory* getConnectionFactory() const = 0;
 			virtual DomainNameResolver* getDomainNameResolver() const = 0;
 			virtual ConnectionServerFactory* getConnectionServerFactory() const = 0;
+			virtual PlatformNATTraversalWorker* getPlatformNATTraversalWorker() const = 0;
 	};
 }
diff --git a/Swiften/Network/NetworkInterface.h b/Swiften/Network/NetworkInterface.h
new file mode 100644
index 0000000..062e1f9
--- /dev/null
+++ b/Swiften/Network/NetworkInterface.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Network/HostAddress.h>
+
+namespace Swift {
+
+class NetworkInterface {
+public:
+	typedef boost::shared_ptr<NetworkInterface> ref;
+
+public:
+	enum InterfaceType {
+		WLAN,
+		Ethernet,
+		Mobile,
+		VPN,
+	};
+
+public:
+	virtual ~NetworkInterface() {};
+	virtual std::vector<HostAddress> getAddresses() = 0;
+	virtual std::string getName() = 0;
+	virtual bool isLoopback() = 0;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp b/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..b28024a
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalForwardPortRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalForwardPortRequest::PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalForwardPortRequest::~PlatformNATTraversalForwardPortRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalForwardPortRequest.h b/Swiften/Network/PlatformNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..cb1750c
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalForwardPortRequest.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalForwardPortRequest : public PlatformNATTraversalRequest {
+public:
+	struct PortMapping {
+		enum Protocol {
+			TCP,
+			UDP,
+		};
+
+		unsigned int publicPort;
+		unsigned int localPort;
+		Protocol protocol;
+		unsigned long leaseInSeconds;
+	};
+
+public:
+	PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker);
+	virtual ~PlatformNATTraversalForwardPortRequest();
+
+	boost::signal<void (boost::optional<PortMapping>)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..7a57e30
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalGetPublicIPRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalGetPublicIPRequest::PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalGetPublicIPRequest::~PlatformNATTraversalGetPublicIPRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..1cb37fe
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalGetPublicIPRequest : public PlatformNATTraversalRequest {
+public:
+	PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker);
+	virtual ~PlatformNATTraversalGetPublicIPRequest();
+
+	boost::signal<void (boost::optional<HostAddress>)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..514988e
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalRemovePortForwardingRequest.h"
+
+namespace Swift {
+
+PlatformNATTraversalRemovePortForwardingRequest::PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) {
+}
+
+PlatformNATTraversalRemovePortForwardingRequest::~PlatformNATTraversalRemovePortForwardingRequest() {
+
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..03427ad
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRequest {
+public:
+	struct PortMapping {
+		enum Protocol {
+			TCP,
+			UDP,
+		};
+
+		unsigned int publicPort;
+		unsigned int localPort;
+		Protocol protocol;
+		unsigned long leaseInSeconds;
+	};
+
+public:
+	PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker);
+	virtual ~PlatformNATTraversalRemovePortForwardingRequest();
+
+	boost::signal<void (boost::optional<bool> /* failure */)> onResult;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRequest.cpp b/Swiften/Network/PlatformNATTraversalRequest.cpp
new file mode 100644
index 0000000..25e8a32
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRequest.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalRequest.h"
+
+#include <Swiften/Network/PlatformNATTraversalWorker.h>
+
+namespace Swift {
+
+PlatformNATTraversalRequest::PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker) : worker(worker) {
+
+}
+
+PlatformNATTraversalRequest::~PlatformNATTraversalRequest() {
+
+}
+
+void PlatformNATTraversalRequest::run() {
+	worker->addRequestToQueue(shared_from_this());
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalRequest.h b/Swiften/Network/PlatformNATTraversalRequest.h
new file mode 100644
index 0000000..4b760ad
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalRequest.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+
+class PlatformNATTraversalWorker;
+
+class PlatformNATTraversalRequest : public boost::enable_shared_from_this<PlatformNATTraversalRequest> {
+public:
+	typedef boost::shared_ptr<PlatformNATTraversalRequest> ref;
+
+public:
+	PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker);
+	virtual ~PlatformNATTraversalRequest();
+
+	virtual void run();
+	virtual void runBlocking() = 0;
+
+private:
+	PlatformNATTraversalWorker* worker;
+};
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalWorker.cpp b/Swiften/Network/PlatformNATTraversalWorker.cpp
new file mode 100644
index 0000000..a4efedd
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalWorker.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "PlatformNATTraversalWorker.h"
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/UPnPNATTraversalForwardPortRequest.h>
+#include <Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalGetPublicIPRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalForwardPortRequest.h>
+#include <Swiften/Network/NATPMPNATTraversalRemovePortForwardingRequest.h>
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+PlatformNATTraversalWorker::PlatformNATTraversalWorker(EventLoop* eventLoop) : backendType(NotYetDecided), eventLoop(eventLoop), stopRequested(false) {
+	checkAvailableNATTraversalProtocols();
+	thread = new boost::thread(boost::bind(&PlatformNATTraversalWorker::run, this));
+}
+
+PlatformNATTraversalWorker::~PlatformNATTraversalWorker() {
+	stopRequested = true;
+	addRequestToQueue(boost::shared_ptr<PlatformNATTraversalRequest>());
+	thread->join();
+	delete thread;
+}
+
+boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> PlatformNATTraversalWorker::createGetPublicIPRequest() {
+	switch(backendType) {
+		case UPnP:
+			return boost::make_shared<UPnPNATTraversalGetPublicIPRequest>(this);
+		case NATPMP:
+			return boost::make_shared<NATPMPNATTraversalGetPublicIPRequest>(this);
+		case NotYetDecided:
+		case None:
+			break;
+	}
+	return boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest>();
+}
+
+boost::shared_ptr<PlatformNATTraversalForwardPortRequest> PlatformNATTraversalWorker::createForwardPortRequest(unsigned int localPort, unsigned int publicPort) {
+	PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+	mapping.protocol = PlatformNATTraversalForwardPortRequest::PortMapping::TCP;
+	mapping.leaseInSeconds = 60 * 60 * 24;
+	mapping.localPort = localPort;
+	mapping.publicPort = publicPort;
+
+	switch(backendType) {
+		case UPnP:
+			return boost::make_shared<UPnPNATTraversalForwardPortRequest>(mapping, this);
+		case NATPMP:
+			return boost::make_shared<NATPMPNATTraversalForwardPortRequest>(mapping, this);
+		case NotYetDecided:
+		case None:
+			break;
+	}
+	return boost::shared_ptr<PlatformNATTraversalForwardPortRequest>();
+}
+
+boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> PlatformNATTraversalWorker::createRemovePortForwardingRequest(unsigned int localPort, unsigned int publicPort) {
+	PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+	mapping.protocol = PlatformNATTraversalRemovePortForwardingRequest::PortMapping::TCP;
+	mapping.leaseInSeconds = 60 * 60 * 24;
+	mapping.localPort = localPort;
+	mapping.publicPort = publicPort;
+
+	switch(backendType) {
+		case UPnP:
+			return boost::make_shared<UPnPNATTraversalRemovePortForwardingRequest>(mapping, this);
+		case NATPMP:
+			return boost::make_shared<NATPMPNATTraversalRemovePortForwardingRequest>(mapping, this);
+		case NotYetDecided:
+		case None:
+			break;
+	}
+	return boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest>();
+}
+
+void PlatformNATTraversalWorker::run() {
+	while (!stopRequested) {
+		PlatformNATTraversalRequest::ref request;
+		{
+			boost::unique_lock<boost::mutex> lock(queueMutex);
+			while (queue.empty()) {
+				queueNonEmpty.wait(lock);
+			}
+			request = queue.front();
+			queue.pop_front();
+		}
+		// Check whether we don't have a non-null request (used to stop the
+		// worker)
+		if (request) {
+			request->runBlocking();
+		}
+	}
+}
+
+void PlatformNATTraversalWorker::addRequestToQueue(PlatformNATTraversalRequest::ref request) {
+	{
+		boost::lock_guard<boost::mutex> lock(queueMutex);
+		queue.push_back(request);
+	}
+	queueNonEmpty.notify_one();
+}
+
+void PlatformNATTraversalWorker::checkAvailableNATTraversalProtocols() {
+	boost::shared_ptr<UPnPNATTraversalGetPublicIPRequest> upnpRequest = boost::make_shared<UPnPNATTraversalGetPublicIPRequest>(this);
+	upnpRequest->onResult.connect(boost::bind(&PlatformNATTraversalWorker::handleUPnPGetPublicIPResult, this, _1));
+
+	boost::shared_ptr<NATPMPNATTraversalGetPublicIPRequest> natpmpRequest = boost::make_shared<NATPMPNATTraversalGetPublicIPRequest>(this);
+	natpmpRequest->onResult.connect(boost::bind(&PlatformNATTraversalWorker::handleNATPMPGetPublicIPResult, this, _1));
+
+	upnpRequest->run();
+	natpmpRequest->run();
+}
+
+void PlatformNATTraversalWorker::handleUPnPGetPublicIPResult(boost::optional<HostAddress> address) {
+	if (backendType == NotYetDecided || backendType == None) {
+		if (address) {
+			SWIFT_LOG(debug) << "Found UPnP IGD in the local network." << std::endl;
+			backendType = UPnP;
+		}
+	}
+}
+
+void PlatformNATTraversalWorker::handleNATPMPGetPublicIPResult(boost::optional<HostAddress> address) {
+	if (backendType == NotYetDecided || backendType == None) {
+		if (address) {
+			SWIFT_LOG(debug) << "Found NAT-PMP device in the local network." << std::endl;
+			backendType = NATPMP;
+		}
+	}
+}
+
+}
diff --git a/Swiften/Network/PlatformNATTraversalWorker.h b/Swiften/Network/PlatformNATTraversalWorker.h
new file mode 100644
index 0000000..7c249cc
--- /dev/null
+++ b/Swiften/Network/PlatformNATTraversalWorker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <deque>
+#include <boost/optional.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/PlatformNATTraversalRequest.h>
+
+namespace Swift {
+
+class EventLoop;
+class PlatformNATTraversalGetPublicIPRequest;
+class PlatformNATTraversalForwardPortRequest;
+class PlatformNATTraversalRemovePortForwardingRequest;
+
+class PlatformNATTraversalWorker {
+private:
+	enum BackendType {
+		NotYetDecided,
+		UPnP,
+		NATPMP,
+		None,
+	};
+
+public:
+	PlatformNATTraversalWorker(EventLoop* eventLoop);
+	~PlatformNATTraversalWorker();
+
+	boost::shared_ptr<PlatformNATTraversalGetPublicIPRequest> createGetPublicIPRequest();
+	boost::shared_ptr<PlatformNATTraversalForwardPortRequest> createForwardPortRequest(unsigned int localPort, unsigned int publicPort);
+	boost::shared_ptr<PlatformNATTraversalRemovePortForwardingRequest> createRemovePortForwardingRequest(unsigned int localPort, unsigned int publicPort);
+
+	void run();
+	void addRequestToQueue(PlatformNATTraversalRequest::ref);
+
+private:
+	void checkAvailableNATTraversalProtocols();
+	void handleUPnPGetPublicIPResult(boost::optional<HostAddress> address);
+	void handleNATPMPGetPublicIPResult(boost::optional<HostAddress> address);
+
+private:
+	BackendType backendType;
+	EventLoop* eventLoop;
+	bool stopRequested;
+	boost::thread* thread;
+	std::deque<PlatformNATTraversalRequest::ref> queue;
+	boost::mutex queueMutex;
+	boost::condition_variable queueNonEmpty;
+};
+
+}
diff --git a/Swiften/Network/PlatformNetworkEnvironment.h b/Swiften/Network/PlatformNetworkEnvironment.h
new file mode 100644
index 0000000..c6b945e
--- /dev/null
+++ b/Swiften/Network/PlatformNetworkEnvironment.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Base/Platform.h>
+
+#if defined(SWIFTEN_PLATFORM_MACOSX)
+#include <Swiften/Network/UnixNetworkEnvironment.h>
+namespace Swift {
+	typedef UnixNetworkEnvironment PlatformNetworkEnvironment;
+}
+#elif defined(SWIFTEN_PLATFORM_WIN32)
+#include <Swiften/Network/WindowsNetworkEnvironment.h>
+namespace Swift {
+	typedef WindowsNetworkEnvironment PlatformNetworkEnvironment;
+}
+#else
+#include <Swiften/Network/UnixNetworkEnvironment.h>
+namespace Swift {
+	typedef UnixNetworkEnvironment PlatformNetworkEnvironment;
+}
+#endif
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
index 965361b..e9853dd 100644
--- a/Swiften/Network/SConscript
+++ b/Swiften/Network/SConscript
@@ -40,7 +40,18 @@ sourceList = [
 			"Timer.cpp",
 			"BoostTimer.cpp",
 			"ProxyProvider.cpp",
-			"NullProxyProvider.cpp"
+			"NullProxyProvider.cpp",
+			"PlatformNATTraversalWorker.cpp",
+			"PlatformNATTraversalGetPublicIPRequest.cpp",
+			"PlatformNATTraversalForwardPortRequest.cpp",
+			"PlatformNATTraversalRemovePortForwardingRequest.cpp",
+			"PlatformNATTraversalRequest.cpp",
+			"UPnPNATTraversalGetPublicIPRequest.cpp",
+			"UPnPNATTraversalForwardPortRequest.cpp",
+			"UPnPNATTraversalRemovePortForwardingRequest.cpp",
+			"NATPMPNATTraversalGetPublicIPRequest.cpp",
+			"NATPMPNATTraversalForwardPortRequest.cpp",
+			"NATPMPNATTraversalRemovePortForwardingRequest.cpp",
 	]
 
 if myenv.get("HAVE_CARES", False) :
@@ -49,9 +60,12 @@ if myenv.get("HAVE_CARES", False) :
 if myenv["PLATFORM"] == "darwin" :
 	myenv.Append(FRAMEWORKS = ["CoreServices", "SystemConfiguration"])
 	sourceList += [ "MacOSXProxyProvider.cpp" ]
+	sourceList += [ "UnixNetworkEnvironment.cpp" ]
 elif myenv["PLATFORM"] == "win32" :
 	sourceList += [ "WindowsProxyProvider.cpp" ]
+	sourceList += [ "WindowsNetworkEnvironment.cpp" ]
 else :
+	sourceList += [ "UnixNetworkEnvironment.cpp" ]
 	sourceList += [ "UnixProxyProvider.cpp" ]
 	sourceList += [ "EnvironmentProxyProvider.cpp" ]
 	if myenv.get("HAVE_GCONF", 0) :
diff --git a/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp b/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp
new file mode 100644
index 0000000..c95066e
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalForwardPortRequest.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalForwardPortRequest.h"
+
+#include <boost/format.hpp>
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+UPnPNATTraversalForwardPortRequest::UPnPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalForwardPortRequest(worker), mapping(mapping) {
+
+}
+
+UPnPNATTraversalForwardPortRequest::~UPnPNATTraversalForwardPortRequest() {
+
+}
+
+void UPnPNATTraversalForwardPortRequest::runBlocking() {
+	boost::optional<PortMapping> result;
+
+	UPNPDev* deviceList = 0;
+	int error = 0;
+	char lanAddrress[64];
+
+	std::string publicPort = str(boost::format("%d") % mapping.publicPort);
+	std::string localPort = str(boost::format("%d") % mapping.localPort);
+	std::string internalClient = getLocalClient().toString();
+	std::string leaseSeconds = str(boost::format("%d") % mapping.leaseInSeconds);
+	UPNPUrls urls;
+	IGDdatas data;
+
+	do {
+		// find valid IGD
+		deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+		if (!deviceList) {
+			break;
+		}
+
+		if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+			break;
+		}
+
+		/*
+		int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+		if (ret != UPNPCOMMAND_SUCCESS) {
+			break;
+		}*/
+
+		int ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, publicPort.c_str(), localPort.c_str(), internalClient.c_str(), 0, mapping.protocol == PlatformNATTraversalForwardPortRequest::PortMapping::TCP ? "TCP" : "UDP", 0, leaseSeconds.c_str());
+		if (ret == UPNPCOMMAND_SUCCESS) {
+			result = boost::optional<PlatformNATTraversalForwardPortRequest::PortMapping>(mapping);
+		}
+	} while(false);
+
+	freeUPNPDevlist(deviceList); deviceList = 0;
+
+	onResult(result);
+}
+
+HostAddress UPnPNATTraversalForwardPortRequest::getLocalClient() {
+	PlatformNetworkEnvironment env;
+
+	foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+		if (!iface->isLoopback()) {
+			foreach (HostAddress address, iface->getAddresses()) {
+				if (address.getRawAddress().is_v4()) {
+					return address;
+				}
+			}
+		}
+	}
+	return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalForwardPortRequest.h b/Swiften/Network/UPnPNATTraversalForwardPortRequest.h
new file mode 100644
index 0000000..931efee
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalForwardPortRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalForwardPortRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalForwardPortRequest : public PlatformNATTraversalForwardPortRequest {
+public:
+	UPnPNATTraversalForwardPortRequest(PlatformNATTraversalForwardPortRequest::PortMapping, PlatformNATTraversalWorker*);
+	virtual ~UPnPNATTraversalForwardPortRequest();
+
+	virtual void runBlocking();
+
+private:
+	HostAddress getLocalClient();
+
+private:
+	PlatformNATTraversalForwardPortRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp
new file mode 100644
index 0000000..4a7c247
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalGetPublicIPRequest.h"
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+namespace Swift {
+
+UPnPNATTraversalGetPublicIPRequest::UPnPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalGetPublicIPRequest(worker) {
+
+}
+
+UPnPNATTraversalGetPublicIPRequest::~UPnPNATTraversalGetPublicIPRequest() {
+
+}
+
+void UPnPNATTraversalGetPublicIPRequest::runBlocking() {
+	boost::optional<HostAddress> result;
+
+	UPNPDev* deviceList = 0;
+	int error = 0;
+	char lanAddrress[64];
+	char externalIPAddress[40];
+	UPNPUrls urls;
+	IGDdatas data;
+
+	do {
+		// find valid IGD
+		deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+		if (!deviceList) {
+			break;
+		}
+
+		if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+			break;
+		}
+
+		int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+		if (ret != UPNPCOMMAND_SUCCESS) {
+			break;
+		} else {
+			result = HostAddress(std::string(externalIPAddress));
+		}
+	} while(false);
+
+	freeUPNPDevlist(deviceList); deviceList = 0;
+
+	onResult(result);
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h
new file mode 100644
index 0000000..9d50001
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalGetPublicIPRequest.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalGetPublicIPRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalGetPublicIPRequest : public PlatformNATTraversalGetPublicIPRequest {
+public:
+	UPnPNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker*);
+	virtual ~UPnPNATTraversalGetPublicIPRequest();
+
+	virtual void runBlocking();
+};
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp
new file mode 100644
index 0000000..2026880
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UPnPNATTraversalRemovePortForwardingRequest.h"
+
+#include <boost/format.hpp>
+
+#include <miniupnpc.h>
+#include <upnpcommands.h>
+#include <upnperrors.h>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/Log.h>
+#include <Swiften/Network/NetworkInterface.h>
+#include <Swiften/Network/PlatformNetworkEnvironment.h>
+
+namespace Swift {
+
+UPnPNATTraversalRemovePortForwardingRequest::UPnPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping, PlatformNATTraversalWorker* worker) : PlatformNATTraversalRemovePortForwardingRequest(worker), mapping(mapping) {
+
+}
+
+UPnPNATTraversalRemovePortForwardingRequest::~UPnPNATTraversalRemovePortForwardingRequest() {
+
+}
+
+void UPnPNATTraversalRemovePortForwardingRequest::runBlocking() {
+	boost::optional<bool> result;
+
+	UPNPDev* deviceList = 0;
+	int error = 0;
+	char lanAddrress[64];
+
+	std::string publicPort = str(boost::format("%d") % mapping.publicPort);
+	std::string localPort = str(boost::format("%d") % mapping.localPort);
+	std::string internalClient = getLocalClient().toString();
+	std::string leaseSeconds = str(boost::format("%d") % mapping.leaseInSeconds);
+	UPNPUrls urls;
+	IGDdatas data;
+
+	do {
+		// find valid IGD
+		deviceList = upnpDiscover(1500 /* timeout in ms */, 0, 0, 0, 0 /* do IPv6? */, &error);
+		if (!deviceList) {
+			break;
+		}
+
+		if (!UPNP_GetValidIGD(deviceList, &urls, &data, lanAddrress, sizeof(lanAddrress))) {
+			break;
+		}
+
+		/*
+		int ret = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+		if (ret != UPNPCOMMAND_SUCCESS) {
+			break;
+		}*/
+		SWIFT_LOG(debug) << "Start removing port forwarding..." << std::endl;
+		int ret = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, publicPort.c_str(), mapping.protocol == PlatformNATTraversalRemovePortForwardingRequest::PortMapping::TCP ? "TCP" : "UDP", 0);
+
+		if (ret == UPNPCOMMAND_SUCCESS) {
+			SWIFT_LOG(debug) << "Removing port " << publicPort << " successfull." << std::endl;
+			result = true;
+		} else {
+			SWIFT_LOG(debug) << "Removing port " << publicPort << " failed." << std::endl;
+			result = false;
+		}
+	} while(false);
+
+	freeUPNPDevlist(deviceList); deviceList = 0;
+
+	onResult(result);
+}
+
+HostAddress UPnPNATTraversalRemovePortForwardingRequest::getLocalClient() {
+	PlatformNetworkEnvironment env;
+
+	foreach (NetworkInterface::ref iface, env.getNetworkInterfaces()) {
+		if (!iface->isLoopback()) {
+			foreach (HostAddress address, iface->getAddresses()) {
+				if (address.getRawAddress().is_v4()) {
+					return address;
+				}
+			}
+		}
+	}
+	return HostAddress();
+}
+
+}
diff --git a/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h
new file mode 100644
index 0000000..ad1e019
--- /dev/null
+++ b/Swiften/Network/UPnPNATTraversalRemovePortForwardingRequest.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Network/PlatformNATTraversalRemovePortForwardingRequest.h>
+
+namespace Swift {
+
+class UPnPNATTraversalRemovePortForwardingRequest : public PlatformNATTraversalRemovePortForwardingRequest {
+public:
+	UPnPNATTraversalRemovePortForwardingRequest(PlatformNATTraversalRemovePortForwardingRequest::PortMapping, PlatformNATTraversalWorker*);
+	virtual ~UPnPNATTraversalRemovePortForwardingRequest();
+
+	virtual void runBlocking();
+
+private:
+	HostAddress getLocalClient();
+
+private:
+	PlatformNATTraversalRemovePortForwardingRequest::PortMapping mapping;
+};
+
+}
diff --git a/Swiften/Network/UnixNetworkEnvironment.cpp b/Swiften/Network/UnixNetworkEnvironment.cpp
new file mode 100644
index 0000000..649855d
--- /dev/null
+++ b/Swiften/Network/UnixNetworkEnvironment.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "UnixNetworkEnvironment.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+namespace Swift {
+
+std::vector<NetworkInterface::ref> UnixNetworkEnvironment::getNetworkInterfaces() {
+	std::map<std::string, UnixNetworkInterface::ref> interfaces;
+	std::vector<NetworkInterface::ref> result;
+
+	ifaddrs *addrs = 0;
+	int ret = getifaddrs(&addrs);
+	if (ret != 0) {
+		return result;
+	}
+
+	for (ifaddrs *a = addrs; a != 0; a = a->ifa_next) {
+		std::string name(a->ifa_name);
+		std::string ip;
+		if (a->ifa_addr->sa_family == PF_INET) {
+			sockaddr_in* sa = reinterpret_cast<sockaddr_in*>(a->ifa_addr);
+			char str[INET_ADDRSTRLEN];
+			inet_ntop(AF_INET, &(sa->sin_addr), str, INET_ADDRSTRLEN);
+			ip.assign(str);
+		}
+		else if (a->ifa_addr->sa_family == PF_INET6) {
+			sockaddr_in6* sa = reinterpret_cast<sockaddr_in6*>(a->ifa_addr);
+			char str[INET6_ADDRSTRLEN];
+			/*if (IN6_IS_ADDR_LINKLOCAL(sa)) {
+				continue;
+			}*/
+			inet_ntop(AF_INET6, &(sa->sin6_addr), str, INET6_ADDRSTRLEN);
+			ip.assign(str);
+		}
+		if (!ip.empty()) {
+			if (interfaces.find(name) == interfaces.end()) {
+				interfaces[name] = boost::make_shared<UnixNetworkInterface>(name);
+				if (a->ifa_flags & IFF_LOOPBACK) {
+					interfaces[name]->setLoopback(true);
+				}
+			}
+			interfaces[name]->addHostAddress(HostAddress(ip));
+		}
+	}
+
+	freeifaddrs(addrs);
+
+	for(std::map<std::string, UnixNetworkInterface::ref>::iterator i = interfaces.begin(); i != interfaces.end(); ++i) {
+		result.push_back(i->second);
+	}
+	return result;
+}
+
+}
diff --git a/Swiften/Network/UnixNetworkEnvironment.h b/Swiften/Network/UnixNetworkEnvironment.h
new file mode 100644
index 0000000..e4b2f37
--- /dev/null
+++ b/Swiften/Network/UnixNetworkEnvironment.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Network/NetworkEnvironment.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class UnixNetworkEnvironment : public NetworkEnvironment {
+	class UnixNetworkInterface : public NetworkInterface {
+	public:
+		typedef boost::shared_ptr<UnixNetworkInterface> ref;
+
+	public:
+		UnixNetworkInterface(std::string name) : name(name), loopback(false) { }
+
+		std::vector<HostAddress> getAddresses() {
+			return addresses;
+		}
+
+		std::string getName() {
+			return name;
+		}
+
+		bool isLoopback() {
+			return loopback;
+		}
+
+	private:
+		void addHostAddress(HostAddress address) {
+			addresses.push_back(address);
+		}
+
+		void setLoopback(bool loopback) {
+			this->loopback = loopback;
+		}
+
+	private:
+		friend class UnixNetworkEnvironment;
+		std::vector<HostAddress> addresses;
+		std::string name;
+		InterfaceType type;
+		bool loopback;
+	};
+
+public:
+	std::vector<NetworkInterface::ref> getNetworkInterfaces();
+};
+
+}
diff --git a/Swiften/Network/WindowsNetworkEnvironment.cpp b/Swiften/Network/WindowsNetworkEnvironment.cpp
new file mode 100644
index 0000000..5163f43
--- /dev/null
+++ b/Swiften/Network/WindowsNetworkEnvironment.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "WindowsNetworkEnvironment.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+#include <Swiften/Network/HostAddress.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+#include <winsock2.h>
+#include <iphlpapi.h>
+
+namespace Swift {
+
+std::string winSocketAddressToStdString(const SOCKET_ADDRESS& socketAddress) {
+	char text[46];
+	ULONG bufferSize = sizeof(text);
+	std::string result;
+
+	int ret = WSAAddressToString(socketAddress.lpSockaddr, socketAddress.iSockaddrLength, NULL, text, &bufferSize);
+	if (ret == 0) {
+		result.assign(text, sizeof(text));
+	}
+	return result;
+}
+
+std::vector<NetworkInterface::ref> WindowsNetworkEnvironment::getNetworkInterfaces() {
+	std::map<std::string, WindowsNetworkInterface::ref> interfaces;
+	std::vector<NetworkInterface::ref> result;
+
+	IP_ADAPTER_ADDRESSES preBuffer[5];
+	PIP_ADAPTER_ADDRESSES adapterStart = preBuffer;
+
+	ULONG bufferSize = sizeof(preBuffer);
+
+	ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES |  GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
+
+	ULONG ret = GetAdaptersAddresses(	AF_UNSPEC, flags,	NULL, adapterStart, &bufferSize);
+	if (ret == ERROR_BUFFER_OVERFLOW) {
+		adapterStart = new IP_ADAPTER_ADDRESSES[bufferSize / sizeof(IP_ADAPTER_ADDRESSES)];
+		if (!adapterStart) {
+			return result;
+		}
+		ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapterStart, &bufferSize);
+	}
+	if (ret != ERROR_SUCCESS) {
+		if (adapterStart != preBuffer) {
+			delete adapterStart;
+		}
+		return result;
+	}
+
+	for (PIP_ADAPTER_ADDRESSES adapter = adapterStart; adapter; adapter = adapter->Next) {
+		std::string name(adapter->AdapterName);
+
+		if (adapter->OperStatus != IfOperStatusUp) {
+			continue;
+		}
+
+		// iterate over addresses
+		for (PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;  address; address = address->Next) {
+			std::string ip;
+
+			if (address->Address.lpSockaddr->sa_family == PF_INET ||
+					address->Address.lpSockaddr->sa_family == PF_INET6) {
+				ip = winSocketAddressToStdString(address->Address);
+				if (!ip.empty()) {
+					if (interfaces.find(name) == interfaces.end()) {
+						interfaces[name] = boost::make_shared<WindowsNetworkInterface>();
+						interfaces[name]->setName(name);
+					}
+					interfaces[name]->addHostAddress(HostAddress(ip));
+				}
+			}
+		}
+	}
+
+	if (adapterStart != preBuffer) {
+		//delete adapterStart;
+	}
+
+	for(std::map<std::string, WindowsNetworkInterface::ref>::iterator i = interfaces.begin(); i != interfaces.end(); ++i) {
+		result.push_back(i->second);
+	}
+	return result;
+}
+
+}
diff --git a/Swiften/Network/WindowsNetworkEnvironment.h b/Swiften/Network/WindowsNetworkEnvironment.h
new file mode 100644
index 0000000..2b79504
--- /dev/null
+++ b/Swiften/Network/WindowsNetworkEnvironment.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <Swiften/Base/boost_bsignals.h>
+
+#include <Swiften/Network/NetworkEnvironment.h>
+#include <Swiften/Network/NetworkInterface.h>
+
+namespace Swift {
+
+class WindowsNetworkEnvironment : public NetworkEnvironment {
+	class WindowsNetworkInterface : public NetworkInterface {
+	public:
+		typedef boost::shared_ptr<WindowsNetworkInterface> ref;
+
+	public:
+		virtual ~WindowsNetworkInterface() { }
+		virtual std::vector<HostAddress> getAddresses() {
+			return addresses;
+		}
+
+		virtual std::string getName() {
+			return name;
+		}
+
+		virtual bool isLoopback() {
+			return false;
+		}
+
+	public:
+		void addHostAddress(HostAddress address) {
+			addresses.push_back(address);
+		}
+
+		void setName(const std::string& name) {
+			this->name = name;
+		}
+
+	private:
+		std::vector<HostAddress> addresses;
+		InterfaceType type;
+		std::string name;
+	};
+
+public:
+	std::vector<NetworkInterface::ref> getNetworkInterfaces();
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 1b59f6f..9eafcba 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -48,6 +48,19 @@
 #include <Swiften/Parser/PayloadParsers/NicknameParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/ReplaceParser.h>
 #include <Swiften/Parser/PayloadParsers/LastParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleReasonParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h>
+#include <Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h>
+
+#include "JingleIBBTransportMethodPayloadParser.h"
+#include "JingleFileTransferDescriptionParser.h"
 
 using namespace boost;
 
@@ -91,6 +104,16 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new MUCUserPayloadParserFactory()));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<MUCAdminPayloadParser>("query", "http://jabber.org/protocol/muc#admin")));
 	factories_.push_back(shared_ptr<PayloadParserFactory>(new NicknameParserFactory()));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleParserFactory(this)));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleReasonParser>("reason", "urn:xmpp:jingle:1")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleContentPayloadParserFactory(this)));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleIBBTransportMethodPayloadParser>("transport", "urn:xmpp:jingle:transports:ibb:1")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleS5BTransportMethodPayloadParser>("transport", "urn:xmpp:jingle:transports:s5b:1")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new JingleFileTransferDescriptionParserFactory(this)));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StreamInitiationFileInfoParser>("file", "http://jabber.org/protocol/si/profile/file-transfer")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleFileTransferReceivedParser>("received", "urn:xmpp:jingle:apps:file-transfer:3")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<JingleFileTransferHashParser>("checksum")));
+	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<S5BProxyRequestParser>("query", "http://jabber.org/protocol/bytestreams")));
 	foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
 		addFactory(factory.get());
 	}
diff --git a/Swiften/Parser/PayloadParsers/IBBParser.cpp b/Swiften/Parser/PayloadParsers/IBBParser.cpp
index 2705c75..20a1ce9 100644
--- a/Swiften/Parser/PayloadParsers/IBBParser.cpp
+++ b/Swiften/Parser/PayloadParsers/IBBParser.cpp
@@ -60,7 +60,7 @@ void IBBParser::handleEndElement(const std::string& element, const std::string&)
 			std::vector<char> data;
 			for (size_t i = 0; i < currentText.size(); ++i) {
 				char c = currentText[i];
-				if (c >= 48 && c <= 122) {
+				if ((c >= 48 && c <= 122) || c == 47 || c == 43) {
 					data.push_back(c);
 				}
 			}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp
new file mode 100644
index 0000000..1431b9e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleContentPayloadParser.h"
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Elements/JinglePayload.h>
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+	JingleContentPayloadParser::JingleContentPayloadParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+		
+	}
+	
+	void JingleContentPayloadParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+		if (level == 0) {
+			std::string creator = attributes.getAttributeValue("creator").get_value_or("");
+			if (creator == "initiator") {
+				getPayloadInternal()->setCreator(JingleContentPayload::InitiatorCreator);
+			} else if (creator == "responder") {
+				getPayloadInternal()->setCreator(JingleContentPayload::ResponderCreator);
+			} else {
+				getPayloadInternal()->setCreator(JingleContentPayload::UnknownCreator);
+			}
+			
+			getPayloadInternal()->setName(attributes.getAttributeValue("name").get_value_or(""));
+		}
+		
+		if (level == 1) {
+			PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+			if (payloadParserFactory) {
+				currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+			}
+		}
+		
+		if (level >= 1 && currentPayloadParser) {
+			currentPayloadParser->handleStartElement(element, ns, attributes);
+		}
+		
+		++level;
+	}
+	
+	void JingleContentPayloadParser::handleEndElement(const std::string& element, const std::string& ns) {
+		--level;
+		
+		if (currentPayloadParser) { 
+			if (level >= 1) {
+				currentPayloadParser->handleEndElement(element, ns);
+			}
+			
+			if (level == 1) {
+				boost::shared_ptr<JingleTransportPayload> transport = boost::dynamic_pointer_cast<JingleTransportPayload>(currentPayloadParser->getPayload());
+				if (transport) {
+					getPayloadInternal()->addTransport(transport);
+				}
+				
+				boost::shared_ptr<JingleDescription> description = boost::dynamic_pointer_cast<JingleDescription>(currentPayloadParser->getPayload());
+				if (description) {
+					getPayloadInternal()->addDescription(description);
+				}
+			}
+		}
+	}
+	
+	void JingleContentPayloadParser::handleCharacterData(const std::string& data) {
+		if (level > 1 && currentPayloadParser) {
+			currentPayloadParser->handleCharacterData(data);
+		}
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h
new file mode 100644
index 0000000..a871cc4
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class PayloadParserFactoryCollection;
+
+class JingleContentPayloadParser : public GenericPayloadParser<JingleContentPayload> {
+	public:
+		JingleContentPayloadParser(PayloadParserFactoryCollection* factories);
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);	
+
+	private:
+		PayloadParserFactoryCollection* factories;
+		int level;
+		boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h b/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h
new file mode 100644
index 0000000..6d66e74
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleContentPayloadParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleContentPayloadParser.h>
+
+namespace Swift {
+	
+	class PayloadParserFactoryCollection;
+
+	class JingleContentPayloadParserFactory : public PayloadParserFactory {
+		public:
+			JingleContentPayloadParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+			}
+
+			virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+				return element == "content" && ns == "urn:xmpp:jingle:1";
+			}
+
+			virtual PayloadParser* createPayloadParser() {
+				return new JingleContentPayloadParser(factories);
+			}
+
+		private:
+			PayloadParserFactoryCollection* factories;
+			
+	};
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp
new file mode 100644
index 0000000..b394115
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferDescriptionParser.h"
+
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+JingleFileTransferDescriptionParser::JingleFileTransferDescriptionParser(PayloadParserFactoryCollection* factories) :   factories(factories), level(0),
+															currentElement(UnknownElement) {
+	
+}
+
+void JingleFileTransferDescriptionParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+	if (level == 0) {
+		
+	}
+	
+	if (level == 1) {
+		if (element == "offer") {
+			currentElement = OfferElement;
+		} else if (element == "request") {
+			currentElement = RequestElement;
+		} else {
+			currentElement = UnknownElement;
+		}
+	}
+
+	if (level == 2) {
+		PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+		if (payloadParserFactory) {
+			currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+		}
+	}
+
+	if (level >= 2 && currentPayloadParser) {
+		currentPayloadParser->handleStartElement(element, ns, attributes);
+	}
+
+	++level;
+}
+
+void JingleFileTransferDescriptionParser::handleEndElement(const std::string& element, const std::string& ns) {
+	--level;
+	if (currentPayloadParser) { 
+		if (level >= 2) {
+			currentPayloadParser->handleEndElement(element, ns);
+		}
+
+		if (level == 2) {
+			boost::shared_ptr<StreamInitiationFileInfo> info = boost::dynamic_pointer_cast<StreamInitiationFileInfo>(currentPayloadParser->getPayload());
+			if (info) {
+				if (currentElement == OfferElement) {
+					getPayloadInternal()->addOffer(*info);
+				} else if (currentElement == RequestElement) {
+					getPayloadInternal()->addRequest(*info);
+				}
+			}
+		}
+	}
+}
+
+void JingleFileTransferDescriptionParser::handleCharacterData(const std::string& data) {
+	if (level >= 2 && currentPayloadParser) {
+		currentPayloadParser->handleCharacterData(data);
+	}
+}
+		
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h
new file mode 100644
index 0000000..93560c6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParser.h>
+
+namespace Swift {
+
+class PayloadParserFactoryCollection;
+
+class JingleFileTransferDescriptionParser : public GenericPayloadParser<JingleFileTransferDescription> {
+	public:
+		JingleFileTransferDescriptionParser(PayloadParserFactoryCollection* factories);
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);	
+
+	private:
+		enum CurrentParseElement {
+			UnknownElement,
+			RequestElement,
+			OfferElement,
+		};
+		
+		PayloadParserFactoryCollection* factories;
+		int level;
+		CurrentParseElement currentElement;
+		boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h
new file mode 100644
index 0000000..b997c1d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleFileTransferDescriptionParser.h>
+
+namespace Swift {
+	
+	class PayloadParserFactoryCollection;
+
+	class JingleFileTransferDescriptionParserFactory : public PayloadParserFactory {
+		public:
+			JingleFileTransferDescriptionParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+			}
+
+			virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+				return element == "description" && ns == "urn:xmpp:jingle:apps:file-transfer:3";
+			}
+
+			virtual PayloadParser* createPayloadParser() {
+				return new JingleFileTransferDescriptionParser(factories);
+			}
+
+		private:
+			PayloadParserFactoryCollection* factories;
+			
+	};
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp
new file mode 100644
index 0000000..87f8317
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferHashParser.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+
+namespace Swift {
+
+JingleFileTransferHashParser::JingleFileTransferHashParser() {
+}
+	
+void JingleFileTransferHashParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+	if (element == "hash") {
+		algo = attributes.getAttribute("algo");
+	}
+}
+
+void JingleFileTransferHashParser::handleEndElement(const std::string& element, const std::string& ) {
+	if (element == "hash" && !algo.empty() && !hash.empty()) {
+		getPayloadInternal()->setHash(algo, hash);
+		algo.clear();
+		hash.clear();
+	}
+}
+
+void JingleFileTransferHashParser::handleCharacterData(const std::string& data) {
+	if (!algo.empty()) {
+		std::string new_data(data);
+		boost::trim(new_data);
+		hash += new_data;
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h
new file mode 100644
index 0000000..35e4a05
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferHashParser.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleFileTransferHashParser : public GenericPayloadParser<JingleFileTransferHash> {
+public:
+	JingleFileTransferHashParser();
+	
+	virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+	virtual void handleEndElement(const std::string& element, const std::string&);
+	virtual void handleCharacterData(const std::string& data);
+	
+private:
+	std::string algo;
+	std::string hash;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp
new file mode 100644
index 0000000..20ad73e
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleFileTransferReceivedParser.h"
+#include "StreamInitiationFileInfoParser.h"
+
+#include <boost/shared_ptr.hpp>
+#include <Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h>
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+
+namespace Swift {
+
+JingleFileTransferReceivedParser::JingleFileTransferReceivedParser() : level(0) {
+}
+	
+void JingleFileTransferReceivedParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {
+	if (level == 1 && element == "file") {
+		PayloadParserFactory* payloadParserFactory = new GenericPayloadParserFactory<StreamInitiationFileInfoParser>("file", "http://jabber.org/protocol/si/profile/file-transfer");
+		if (payloadParserFactory) {
+			currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+		}
+	}
+	
+	if (currentPayloadParser && level >= 1) {
+		currentPayloadParser->handleStartElement(element, ns, attributes);
+	}
+	
+	++level;
+}
+
+void JingleFileTransferReceivedParser::handleEndElement(const std::string& element, const std::string& ) {
+	--level;
+	if (element == "file") {
+		boost::shared_ptr<StreamInitiationFileInfo> fileInfo = boost::dynamic_pointer_cast<StreamInitiationFileInfo>(currentPayloadParser->getPayload());
+		if (fileInfo) {
+			getPayloadInternal()->setFileInfo(*fileInfo);
+		}
+	}
+}
+
+void JingleFileTransferReceivedParser::handleCharacterData(const std::string& ) {
+	
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h
new file mode 100644
index 0000000..824b06d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleFileTransferReceivedParser.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleFileTransferReceivedParser : public GenericPayloadParser<JingleFileTransferReceived> {
+public:
+	JingleFileTransferReceivedParser();
+	
+	virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+	virtual void handleEndElement(const std::string& element, const std::string&);
+	virtual void handleCharacterData(const std::string& data);
+	
+private:
+	boost::shared_ptr<PayloadParser> currentPayloadParser;
+	int level;
+};
+
+}
\ No newline at end of file
diff --git a/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp
new file mode 100644
index 0000000..a3dfd12
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+#include "JingleIBBTransportMethodPayloadParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+	JingleIBBTransportMethodPayloadParser::JingleIBBTransportMethodPayloadParser() : level(0) {
+		
+	}
+	
+	void JingleIBBTransportMethodPayloadParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) {
+		try {
+			getPayloadInternal()->setBlockSize(boost::lexical_cast<int>(attributes.getAttributeValue("block-size").get_value_or("0")));
+		} catch (boost::bad_lexical_cast &) {
+			getPayloadInternal()->setBlockSize(0);
+		}
+		getPayloadInternal()->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+		++level;
+	}
+	
+	void JingleIBBTransportMethodPayloadParser::handleEndElement(const std::string&, const std::string&) {
+		--level;
+		
+
+	}
+	
+	void JingleIBBTransportMethodPayloadParser::handleCharacterData(const std::string&) {
+
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h
new file mode 100644
index 0000000..311cc5b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleIBBTransportMethodPayloadParser.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleIBBTransportMethodPayloadParser : public GenericPayloadParser<JingleIBBTransportPayload> {
+	public:
+		JingleIBBTransportMethodPayloadParser();
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);	
+
+	private:
+		int level;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleParser.cpp b/Swiften/Parser/PayloadParsers/JingleParser.cpp
new file mode 100644
index 0000000..dd34458
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParser.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+#include <Swiften/Parser/PayloadParserFactory.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Base/Log.h>
+
+#include <boost/intrusive_ptr.hpp>
+
+namespace Swift {
+
+	JingleParser::JingleParser(PayloadParserFactoryCollection* factories) : factories(factories), level(0) {
+		
+	}
+
+	void JingleParser::handleStartElement(const std::string& element, const std::string &ns, const AttributeMap& attributes) {
+		if (level == 0) {
+			// <jingle > tag
+			JinglePayload::ref payload = getPayloadInternal();
+			payload->setAction(stringToAction(attributes.getAttributeValue("action").get_value_or("")));
+			payload->setInitiator(JID(attributes.getAttributeValue("initiator").get_value_or("")));
+			payload->setResponder(JID(attributes.getAttributeValue("responder").get_value_or("")));
+			payload->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+		}
+		
+		if (level == 1) {
+			PayloadParserFactory* payloadParserFactory = factories->getPayloadParserFactory(element, ns, attributes);
+			if (payloadParserFactory) {
+				currentPayloadParser.reset(payloadParserFactory->createPayloadParser());
+			}
+		}
+		
+		if (level >= 1 && currentPayloadParser) {
+			currentPayloadParser->handleStartElement(element, ns, attributes);
+		}
+		
+		++level;
+	}
+	
+	void JingleParser::handleEndElement(const std::string& element, const std::string &ns) {
+		--level;
+		if (currentPayloadParser) {
+			if (level >= 1) {
+				currentPayloadParser->handleEndElement(element, ns);
+			}
+			
+			if (level == 1) {
+				boost::shared_ptr<JinglePayload::Reason> reason = boost::dynamic_pointer_cast<JinglePayload::Reason>(currentPayloadParser->getPayload());
+				if (reason) {
+					getPayloadInternal()->setReason(*reason);
+				}
+				
+				boost::shared_ptr<JingleContentPayload> payload = boost::dynamic_pointer_cast<JingleContentPayload>(currentPayloadParser->getPayload());
+				if (payload) {
+					getPayloadInternal()->addContent(payload);
+				}
+				
+				boost::shared_ptr<JingleFileTransferReceived> received = boost::dynamic_pointer_cast<JingleFileTransferReceived>(currentPayloadParser->getPayload());
+				if (received) {
+					getPayloadInternal()->addPayload(received);
+				}
+
+				boost::shared_ptr<JingleFileTransferHash> hash = boost::dynamic_pointer_cast<JingleFileTransferHash>(currentPayloadParser->getPayload());
+				if (hash) {
+					getPayloadInternal()->addPayload(hash);
+				}
+			}
+		}
+	}
+	
+	void JingleParser::handleCharacterData(const std::string& data) {
+		if (level > 1 && currentPayloadParser) {
+			currentPayloadParser->handleCharacterData(data);
+		}
+	}
+	
+	JinglePayload::Action JingleParser::stringToAction(const std::string &str) const {
+		if (str == "content-accept") {
+			return JinglePayload::ContentAccept;
+		} else if (str == "content-add") {
+			return JinglePayload::ContentAdd;
+		} else if (str == "content-modify") {
+			return JinglePayload::ContentModify;
+		} else if (str == "content-reject") {
+			return JinglePayload::ContentReject;
+		} else if (str == "content-remove") {
+			return JinglePayload::ContentRemove;
+		} else if (str == "description-info") {
+			return JinglePayload::DescriptionInfo;
+		} else if (str == "security-info") {
+			return JinglePayload::SecurityInfo;
+		} else if (str == "session-accept") {
+			return JinglePayload::SessionAccept;
+		} else if (str == "session-info") {
+			return JinglePayload::SessionInfo;
+		} else if (str == "session-initiate") {
+			return JinglePayload::SessionInitiate;
+		} else if (str == "session-terminate") {
+			return JinglePayload::SessionTerminate;
+		} else if (str == "transport-accept") {
+			return JinglePayload::TransportAccept;
+		} else if (str == "transport-info") {
+			return JinglePayload::TransportInfo;
+		} else if (str == "transport-reject") {
+			return JinglePayload::TransportReject;
+		} else if (str == "transport-replace") {
+			return JinglePayload::TransportReplace;
+		} else {
+			return JinglePayload::UnknownAction;
+		}
+
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleParser.h b/Swiften/Parser/PayloadParsers/JingleParser.h
new file mode 100644
index 0000000..ecaca3c
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParser.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+#include <Swiften/Parser/PayloadParserFactoryCollection.h>
+
+namespace Swift {
+
+class JingleParser : public GenericPayloadParser<JinglePayload> {
+		public:
+			JingleParser(PayloadParserFactoryCollection* factories);
+
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+			virtual void handleEndElement(const std::string& element, const std::string&);
+			virtual void handleCharacterData(const std::string& data);
+		
+		private:
+			JinglePayload::Action stringToAction(const std::string &str) const;
+			
+		private:
+			PayloadParserFactoryCollection* factories;
+			int level;
+			boost::shared_ptr<PayloadParser> currentPayloadParser;
+};
+
+};
\ No newline at end of file
diff --git a/Swiften/Parser/PayloadParsers/JingleParserFactory.h b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
new file mode 100644
index 0000000..fa25aeb
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Parser/GenericPayloadParserFactory.h>
+#include <Swiften/Parser/PayloadParsers/JingleParser.h>
+
+namespace Swift {
+	
+	class PayloadParserFactoryCollection;
+
+	class JingleParserFactory : public PayloadParserFactory {
+		public:
+			JingleParserFactory(PayloadParserFactoryCollection* factories) : factories(factories) {
+			}
+
+			virtual bool canParse(const std::string& element, const std::string& ns, const AttributeMap&) const {
+				return element == "jingle" && ns == "urn:xmpp:jingle:1";
+			}
+
+			virtual PayloadParser* createPayloadParser() {
+				return new JingleParser(factories);
+			}
+
+		private:
+			PayloadParserFactoryCollection* factories;
+			
+	};
+}
+
+
diff --git a/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp b/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp
new file mode 100644
index 0000000..3df82ae
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleReasonParser.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "JingleReasonParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+	JingleReasonParser::JingleReasonParser() : level(0), parseText(false) {
+		
+	}
+	
+	void JingleReasonParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap&) {
+		if (level == 1) {
+			if (element == "text") {
+				parseText = true;
+			} else {
+				// reason type
+				getPayloadInternal()->type = stringToReasonType(element);
+			}
+		}
+		++level;
+	}
+	
+	void JingleReasonParser::handleEndElement(const std::string& element, const std::string&) {
+		--level;		
+		if (element == "text") {
+			parseText = false;
+			getPayloadInternal()->text = text;
+		}
+	}
+	
+	void JingleReasonParser::handleCharacterData(const std::string& data) {
+		if (parseText) {
+			text += data;
+		}
+	}
+	
+	JinglePayload::Reason::Type JingleReasonParser::stringToReasonType(const std::string& type) const {
+		if (type == "alternative-session") {
+			return JinglePayload::Reason::AlternativeSession;
+		} else if (type == "busy") {
+			return JinglePayload::Reason::Busy;
+		} else if (type == "cancel") {
+			return JinglePayload::Reason::Cancel;
+		} else if (type == "connectivity-error") {
+			return JinglePayload::Reason::ConnectivityError;
+		} else if (type == "decline") {
+			return JinglePayload::Reason::Decline;
+		} else if (type == "expired") {
+			return JinglePayload::Reason::Expired;
+		} else if (type == "failed-application") {
+			return JinglePayload::Reason::FailedApplication;
+		} else if (type == "failed-transport") {
+			return JinglePayload::Reason::FailedTransport;
+		} else if (type == "general-error") {
+			return JinglePayload::Reason::GeneralError;
+		} else if (type == "gone") {
+			return JinglePayload::Reason::Gone;
+		} else if (type == "incompatible-parameters") {
+			return JinglePayload::Reason::IncompatibleParameters;
+		} else if (type == "media-error") {
+			return JinglePayload::Reason::MediaError;
+		} else if (type == "security-error") {
+			return JinglePayload::Reason::SecurityError;
+		} else if (type == "success") {
+			return JinglePayload::Reason::Success;
+		} else if (type == "timeout") {
+			return JinglePayload::Reason::Timeout;
+		} else if (type == "unsupported-applications") {
+			return JinglePayload::Reason::UnsupportedApplications;
+		} else if (type == "unsupported-transports") {
+			return JinglePayload::Reason::UnsupportedTransports;
+		} else {
+			return JinglePayload::Reason::UnknownType;
+		}
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleReasonParser.h b/Swiften/Parser/PayloadParsers/JingleReasonParser.h
new file mode 100644
index 0000000..08af31a
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleReasonParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleReasonParser : public GenericPayloadParser<JinglePayload::Reason> {
+	public:
+		JingleReasonParser();
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);	
+	private:
+		JinglePayload::Reason::Type stringToReasonType(const std::string& type) const;
+
+	private:
+		int level;
+		bool parseText;
+		std::string text;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp
new file mode 100644
index 0000000..14a80e6
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+#include "JingleS5BTransportMethodPayloadParser.h"
+
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+	JingleS5BTransportMethodPayloadParser::JingleS5BTransportMethodPayloadParser() : level(0) {
+		
+	}
+	
+	void JingleS5BTransportMethodPayloadParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+		if (level == 0) {
+			getPayloadInternal()->setSessionID(attributes.getAttributeValue("sid").get_value_or(""));
+			std::string mode = attributes.getAttributeValue("mode").get_value_or("tcp");
+			if (mode == "tcp") {
+				getPayloadInternal()->setMode(JingleS5BTransportPayload::TCPMode);
+			} else if(mode == "udp") {
+				getPayloadInternal()->setMode(JingleS5BTransportPayload::UDPMode);
+			} else {
+				std::cerr << "Unknown S5B mode; falling back to defaul!" << std::endl;
+				getPayloadInternal()->setMode(JingleS5BTransportPayload::TCPMode);
+			}
+		} else if (level == 1) {
+			if (element == "candidate") {
+				JingleS5BTransportPayload::Candidate candidate;
+				candidate.cid = attributes.getAttributeValue("cid").get_value_or("");
+
+				int port = -1;
+				try {
+					port = boost::lexical_cast<int>(attributes.getAttributeValue("port").get_value_or("-1"));
+				} catch(boost::bad_lexical_cast &) { }
+				candidate.hostPort = HostAddressPort(HostAddress(attributes.getAttributeValue("host").get_value_or("")), port);
+				candidate.jid = JID(attributes.getAttributeValue("jid").get_value_or(""));
+				int priority = -1;
+				try {
+					priority = boost::lexical_cast<int>(attributes.getAttributeValue("priority").get_value_or("-1"));
+				} catch(boost::bad_lexical_cast &) { }
+				candidate.priority = priority;
+				candidate.type = stringToType(attributes.getAttributeValue("type").get_value_or("direct"));
+
+				getPayloadInternal()->addCandidate(candidate);
+			} else if (element == "candidate-used") {
+				getPayloadInternal()->setCandidateUsed(attributes.getAttributeValue("cid").get_value_or(""));
+			} else if (element == "candidate-error") {
+				getPayloadInternal()->setCandidateError(true);
+			} else if (element == "activated") {
+				getPayloadInternal()->setActivated(attributes.getAttributeValue("cid").get_value_or(""));
+			} else if (element == "proxy-error") {
+				getPayloadInternal()->setProxyError(true);
+			}
+		}
+
+		++level;
+	}
+	
+	void JingleS5BTransportMethodPayloadParser::handleEndElement(const std::string&, const std::string&) {
+		--level;
+		
+
+	}
+	
+	void JingleS5BTransportMethodPayloadParser::handleCharacterData(const std::string&) {
+
+	}
+
+	JingleS5BTransportPayload::Candidate::Type JingleS5BTransportMethodPayloadParser::stringToType(const std::string &str) const {
+		if (str == "direct") {
+			return JingleS5BTransportPayload::Candidate::DirectType;
+		} else if (str == "assisted") {
+			return JingleS5BTransportPayload::Candidate::AssistedType;
+		} else if (str == "tunnel") {
+			return JingleS5BTransportPayload::Candidate::TunnelType;
+		} else if (str == "proxy") {
+			return JingleS5BTransportPayload::Candidate::ProxyType;
+		} else {
+			std::cerr << "Unknown candidate type; falling back to default!" << std::endl;
+			return JingleS5BTransportPayload::Candidate::DirectType;
+		}
+	}
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h
new file mode 100644
index 0000000..1987d3f
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/JingleS5BTransportMethodPayloadParser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class JingleS5BTransportMethodPayloadParser : public GenericPayloadParser<JingleS5BTransportPayload> {
+	public:
+		JingleS5BTransportMethodPayloadParser();
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);	
+
+	private:
+		JingleS5BTransportPayload::Candidate::Type stringToType(const std::string &str) const;
+
+	private:
+		int level;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp
new file mode 100644
index 0000000..6e33f16
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "S5BProxyRequestParser.h"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/optional.hpp>
+
+namespace Swift {
+
+S5BProxyRequestParser::S5BProxyRequestParser() : parseActivate(false) {
+}
+
+S5BProxyRequestParser::~S5BProxyRequestParser() {
+}
+
+void S5BProxyRequestParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+	if (element == "streamhost") {
+		if (attributes.getAttributeValue("host") && attributes.getAttributeValue("jid") && attributes.getAttributeValue("port")) {
+			HostAddress address = attributes.getAttributeValue("host").get_value_or("");
+			int port = -1;
+			JID jid = attributes.getAttributeValue("jid").get_value_or("");
+
+			try {
+				port = boost::lexical_cast<int>(attributes.getAttributeValue("port").get());
+			} catch (boost::bad_lexical_cast &) {
+				port = -1;
+			}
+			if (address.isValid() && port != -1 && jid.isValid()) {
+				S5BProxyRequest::StreamHost streamHost;
+				streamHost.addressPort = HostAddressPort(address, port);
+				streamHost.jid = jid;
+				getPayloadInternal()->setStreamHost(streamHost);
+			}
+		}
+	} else if (element == "activate") {
+		parseActivate = true;
+	} else if (element == "query") {
+		if (attributes.getAttributeValue("sid")) {
+			getPayloadInternal()->setSID(attributes.getAttributeValue("sid").get());
+		}
+	}
+}
+
+void S5BProxyRequestParser::handleEndElement(const std::string& element, const std::string&) {
+	if (element == "activate") {
+		JID activate = JID(activateJID);
+		if (activate.isValid()) {
+			getPayloadInternal()->setActivate(activate);
+		}
+		parseActivate = false;
+	}
+}
+
+void S5BProxyRequestParser::handleCharacterData(const std::string& data) {
+	if (parseActivate) {
+		activateJID = activateJID + data;
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h
new file mode 100644
index 0000000..0bf1a26
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/S5BProxyRequestParser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class S5BProxyRequestParser : public GenericPayloadParser<S5BProxyRequest> {
+public:
+	S5BProxyRequestParser();
+	virtual ~S5BProxyRequestParser();
+
+	virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+	virtual void handleEndElement(const std::string& element, const std::string&);
+	virtual void handleCharacterData(const std::string& data);
+
+private:
+	bool parseActivate;
+	std::string activateJID;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp
new file mode 100644
index 0000000..0a13844
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include "StreamInitiationFileInfoParser.h"
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+StreamInitiationFileInfoParser::StreamInitiationFileInfoParser() : level(0), parseDescription(false) {
+	
+}
+
+void StreamInitiationFileInfoParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+	if (level == 0) {
+		getPayloadInternal()->setName(attributes.getAttributeValue("name").get_value_or(""));
+		getPayloadInternal()->setHash(attributes.getAttributeValue("hash").get_value_or(""));
+		getPayloadInternal()->setAlgo(attributes.getAttributeValue("algo").get_value_or("md5"));
+		try {
+			getPayloadInternal()->setSize(boost::lexical_cast<boost::uintmax_t>(attributes.getAttributeValue("size").get_value_or("0")));
+		} catch (boost::bad_lexical_cast &) {
+			getPayloadInternal()->setSize(0);
+		}
+		getPayloadInternal()->setDate(stringToDateTime(attributes.getAttributeValue("date").get_value_or("")));
+	} else if (level == 1) {
+		if (element == "desc") {
+			parseDescription = true;
+		} else {
+			parseDescription = false;
+			if (element == "range") {
+				int offset = 0;
+				try {
+					offset = boost::lexical_cast<boost::uintmax_t>(attributes.getAttributeValue("offset").get_value_or("0"));
+				} catch (boost::bad_lexical_cast &) {
+					offset = 0;
+				}
+				if (offset == 0) {
+					getPayloadInternal()->setSupportsRangeRequests(true);
+				} else {
+					getPayloadInternal()->setRangeOffset(offset);
+				}
+			}
+		}
+	}
+	++level;
+}
+
+void StreamInitiationFileInfoParser::handleEndElement(const std::string& element, const std::string&) {
+	--level;
+	if (parseDescription && element == "desc") {
+		parseDescription = false;
+		getPayloadInternal()->setDescription(desc);
+	}
+}
+
+void StreamInitiationFileInfoParser::handleCharacterData(const std::string& data) {
+	if (parseDescription) {
+		desc += data;
+	}
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h
new file mode 100644
index 0000000..6d3591d
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationFileInfoParser.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+
+class StreamInitiationFileInfoParser : public GenericPayloadParser<StreamInitiationFileInfo> {
+	public:
+		StreamInitiationFileInfoParser();
+
+		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+		virtual void handleEndElement(const std::string& element, const std::string&);
+		virtual void handleCharacterData(const std::string& data);
+		
+	private:
+		int level;
+		bool parseDescription;
+		std::string desc;
+};
+
+}
diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
index 9ea8089..fd3d019 100644
--- a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
+++ b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp
@@ -38,9 +38,9 @@ void StreamInitiationParser::handleStartElement(const std::string& element, cons
 		if (element == "file") {
 			inFile = true;
 			currentFile = StreamInitiationFileInfo();
-			currentFile.name = attributes.getAttribute("name");
+			currentFile.setName(attributes.getAttribute("name"));
 			try {
-				currentFile.size = boost::lexical_cast<int>(attributes.getAttribute("size"));
+				currentFile.setSize(boost::lexical_cast<int>(attributes.getAttribute("size")));
 			}
 			catch (boost::bad_lexical_cast&) {
 			}
@@ -83,7 +83,7 @@ void StreamInitiationParser::handleEndElement(const std::string& element, const
 	}
 	else if (level == FileOrFeatureLevel) {
 		if (inFile && element == "desc") {
-			currentFile.description = currentText;
+			currentFile.setDescription(currentText);
 		}
 		else if (formParser) {
 			Form::ref form = formParser->getPayloadInternal();
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp
new file mode 100644
index 0000000..d03ba8b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/JingleParserTest.cpp
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Base/DateTime.h>
+
+#include <Swiften/Base/Log.h>
+
+using namespace Swift;
+
+class JingleParserTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(JingleParserTest);
+		CPPUNIT_TEST(testParse_Xep0166_Example3);
+		CPPUNIT_TEST(testParse_Xep0166_Example8);
+		
+		CPPUNIT_TEST(testParse_Xep0261_Example1);
+		CPPUNIT_TEST(testParse_Xep0261_Example3);
+		CPPUNIT_TEST(testParse_Xep0261_Example9);
+		CPPUNIT_TEST(testParse_Xep0261_Example13);
+		
+		CPPUNIT_TEST(testParse_Xep0234_Example1);
+		CPPUNIT_TEST(testParse_Xep0234_Example3);
+		CPPUNIT_TEST(testParse_Xep0234_Example5);
+		CPPUNIT_TEST(testParse_Xep0234_Example8);
+		CPPUNIT_TEST(testParse_Xep0234_Example10);
+		CPPUNIT_TEST(testParse_Xep0234_Example11);
+		CPPUNIT_TEST(testParse_Xep0234_Example12);
+		
+		CPPUNIT_TEST(testParse_Xep0260_Example1);
+		CPPUNIT_TEST(testParse_Xep0260_Example3);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		//http://xmpp.org/extensions/xep-0166.html#example-3
+		void testParse_Xep0166_Example3() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"	 action='session-terminate'\n"
+				"	 sid='a73sjjvkla37jfea'>\n"
+				"	<reason>\n"
+				"		<success/>\n"
+				"	</reason>\n"
+				"</jingle>\n"
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success, 
+				jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).type);
+		}
+		
+		//http://xmpp.org/extensions/xep-0166.html#example-8
+		void testParse_Xep0166_Example8() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-terminate'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"	<reason>\n"
+				"		<success/>\n"
+				"		<text>Sorry, gotta go!</text>\n"
+				"	</reason>\n"
+				"</jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success, 
+				jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).type);
+			CPPUNIT_ASSERT_EQUAL(std::string("Sorry, gotta go!"), 
+				jingle->getReason().get_value_or(JinglePayload::Reason(JinglePayload::Reason::UnknownType, "")).text);
+		}
+		
+		// IBB Transport Method Examples
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-1
+		void testParse_Xep0261_Example1() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"          action='session-initiate'\n"
+				"          initiator='romeo@montague.lit/orchard'\n"
+				"          sid='a73sjjvkla37jfea'>\n"
+				"    <content creator='initiator' name='ex'>\n"
+				"      <description xmlns='urn:xmpp:example'/>\n"
+				"      <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+				"                 block-size='4096'\n"
+				"                 sid='ch3d9s71'/>\n"
+				"    </content>\n"
+				"</jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+			JingleContentPayload::ref payload = payloads[0];
+			CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+			CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payload->getTransports().size());
+			
+			JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+			CPPUNIT_ASSERT(transportPaylod);
+			CPPUNIT_ASSERT_EQUAL(4096, transportPaylod->getBlockSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("ch3d9s71"), transportPaylod->getSessionID());
+		}
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-1
+		void testParse_Xep0261_Example3() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"          action='session-accept'\n"
+				"          initiator='romeo@montague.lit/orchard'\n"
+				"          responder='juliet@capulet.lit/balcony'\n"
+				"          sid='a73sjjvkla37jfea'>\n"
+				"    <content creator='initiator' name='ex'>\n"
+				"      <description xmlns='urn:xmpp:example'/>\n"
+				"      <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+				"                 block-size='2048'\n"
+				"                 sid='ch3d9s71'/>\n"
+				"    </content>\n"
+				"  </jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), jingle->getResponder());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+			JingleContentPayload::ref payload = payloads[0];
+			CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+			CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payload->getTransports().size());
+			
+			JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+			CPPUNIT_ASSERT(transportPaylod);
+			CPPUNIT_ASSERT_EQUAL(2048, transportPaylod->getBlockSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("ch3d9s71"), transportPaylod->getSessionID());
+		}
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-9
+		void testParse_Xep0261_Example9() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='transport-info'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"  <content creator='initiator' name='ex'>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:ibb:1'\n"
+				"               block-size='2048'\n"
+				"               sid='bt8a71h6'/>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::TransportInfo, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> payloads = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), payloads.size());
+			JingleContentPayload::ref payload = payloads[0];
+			CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, payload->getCreator());
+			CPPUNIT_ASSERT_EQUAL(std::string("ex"), payload->getName());
+			
+			JingleIBBTransportPayload::ref transportPaylod = payload->getTransport<JingleIBBTransportPayload>();
+			CPPUNIT_ASSERT(transportPaylod);
+			CPPUNIT_ASSERT_EQUAL(2048, transportPaylod->getBlockSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("bt8a71h6"), transportPaylod->getSessionID());	
+		}
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-13
+		void testParse_Xep0261_Example13() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"          action='session-terminate'\n"
+				"          initiator='romeo@montague.lit/orchard'\n"
+				"          sid='a73sjjvkla37jfea'>\n"
+				"    <reason><success/></reason>\n"
+				"  </jingle>\n"
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionTerminate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::Reason::Success, jingle->getReason().get_value_or(JinglePayload::Reason()).type);
+			
+		}
+		
+		// Jingle File Transfer Examples
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-1
+		void testParse_Xep0234_Example1() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"          action='session-initiate'\n"
+				"          initiator='romeo@montague.lit/orchard'\n"
+				"          sid='851ba2'>\n"
+				"    <content creator='initiator' name='a-file-offer'>\n"
+				"      <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"        <offer>\n"
+				"          <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"                date='1969-07-21T02:56:15Z'\n"
+				"                hash='552da749930852c69ae5d2141d3766b1'\n"
+				"                name='test.txt'\n"
+				"                size='1022'>\n"
+				"            <desc>This is a test. If this were a real file...</desc>\n"
+				"            <range/>\n"
+				"          </file>\n"
+				"        </offer>\n"
+				"      </description>\n"
+				"      <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"                 mode='tcp'\n"
+				"                 sid='vj3hs98y'>\n"
+				"        <candidate cid='hft54dqy'\n"
+				"                   host='192.168.4.1'\n"
+				"                   jid='romeo@montague.lit/orchard'\n"
+				"                   port='5086'\n"
+				"                   priority='8257636'\n"
+				"                   type='direct'/>\n"
+				"        <candidate cid='hutr46fe'\n"
+				"                   host='24.24.24.1'\n"
+				"                   jid='romeo@montague.lit/orchard'\n"
+				"                   port='5087'\n"
+				"                   priority='8258636'\n"
+				"                   type='direct'/>\n"
+				"      </transport>\n"
+				"    </content>\n"
+				"  </jingle>\n"
+			));
+						
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("851ba2"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+			
+			JingleFileTransferDescription::ref description = contents[0]->getDescription<JingleFileTransferDescription>();
+			
+			
+			std::vector<StreamInitiationFileInfo> offers = description->getOffers();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), offers.size());
+			CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), offers[0].getName());
+			CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), offers[0].getHash());
+			CPPUNIT_ASSERT(1022 == offers[0].getSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("This is a test. If this were a real file..."), offers[0].getDescription());
+			CPPUNIT_ASSERT_EQUAL(true, offers[0].getSupportsRangeRequests());
+			CPPUNIT_ASSERT(stringToDateTime("1969-07-21T02:56:15Z") == offers[0].getDate());
+			CPPUNIT_ASSERT_EQUAL(std::string("md5"), offers[0].getAlgo());	
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-3
+		void testParse_Xep0234_Example3() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-accept'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='851ba2'>\n"
+				"  <content creator='initiator' name='a-file-offer'>\n"
+				"    <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"      <offer>\n"
+				"        <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"              name='test.txt'\n"
+				"              size='1022'\n"
+				"              hash='552da749930852c69ae5d2141d3766b1'\n"
+				"              date='1969-07-21T02:56:15Z'>\n"
+				"          <desc>This is a test. If this were a real file...</desc>\n"
+				"          <range/>\n"
+				"        </file>\n"
+				"      </offer>\n"
+				"    </description>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               mode='tcp'\n"
+				"               sid='vj3hs98y'>\n"
+				"      <candidate cid='ht567dq'\n"
+				"                 host='192.169.1.10'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257636'\n"
+				"                 type='direct'/>\n"
+				"      <candidate cid='hr65dqyd'\n"
+				"                 host='134.102.201.180'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='16453'\n"
+				"                 priority='7929856'\n"
+				"                 type='assisted'/>\n"
+				"      <candidate cid='grt654q2'\n"
+				"                 host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257606'\n"
+				"                 type='direct'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+						
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("851ba2"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+			
+			JingleFileTransferDescription::ref description = contents[0]->getDescription<JingleFileTransferDescription>();
+			
+			
+			std::vector<StreamInitiationFileInfo> offers = description->getOffers();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), offers.size());
+			CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), offers[0].getName());
+			CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), offers[0].getHash());
+			CPPUNIT_ASSERT(1022 == offers[0].getSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("This is a test. If this were a real file..."), offers[0].getDescription());
+			CPPUNIT_ASSERT_EQUAL(true, offers[0].getSupportsRangeRequests());
+			CPPUNIT_ASSERT(stringToDateTime("1969-07-21T02:56:15Z") == offers[0].getDate());
+			CPPUNIT_ASSERT_EQUAL(std::string("md5"), offers[0].getAlgo());
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-5
+		void testParse_Xep0234_Example5() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='transport-info'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"  <content creator='initiator' name='ex'>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               sid='vj3hs98y'>\n"
+				"      <candidate-used cid='hr65dqyd'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+						
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::TransportInfo, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			std::vector<JingleContentPayload::ref> contents = jingle->getContents();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), contents.size());
+			
+			JingleS5BTransportPayload::ref transport = contents[0]->getTransport<JingleS5BTransportPayload>();
+			CPPUNIT_ASSERT(transport);
+
+			CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), transport->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(std::string("hr65dqyd"), transport->getCandidateUsed());
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-8
+		void testParse_Xep0234_Example8() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-info'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"	<checksum xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"	  <file>\n"
+				"	    <hashes xmlns='urn:xmpp:hashes:0'>\n"
+				"	      <hash algo='sha-1'>552da749930852c69ae5d2141d3766b1</hash>\n"
+				"	    </hashes>\n"
+				"	  </file>\n"
+				"	</checksum>\n"
+				"</jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInfo, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			JingleFileTransferHash::ref hash = jingle->getPayload<JingleFileTransferHash>();
+			CPPUNIT_ASSERT(hash);
+			CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), hash->getHashes().at("sha-1"));
+			
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-10
+		void testParse_Xep0234_Example10() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-initiate'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='uj3b2'>\n"
+				"  <content creator='initiator' name='a-file-request'>\n"
+				"    <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"      <request>\n"
+				"        <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"              hash='552da749930852c69ae5d2141d3766b1'>\n"
+				"          <range offset='270336'/>\n"
+				"        </file>\n"
+				"      </request>\n"
+				"    </description>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               mode='tcp'\n"
+				"               sid='xig361fj'>\n"
+				"      <candidate cid='ht567dq'\n"
+				"                 host='192.169.1.10'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257636'\n"
+				"                 type='direct'/>\n"
+				"      <candidate cid='hr65dqyd'\n"
+				"                 host='134.102.201.180'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='16453'\n"
+				"                 priority='7929856'\n"
+				"                 type='assisted'/>\n"
+				"      <candidate cid='grt654q2'\n"
+				"                 host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257606'\n"
+				"                 type='direct'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("uj3b2"), jingle->getSessionID());
+			
+			JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+			CPPUNIT_ASSERT(content);
+			
+			StreamInitiationFileInfo file = content->getDescription<JingleFileTransferDescription>()->getRequests()[0];
+			CPPUNIT_ASSERT_EQUAL(std::string("552da749930852c69ae5d2141d3766b1"), file.getHash());
+			CPPUNIT_ASSERT_EQUAL(270336, file.getRangeOffset());
+			CPPUNIT_ASSERT_EQUAL(true, file.getSupportsRangeRequests());
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-11
+		void testParse_Xep0234_Example11() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-initiate'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='h2va419i'>\n"
+				"  <content creator='initiator' name='a-file-offer'>\n"
+				"    <description xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"      <offer>\n"
+				"        <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"              date='2011-06-01T15:58:15Z'\n"
+				"              hash='a749930852c69ae5d2141d3766b1552d'\n"
+				"              name='somefile.txt'\n"
+				"              size='1234'/>\n"
+				"        <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"              date='2011-06-01T15:58:15Z'\n"
+				"              hash='930852c69ae5d2141d3766b1552da749'\n"
+				"              name='anotherfile.txt'\n"
+				"              size='2345'/>\n"
+				"        <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"              date='2011-06-01T15:58:15Z'\n"
+				"              hash='52c69ae5d2141d3766b1552da7499308'\n"
+				"              name='yetanotherfile.txt'\n"
+				"              size='3456'/>\n"
+				"      </offer>\n"
+				"    </description>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               mode='tcp'\n"
+				"               sid='vj3hs98y'>\n"
+				"      <candidate cid='hft54dqy'\n"
+				"                 host='192.168.4.1'\n"
+				"                 jid='romeo@montague.lit/orchard'\n"
+				"                 port='5086'\n"
+				"                 priority='8257636'\n"
+				"                 type='direct'/>\n"
+				"      <candidate cid='hutr46fe'\n"
+				"                 host='24.24.24.1'\n"
+				"                 jid='romeo@montague.lit/orchard'\n"
+				"                 port='5087'\n"
+				"                 priority='8258636'\n"
+				"                 type='direct'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"			
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("h2va419i"), jingle->getSessionID());
+			
+			JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+			CPPUNIT_ASSERT(content);
+			CPPUNIT_ASSERT_EQUAL(JingleContentPayload::InitiatorCreator, content->getCreator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a-file-offer"), content->getName());
+			
+			std::vector<StreamInitiationFileInfo> offers = content->getDescription<JingleFileTransferDescription>()->getOffers();
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), offers.size());
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-12
+		void testParse_Xep0234_Example12() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-info'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"  <received xmlns='urn:xmpp:jingle:apps:file-transfer:3'>\n"
+				"    <file xmlns='http://jabber.org/protocol/si/profile/file-transfer'\n"
+				"          hash='a749930852c69ae5d2141d3766b1552d'/>\n"
+				"  </received>\n"
+				"</jingle>\n"
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInfo, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			boost::shared_ptr<JingleFileTransferReceived> received = jingle->getPayload<JingleFileTransferReceived>();
+			CPPUNIT_ASSERT(received);
+			CPPUNIT_ASSERT_EQUAL(std::string("a749930852c69ae5d2141d3766b1552d"), received->getFileInfo().getHash());			
+		}
+		
+		// http://xmpp.org/extensions/xep-0260.html#example-1
+		void testParse_Xep0260_Example1() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-initiate'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"  <content creator='initiator' name='ex'>\n"
+				"    <description xmlns='urn:xmpp:example'/>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               mode='tcp'\n"
+				"               sid='vj3hs98y'>\n"
+				"      <candidate cid='hft54dqy'\n"
+				"                 host='192.168.4.1'\n"
+				"                 jid='romeo@montague.lit/orchard'\n"
+				"                 port='5086'\n"
+				"                 priority='8257636'\n"
+				"                 type='direct'/>\n"
+				"      <candidate cid='hutr46fe'\n"
+				"                 host='24.24.24.1'\n"
+				"                 jid='romeo@montague.lit/orchard'\n"
+				"                 port='5087'\n"
+				"                 priority='8258636'\n"
+				"                 type='direct'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionInitiate, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+			CPPUNIT_ASSERT(content);
+			
+			JingleS5BTransportPayload::ref s5bPayload = content->getTransport<JingleS5BTransportPayload>();
+			CPPUNIT_ASSERT(s5bPayload);
+			
+			CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), s5bPayload->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::TCPMode, s5bPayload->getMode());
+			CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasCandidateError());
+			CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasProxyError());
+			CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getActivated());
+			CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getCandidateUsed());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), s5bPayload->getCandidates().size());
+			
+			JingleS5BTransportPayload::Candidate candidate;
+			candidate = s5bPayload->getCandidates()[0];
+			CPPUNIT_ASSERT_EQUAL(std::string("hft54dqy"), candidate.cid);
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), candidate.jid);
+			CPPUNIT_ASSERT(HostAddressPort(HostAddress("192.168.4.1"), 5086) == candidate.hostPort);
+			CPPUNIT_ASSERT_EQUAL(8257636, candidate.priority);
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+			
+			candidate = s5bPayload->getCandidates()[1];
+			CPPUNIT_ASSERT_EQUAL(std::string("hutr46fe"), candidate.cid);
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), candidate.jid);
+			CPPUNIT_ASSERT(HostAddressPort(HostAddress("24.24.24.1"), 5087) == candidate.hostPort);
+			CPPUNIT_ASSERT_EQUAL(8258636, candidate.priority);
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+		}
+		
+		// http://xmpp.org/extensions/xep-0260.html#example-3
+		void testParse_Xep0260_Example3() {
+			PayloadsParserTester parser;
+			CPPUNIT_ASSERT(parser.parse(
+				"<jingle xmlns='urn:xmpp:jingle:1'\n"
+				"        action='session-accept'\n"
+				"        initiator='romeo@montague.lit/orchard'\n"
+				"        sid='a73sjjvkla37jfea'>\n"
+				"  <content creator='initiator' name='ex'>\n"
+				"    <description xmlns='urn:xmpp:example'/>\n"
+				"    <transport xmlns='urn:xmpp:jingle:transports:s5b:1'\n"
+				"               mode='tcp'\n"
+				"               sid='vj3hs98y'>\n"
+				"      <candidate cid='ht567dq'\n"
+				"                 host='192.169.1.10'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257636'\n"
+				"                 type='direct'/>\n"
+				"      <candidate cid='hr65dqyd'\n"
+				"                 host='134.102.201.180'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='16453'\n"
+				"                 priority='7929856'\n"
+				"                 type='assisted'/>\n"
+				"      <candidate cid='grt654q2'\n"
+				"                 host='2001:638:708:30c9:219:d1ff:fea4:a17d'\n"
+				"                 jid='juliet@capulet.lit/balcony'\n"
+				"                 port='6539'\n"
+				"                 priority='8257606'\n"
+				"                 type='direct'/>\n"
+				"    </transport>\n"
+				"  </content>\n"
+				"</jingle>\n"
+			));
+			
+			JinglePayload::ref jingle = parser.getPayload<JinglePayload>();
+			CPPUNIT_ASSERT(jingle);
+			CPPUNIT_ASSERT_EQUAL(JinglePayload::SessionAccept, jingle->getAction());
+			CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.lit/orchard"), jingle->getInitiator());
+			CPPUNIT_ASSERT_EQUAL(std::string("a73sjjvkla37jfea"), jingle->getSessionID());
+			
+			JingleContentPayload::ref content = jingle->getPayload<JingleContentPayload>();
+			CPPUNIT_ASSERT(content);
+			
+			JingleS5BTransportPayload::ref s5bPayload = content->getTransport<JingleS5BTransportPayload>();
+			CPPUNIT_ASSERT(s5bPayload);
+			
+			CPPUNIT_ASSERT_EQUAL(std::string("vj3hs98y"), s5bPayload->getSessionID());
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::TCPMode, s5bPayload->getMode());
+			CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasCandidateError());
+			CPPUNIT_ASSERT_EQUAL(false, s5bPayload->hasProxyError());
+			CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getActivated());
+			CPPUNIT_ASSERT_EQUAL(std::string(), s5bPayload->getCandidateUsed());
+			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), s5bPayload->getCandidates().size());
+
+			JingleS5BTransportPayload::Candidate candidate;
+			candidate = s5bPayload->getCandidates()[0];
+			CPPUNIT_ASSERT_EQUAL(std::string("ht567dq"), candidate.cid);
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+			CPPUNIT_ASSERT(HostAddressPort(HostAddress("192.169.1.10"), 6539) == candidate.hostPort);
+			CPPUNIT_ASSERT_EQUAL(8257636, candidate.priority);
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+
+			candidate = s5bPayload->getCandidates()[1];
+			CPPUNIT_ASSERT_EQUAL(std::string("hr65dqyd"), candidate.cid);
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+			CPPUNIT_ASSERT(HostAddressPort(HostAddress("134.102.201.180"), 16453) == candidate.hostPort);
+			CPPUNIT_ASSERT_EQUAL(7929856, candidate.priority);
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::AssistedType, candidate.type);
+
+			candidate = s5bPayload->getCandidates()[2];
+			CPPUNIT_ASSERT_EQUAL(std::string("grt654q2"), candidate.cid);
+			CPPUNIT_ASSERT_EQUAL(JID("juliet@capulet.lit/balcony"), candidate.jid);
+			CPPUNIT_ASSERT(HostAddressPort(HostAddress("2001:638:708:30c9:219:d1ff:fea4:a17d"), 6539) == candidate.hostPort);
+			CPPUNIT_ASSERT_EQUAL(8257606, candidate.priority);
+			CPPUNIT_ASSERT_EQUAL(JingleS5BTransportPayload::Candidate::DirectType, candidate.type);
+		}
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JingleParserTest);
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
index 47b2816..63a6c9b 100644
--- a/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
+++ b/Swiften/Parser/PayloadParsers/UnitTest/StreamInitiationParserTest.cpp
@@ -42,9 +42,9 @@ class StreamInitiationParserTest : public CppUnit::TestFixture {
 			StreamInitiation::ref si = parser.getPayload<StreamInitiation>();
 			CPPUNIT_ASSERT(si->getIsFileTransfer());
 			CPPUNIT_ASSERT(si->getFileInfo());
-			CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), si->getFileInfo()->name);
-			CPPUNIT_ASSERT_EQUAL(1022, si->getFileInfo()->size);
-			CPPUNIT_ASSERT_EQUAL(std::string("This is info about the file."), si->getFileInfo()->description);
+			CPPUNIT_ASSERT_EQUAL(std::string("test.txt"), si->getFileInfo()->getName());
+			CPPUNIT_ASSERT(1022 == si->getFileInfo()->getSize());
+			CPPUNIT_ASSERT_EQUAL(std::string("This is info about the file."), si->getFileInfo()->getDescription());
 			CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(si->getProvidedMethods().size()));
 			CPPUNIT_ASSERT_EQUAL(std::string("http://jabber.org/protocol/bytestreams"), si->getProvidedMethods()[0]);
 			CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:oob"), si->getProvidedMethods()[1]);
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index e6c16d1..3dbfbee 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -29,6 +29,15 @@ sources = [
 		"PayloadParsers/ErrorParser.cpp",
 		"PayloadParsers/FormParser.cpp",
 		"PayloadParsers/IBBParser.cpp",
+		"PayloadParsers/JingleParser.cpp",
+		"PayloadParsers/JingleReasonParser.cpp",
+		"PayloadParsers/JingleContentPayloadParser.cpp",
+		"PayloadParsers/JingleIBBTransportMethodPayloadParser.cpp",
+		"PayloadParsers/JingleS5BTransportMethodPayloadParser.cpp",
+		"PayloadParsers/JingleFileTransferDescriptionParser.cpp",
+		"PayloadParsers/JingleFileTransferReceivedParser.cpp",
+		"PayloadParsers/JingleFileTransferHashParser.cpp",
+		"PayloadParsers/StreamInitiationFileInfoParser.cpp",
 		"PayloadParsers/CommandParser.cpp",
 		"PayloadParsers/InBandRegistrationPayloadParser.cpp",
 		"PayloadParsers/SearchPayloadParser.cpp",
@@ -56,6 +65,7 @@ sources = [
 		"PayloadParsers/NicknameParser.cpp",
 		"PayloadParsers/ReplaceParser.cpp",
 		"PayloadParsers/LastParser.cpp",
+		"PayloadParsers/S5BProxyRequestParser.cpp",
 		"PlatformXMLParserFactory.cpp",
 		"PresenceParser.cpp",
 		"SerializingParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index e20e5e6..ca8dee5 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -6,7 +6,7 @@ Import("env")
 # Flags
 ################################################################################
 
-swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI"]
+swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI", "LIBMINIUPNPC", "LIBNATPMP"]
 
 if env["SCONS_STAGE"] == "flags" :
 	env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")
@@ -21,6 +21,10 @@ if env["SCONS_STAGE"] == "flags" :
 	env["SWIFTEN_LIBRARY"] = "Swiften"
 	env["SWIFTEN_LIBRARY_FILE"] = "Swiften"
 	env["SWIFTEN_LIBRARY_ALIASES"] = []
+	
+	if env["PLATFORM"] == "win32" :
+		env.Append(CCFLAGS = ["-DSTATICLIB"])
+	
 	if ARGUMENTS.get("swiften_dll", False) :
 		if env["PLATFORM"] == "win32" :
 			pass
@@ -87,6 +91,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Client/NickManager.cpp",
 			"Client/NickManagerImpl.cpp",
 			"Client/Storages.cpp",
+			"Client/XMLBeautifier.cpp",
 			"Compress/ZLibCodecompressor.cpp",
 			"Compress/ZLibDecompressor.cpp",
 			"Compress/ZLibCompressor.cpp",
@@ -166,6 +171,14 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializers/SearchPayloadSerializer.cpp",
 			"Serializer/PayloadSerializers/FormSerializer.cpp",
 			"Serializer/PayloadSerializers/NicknameSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp",
+			"Serializer/PayloadSerializers/JinglePayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp",
 			"Serializer/PresenceSerializer.cpp",
 			"Serializer/StanzaSerializer.cpp",
 			"Serializer/StreamErrorSerializer.cpp",
@@ -281,6 +294,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"),
+			File("Parser/PayloadParsers/UnitTest/JingleParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SecurityLabelParserTest.cpp"),
 			File("Parser/PayloadParsers/UnitTest/SecurityLabelsCatalogParserTest.cpp"),
@@ -340,6 +354,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"),
+			File("Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp"),
 			File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),
 			File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),
 			File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 0ddd445..55c39c7 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -49,6 +49,16 @@
 #include <Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/LastSerializer.h>
 
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h>
+
 namespace Swift {
 
 FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
@@ -92,6 +102,17 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
 	serializers_.push_back(new SearchPayloadSerializer());
 	serializers_.push_back(new ReplaceSerializer());
 	serializers_.push_back(new LastSerializer());
+	
+	serializers_.push_back(new StreamInitiationFileInfoSerializer());
+	serializers_.push_back(new JingleContentPayloadSerializer());
+	serializers_.push_back(new JingleFileTransferDescriptionSerializer());
+	serializers_.push_back(new JingleFileTransferHashSerializer());
+	serializers_.push_back(new JingleFileTransferReceivedSerializer());
+	serializers_.push_back(new JingleIBBTransportPayloadSerializer());
+	serializers_.push_back(new JingleS5BTransportPayloadSerializer());
+	serializers_.push_back(new JinglePayloadSerializer(this));
+	serializers_.push_back(new S5BProxyRequestSerializer());
+	
 	foreach(PayloadSerializer* serializer, serializers_) {
 		addSerializer(serializer);
 	}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp
new file mode 100644
index 0000000..90bd940
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+
+#include "Swiften/FileTransfer/JingleTransport.h"
+
+namespace Swift {
+
+JingleContentPayloadSerializer::JingleContentPayloadSerializer() {
+}
+
+std::string JingleContentPayloadSerializer::serializePayload(boost::shared_ptr<JingleContentPayload> payload) const {
+	XMLElement payloadXML("content");
+	payloadXML.setAttribute("creator", creatorToString(payload->getCreator()));
+	payloadXML.setAttribute("name", payload->getName());
+	
+	if (!payload->getDescriptions().empty()) {
+		// JingleFileTransferDescription
+		JingleFileTransferDescriptionSerializer ftSerializer;
+		JingleFileTransferDescription::ref filetransfer;
+		
+		foreach(JingleDescription::ref desc, payload->getDescriptions()) {
+			if ((filetransfer = boost::dynamic_pointer_cast<JingleFileTransferDescription>(desc))) {
+				payloadXML.addNode(boost::make_shared<XMLRawTextNode>(ftSerializer.serializePayload(filetransfer)));
+			}
+		}
+	}
+	
+	if (!payload->getTransports().empty()) {	
+		// JingleIBBTransportPayload
+		JingleIBBTransportPayloadSerializer ibbSerializer;
+		JingleIBBTransportPayload::ref ibb;
+		
+		// JingleS5BTransportPayload
+		JingleS5BTransportPayloadSerializer s5bSerializer;
+		JingleS5BTransportPayload::ref s5b;
+
+		foreach(JingleTransportPayload::ref transport, payload->getTransports()) {
+			if ((ibb = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport))) {
+				payloadXML.addNode(boost::make_shared<XMLRawTextNode>(ibbSerializer.serializePayload(ibb)));
+			} else if ((s5b = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(transport))) {
+				payloadXML.addNode(boost::make_shared<XMLRawTextNode>(s5bSerializer.serializePayload(s5b)));
+			}
+		}
+	}
+	return payloadXML.serialize();
+}
+
+std::string JingleContentPayloadSerializer::creatorToString(JingleContentPayload::Creator creator) const {
+	switch(creator) {
+		case JingleContentPayload::InitiatorCreator:
+			return "initiator";
+		case JingleContentPayload::ResponderCreator:
+			return "responder";
+		case JingleContentPayload::UnknownCreator:
+			std::cerr << "Serializing unknown creator value." << std::endl;
+			return "ERROR ERROR ERROR";
+	}
+	assert(false);
+}
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h
new file mode 100644
index 0000000..2de0064
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	
+	class JingleContentPayloadSerializer : public GenericPayloadSerializer<JingleContentPayload> {
+		public:
+			JingleContentPayloadSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleContentPayload>)  const;
+			
+		private:
+			std::string creatorToString(JingleContentPayload::Creator creator) const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp
new file mode 100644
index 0000000..16337ff
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+
+namespace Swift {
+
+JingleFileTransferDescriptionSerializer::JingleFileTransferDescriptionSerializer() {
+}
+
+std::string JingleFileTransferDescriptionSerializer::serializePayload(boost::shared_ptr<JingleFileTransferDescription> payload) const {
+	XMLElement description("description", "urn:xmpp:jingle:apps:file-transfer:3");
+	StreamInitiationFileInfoSerializer fileInfoSerializer;
+	if (!payload->getOffers().empty()) {
+		boost::shared_ptr<XMLElement> offers = boost::make_shared<XMLElement>("offer");
+		foreach(const StreamInitiationFileInfo &fileInfo, payload->getOffers()) {
+			boost::shared_ptr<XMLRawTextNode> fileInfoXML = boost::make_shared<XMLRawTextNode>(fileInfoSerializer.serialize(boost::make_shared<StreamInitiationFileInfo>(fileInfo)));
+			offers->addNode(fileInfoXML);
+		}
+		description.addNode(offers);
+	}
+	if (!payload->getRequests().empty()) {
+		boost::shared_ptr<XMLElement> requests = boost::make_shared<XMLElement>("request");
+		foreach(const StreamInitiationFileInfo &fileInfo, payload->getRequests()) {
+			boost::shared_ptr<XMLRawTextNode> fileInfoXML = boost::make_shared<XMLRawTextNode>(fileInfoSerializer.serialize(boost::make_shared<StreamInitiationFileInfo>(fileInfo)));
+			requests->addNode(fileInfoXML);
+		}
+		description.addNode(requests);
+	}
+	return description.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h
new file mode 100644
index 0000000..5131435
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+
+
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JingleFileTransferDescriptionSerializer : public GenericPayloadSerializer<JingleFileTransferDescription> {
+		public:
+			JingleFileTransferDescriptionSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferDescription>)  const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp
new file mode 100644
index 0000000..2bd3afa
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+
+#include <string>
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+
+namespace Swift {
+
+JingleFileTransferHashSerializer::JingleFileTransferHashSerializer() {
+}
+
+std::string JingleFileTransferHashSerializer::serializePayload(boost::shared_ptr<JingleFileTransferHash> payload) const {
+	// code for version urn:xmpp:jingle:apps:file-transfer:2
+	//XMLElement hash("hash", "urn:xmpp:jingle:apps:file-transfer:info:2", payload->getHash());
+
+	// code for version urn:xmpp:jingle:apps:file-transfer:3
+	XMLElement checksum("checksum", "urn:xmpp:jingle:apps:file-transfer:3");
+	boost::shared_ptr<XMLElement> file = boost::make_shared<XMLElement>("file");
+	checksum.addNode(file);
+	boost::shared_ptr<XMLElement> hashes = boost::make_shared<XMLElement>("hashes", "urn:xmpp:hashes:0");
+	file->addNode(hashes);
+	foreach(const JingleFileTransferHash::HashesMap::value_type& pair, payload->getHashes()) {
+		boost::shared_ptr<XMLElement> hash = boost::make_shared<XMLElement>("hash", "", pair.second);
+		hash->setAttribute("algo", pair.first);
+		hashes->addNode(hash);
+	}
+
+	return checksum.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h
new file mode 100644
index 0000000..7fa6ac5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JingleFileTransferHashSerializer : public GenericPayloadSerializer<JingleFileTransferHash> {
+		public:
+			JingleFileTransferHashSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferHash>)  const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp
new file mode 100644
index 0000000..40be70e
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+
+JingleFileTransferReceivedSerializer::JingleFileTransferReceivedSerializer() {
+}
+
+std::string JingleFileTransferReceivedSerializer::serializePayload(boost::shared_ptr<JingleFileTransferReceived> payload) const {
+	XMLElement receivedElement("received", "urn:xmpp:jingle:apps:file-transfer:3");
+	XMLElement::ref fileElement = boost::make_shared<XMLElement>("file", "http://jabber.org/protocol/si/profile/file-transfer");
+	fileElement->setAttribute("hash", payload->getFileInfo().getHash());
+	if (payload->getFileInfo().getAlgo() != "md5") {
+		fileElement->setAttribute("algo", payload->getFileInfo().getAlgo());
+	}
+	receivedElement.addNode(fileElement);
+	return receivedElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h
new file mode 100644
index 0000000..4151dd0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JingleFileTransferReceivedSerializer : public GenericPayloadSerializer<JingleFileTransferReceived> {
+		public:
+			JingleFileTransferReceivedSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleFileTransferReceived>)  const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp
new file mode 100644
index 0000000..029a5b4
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+
+namespace Swift {
+
+JingleIBBTransportPayloadSerializer::JingleIBBTransportPayloadSerializer() {
+}
+
+std::string JingleIBBTransportPayloadSerializer::serializePayload(boost::shared_ptr<JingleIBBTransportPayload> payload) const {
+	XMLElement payloadXML("transport", "urn:xmpp:jingle:transports:ibb:1");
+	payloadXML.setAttribute("block-size", boost::lexical_cast<std::string>(payload->getBlockSize()));
+	payloadXML.setAttribute("sid", payload->getSessionID());
+
+	return payloadXML.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h
new file mode 100644
index 0000000..ac9cba9
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleIBBTransportPayloadSerializer.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+
+
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JingleIBBTransportPayloadSerializer : public GenericPayloadSerializer<JingleIBBTransportPayload> {
+		public:
+			JingleIBBTransportPayloadSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleIBBTransportPayload>)  const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp
new file mode 100644
index 0000000..25d35ff
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferHashSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferReceivedSerializer.h>
+
+#include <Swiften/Serializer/PayloadSerializerCollection.h>
+
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleContentPayload.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+
+namespace Swift {
+
+JinglePayloadSerializer::JinglePayloadSerializer(PayloadSerializerCollection* serializers) : serializers(serializers) {
+}
+
+std::string JinglePayloadSerializer::serializePayload(boost::shared_ptr<JinglePayload> payload) const {
+	XMLElement jinglePayload("jingle", "urn:xmpp:jingle:1");
+	jinglePayload.setAttribute("action", actionToString(payload->getAction()));
+	jinglePayload.setAttribute("initiator", payload->getInitiator());
+	jinglePayload.setAttribute("sid", payload->getSessionID());
+	
+	if (!payload->getPayloads().empty()) {		
+		foreach(boost::shared_ptr<Payload> subPayload, payload->getPayloads()) {
+			PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload);
+			if (serializer) {
+				jinglePayload.addNode(boost::shared_ptr<XMLRawTextNode>(new XMLRawTextNode(serializer->serialize(subPayload))));
+			}
+		}
+	}
+	
+	if (payload->getReason().is_initialized()) {
+		boost::shared_ptr<XMLElement> reason = boost::make_shared<XMLElement>("reason");
+		reason->addNode(boost::make_shared<XMLElement>(reasonTypeToString(payload->getReason()->type)));
+		if (!payload->getReason()->text.empty()) {
+			reason->addNode(boost::make_shared<XMLElement>("desc", "", payload->getReason()->text));
+		}
+		jinglePayload.addNode(reason);
+	}
+	
+	return jinglePayload.serialize();
+}
+
+std::string JinglePayloadSerializer::actionToString(JinglePayload::Action action) const {
+	switch(action) {
+		case JinglePayload::ContentAccept:
+			return "content-accept";
+		case JinglePayload::ContentAdd:
+			return "content-add";
+		case JinglePayload::ContentModify:
+			return "content-modify";
+		case JinglePayload::ContentReject:
+			return "content-reject";
+		case JinglePayload::ContentRemove:
+			return "content-remove";
+		case JinglePayload::DescriptionInfo:
+			return "description-info";
+		case JinglePayload::SecurityInfo:
+			return "security-info";
+		case JinglePayload::SessionAccept:
+			return "session-accept";
+		case JinglePayload::SessionInfo:
+			return "session-info";
+		case JinglePayload::SessionInitiate:
+			return "session-initiate";
+		case JinglePayload::SessionTerminate:
+			return "session-terminate";
+		case JinglePayload::TransportAccept:
+			return "transport-accept";
+		case JinglePayload::TransportInfo:
+			return "transport-info";
+		case JinglePayload::TransportReject:
+			return "transport-reject";
+		case JinglePayload::TransportReplace:
+			return "transport-replace";
+		case JinglePayload::UnknownAction:
+			std::cerr << "Serializing unknown action value." << std::endl;
+			return "";
+	}
+	assert(false);
+}
+
+std::string JinglePayloadSerializer::reasonTypeToString(JinglePayload::Reason::Type type) const {
+	switch(type) {
+		case JinglePayload::Reason::UnknownType:
+			std::cerr << "Unknown jingle reason type!" << std::endl;
+			return "";
+		case JinglePayload::Reason::AlternativeSession:
+			return "alternative-session";
+		case JinglePayload::Reason::Busy:
+			return "busy";
+		case JinglePayload::Reason::Cancel:
+			return "cancel";
+		case JinglePayload::Reason::ConnectivityError:
+			return "connectivity-error";
+		case JinglePayload::Reason::Decline:
+			return "decline";
+		case JinglePayload::Reason::Expired:
+			return "expired";
+		case JinglePayload::Reason::FailedApplication:
+			return "failed-application";
+		case JinglePayload::Reason::FailedTransport:
+			return "failed-transport";
+		case JinglePayload::Reason::GeneralError:
+			return "general-error";
+		case JinglePayload::Reason::Gone:
+			return "gone";
+		case JinglePayload::Reason::IncompatibleParameters:
+			return "incompatible-parameters";
+		case JinglePayload::Reason::MediaError:
+			return "media-error";
+		case JinglePayload::Reason::SecurityError:
+			return "security-error";
+		case JinglePayload::Reason::Success:
+			return "success";
+		case JinglePayload::Reason::Timeout:
+			return "timeout";
+		case JinglePayload::Reason::UnsupportedApplications:
+			return "unsupported-applications";
+		case JinglePayload::Reason::UnsupportedTransports:
+			return "unsupported-transports";
+	}
+	assert(false);
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h
new file mode 100644
index 0000000..ccdb6d0
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JinglePayload.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JinglePayloadSerializer : public GenericPayloadSerializer<JinglePayload> {
+		public:
+			JinglePayloadSerializer(PayloadSerializerCollection*);
+			
+			virtual std::string serializePayload(boost::shared_ptr<JinglePayload>)  const;
+			
+		private:
+			std::string actionToString(JinglePayload::Action action) const;
+			std::string reasonTypeToString(JinglePayload::Reason::Type type) const;
+		
+		private:
+			PayloadSerializerCollection* serializers;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp
new file mode 100644
index 0000000..c5b40d5
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLNode.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Base/Log.h>
+
+namespace Swift {
+
+JingleS5BTransportPayloadSerializer::JingleS5BTransportPayloadSerializer() {
+}
+
+std::string JingleS5BTransportPayloadSerializer::serializePayload(boost::shared_ptr<JingleS5BTransportPayload> payload) const {
+	XMLElement payloadXML("transport", "urn:xmpp:jingle:transports:s5b:1");
+	payloadXML.setAttribute("sid", payload->getSessionID());
+	payloadXML.setAttribute("mode", modeToString(payload->getMode()));
+	
+	foreach(JingleS5BTransportPayload::Candidate candidate, payload->getCandidates()) {
+		boost::shared_ptr<XMLElement> candidateXML = boost::make_shared<XMLElement>("candidate");
+		candidateXML->setAttribute("cid", candidate.cid);
+		candidateXML->setAttribute("host", candidate.hostPort.getAddress().toString());
+		candidateXML->setAttribute("jid", candidate.jid.toString());
+		candidateXML->setAttribute("port", boost::lexical_cast<std::string>(candidate.hostPort.getPort()));
+		candidateXML->setAttribute("priority", boost::lexical_cast<std::string>(candidate.priority));
+		candidateXML->setAttribute("type", typeToString(candidate.type));
+		payloadXML.addNode(candidateXML);
+	}
+	
+	if (payload->hasCandidateError()) {
+		payloadXML.addNode(boost::make_shared<XMLElement>("candidate-error"));
+	}
+	if (payload->hasProxyError()) {
+		payloadXML.addNode(boost::make_shared<XMLElement>("proxy-error"));
+	}
+	
+	if (!payload->getActivated().empty()) {
+		boost::shared_ptr<XMLElement> activatedXML = boost::make_shared<XMLElement>("activated");
+		activatedXML->setAttribute("cid", payload->getActivated());
+		payloadXML.addNode(activatedXML);
+	}
+	if (!payload->getCandidateUsed().empty()) {
+		boost::shared_ptr<XMLElement> candusedXML = boost::make_shared<XMLElement>("candidate-used");
+		candusedXML->setAttribute("cid", payload->getCandidateUsed());
+		payloadXML.addNode(candusedXML);
+	}
+	
+	return payloadXML.serialize();
+}
+
+std::string JingleS5BTransportPayloadSerializer::modeToString(JingleS5BTransportPayload::Mode mode) const {
+	switch(mode) {
+		case JingleS5BTransportPayload::TCPMode:
+			return "tcp";
+		case JingleS5BTransportPayload::UDPMode:
+			return "udp";
+	}
+	assert(false);
+}
+
+std::string JingleS5BTransportPayloadSerializer::typeToString(JingleS5BTransportPayload::Candidate::Type type) const {
+	switch(type) {
+		case JingleS5BTransportPayload::Candidate::AssistedType:
+			return "assisted";
+		case JingleS5BTransportPayload::Candidate::DirectType:
+			return "direct";
+		case JingleS5BTransportPayload::Candidate::ProxyType:
+			return "proxy";
+		case JingleS5BTransportPayload::Candidate::TunnelType:
+			return "tunnel";
+	}
+	assert(false);
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h
new file mode 100644
index 0000000..210688d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/JingleS5BTransportPayloadSerializer.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+	class XMLElement;
+	
+	class JingleS5BTransportPayloadSerializer : public GenericPayloadSerializer<JingleS5BTransportPayload> {
+		public:
+			JingleS5BTransportPayloadSerializer();
+			
+			virtual std::string serializePayload(boost::shared_ptr<JingleS5BTransportPayload>)  const;
+			
+		private:
+			std::string modeToString(JingleS5BTransportPayload::Mode) const;
+			std::string typeToString(JingleS5BTransportPayload::Candidate::Type) const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h b/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h
new file mode 100644
index 0000000..b523588
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/S5BProxyRequestSerializer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <boost/lexical_cast.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <Swiften/Elements/S5BProxyRequest.h>
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+
+	class S5BProxyRequestSerializer : public GenericPayloadSerializer<S5BProxyRequest> {
+		public:
+			virtual std::string serializePayload(boost::shared_ptr<S5BProxyRequest> s5bProxyRequest) const {
+				XMLElement queryElement("query", "http://jabber.org/protocol/bytestreams");
+				if (s5bProxyRequest && s5bProxyRequest->getStreamHost()) {
+					boost::shared_ptr<XMLElement> streamHost = boost::make_shared<XMLElement>("streamhost");
+					streamHost->setAttribute("host", s5bProxyRequest->getStreamHost().get().addressPort.getAddress().toString());
+					streamHost->setAttribute("port", boost::lexical_cast<std::string>(s5bProxyRequest->getStreamHost().get().addressPort.getPort()));
+					streamHost->setAttribute("jid", s5bProxyRequest->getStreamHost().get().jid.toString());
+					queryElement.addNode(streamHost);
+				} else if (s5bProxyRequest && s5bProxyRequest->getActivate()) {
+					queryElement.setAttribute("sid", s5bProxyRequest->getSID());
+					boost::shared_ptr<XMLElement> activate = boost::make_shared<XMLElement>("activate", "", s5bProxyRequest->getActivate().get().toString());
+					queryElement.addNode(activate);
+				}
+				return queryElement.serialize();
+			}
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp
new file mode 100644
index 0000000..7b0cad8
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Base/DateTime.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+#include <Swiften/Serializer/XML/XMLRawTextNode.h>
+#include <Swiften/Serializer/XML/XMLTextNode.h>
+
+
+
+namespace Swift {
+
+StreamInitiationFileInfoSerializer::StreamInitiationFileInfoSerializer() {
+}
+								    
+std::string StreamInitiationFileInfoSerializer::serializePayload(boost::shared_ptr<StreamInitiationFileInfo> fileInfo) const {
+	XMLElement fileElement("file", "http://jabber.org/protocol/si/profile/file-transfer");
+	
+	if (fileInfo->getDate() != stringToDateTime("")) {
+		fileElement.setAttribute("date", dateTimeToString(fileInfo->getDate()));
+	}
+	fileElement.setAttribute("hash", fileInfo->getHash());
+	if (fileInfo->getAlgo() != "md5") {
+		fileElement.setAttribute("algo", fileInfo->getAlgo());
+	}
+	if (!fileInfo->getName().empty()) {
+		fileElement.setAttribute("name", fileInfo->getName());
+	}
+	if (fileInfo->getSize() != 0) {
+		fileElement.setAttribute("size", boost::lexical_cast<std::string>(fileInfo->getSize()));
+	}
+	if (!fileInfo->getDescription().empty()) {
+		boost::shared_ptr<XMLElement> desc = boost::make_shared<XMLElement>("desc", "", fileInfo->getDescription());
+		fileElement.addNode(desc);
+	}
+	if (fileInfo->getSupportsRangeRequests()) {
+		boost::shared_ptr<XMLElement> range = boost::make_shared<XMLElement>("range");
+		if (fileInfo->getRangeOffset() != 0) {
+			range->setAttribute("offset", boost::lexical_cast<std::string>(fileInfo->getRangeOffset()));
+		}
+		fileElement.addNode(range);
+	}
+	return fileElement.serialize();
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h
new file mode 100644
index 0000000..4ac0a0d
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+	class PayloadSerializerCollection;
+
+	class StreamInitiationFileInfoSerializer : public GenericPayloadSerializer<StreamInitiationFileInfo> {
+		public:
+			StreamInitiationFileInfoSerializer();
+
+			virtual std::string serializePayload(boost::shared_ptr<StreamInitiationFileInfo>)  const;
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
index 3b71bfb..9ccfab2 100644
--- a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp
@@ -36,13 +36,13 @@ std::string StreamInitiationSerializer::serializePayload(boost::shared_ptr<Strea
 	if (streamInitiation->getFileInfo()) {
 		StreamInitiationFileInfo file = *streamInitiation->getFileInfo();
 		boost::shared_ptr<XMLElement> fileElement(new XMLElement("file", "http://jabber.org/protocol/si/profile/file-transfer"));
-		fileElement->setAttribute("name", file.name);
-		if (file.size != -1) {
-			fileElement->setAttribute("size", boost::lexical_cast<std::string>(file.size));
+		fileElement->setAttribute("name", file.getName());
+		if (file.getSize() != 0) {
+			fileElement->setAttribute("size", boost::lexical_cast<std::string>(file.getSize()));
 		}
-		if (!file.description.empty()) {
+		if (!file.getDescription().empty()) {
 			boost::shared_ptr<XMLElement> descElement(new XMLElement("desc"));
-			descElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(file.description)));
+			descElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(file.getDescription())));
 			fileElement->addNode(descElement);
 		}
 		siElement.addNode(fileElement);
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp
new file mode 100644
index 0000000..e3ec8fc
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2011 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Serializer/PayloadSerializers/JingleFileTransferDescriptionSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/JinglePayloadSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h>
+#include <Swiften/Elements/JingleFileTransferDescription.h>
+#include <Swiften/Elements/StreamInitiationFileInfo.h>
+#include <Swiften/Elements/JingleIBBTransportPayload.h>
+#include <Swiften/Elements/JingleS5BTransportPayload.h>
+#include <Swiften/Elements/JingleFileTransferHash.h>
+#include <Swiften/Elements/JinglePayload.h>
+#include <Swiften/Elements/JingleFileTransferReceived.h>
+#include <Swiften/Base/DateTime.h>
+
+using namespace Swift;
+
+class JingleSerializersTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(JingleSerializersTest);
+		CPPUNIT_TEST(testSerialize_StreamInitiationFileInfo);
+		CPPUNIT_TEST(testSerialize_StreamInitiationFileInfoRange);
+		
+		CPPUNIT_TEST(testSerialize_Xep0261_Example1);
+		CPPUNIT_TEST(testSerialize_Xep0261_Example9);
+		CPPUNIT_TEST(testSerialize_Xep0261_Example13);
+		
+		CPPUNIT_TEST(testSerialize_Xep0234_Example1);
+		CPPUNIT_TEST(testSerialize_Xep0234_Example3);
+		CPPUNIT_TEST(testSerialize_Xep0234_Example5);
+		CPPUNIT_TEST(testSerialize_Xep0234_Example8);
+		CPPUNIT_TEST(testSerialize_Xep0234_Example10);
+		CPPUNIT_TEST(testSerialize_Xep0234_Example13);
+
+		CPPUNIT_TEST(testSerialize_Xep0260_Example1);
+
+		CPPUNIT_TEST_SUITE_END();
+		
+		 boost::shared_ptr<JinglePayloadSerializer> createTestling() {
+			 return boost::make_shared<JinglePayloadSerializer>(&collection);
+		}
+
+		
+	public:
+		void testSerialize_StreamInitiationFileInfo() {
+			std::string expected =	"<file"
+							" date=\"1969-07-21T02:56:15Z\""
+							" hash=\"552da749930852c69ae5d2141d3766b1\""
+							" name=\"test.txt\""
+							" size=\"1022\""
+							" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+							"<desc>This is a test. If this were a real file...</desc>"
+							"<range/>"
+						"</file>";
+			
+			StreamInitiationFileInfo::ref fileInfo = boost::make_shared<StreamInitiationFileInfo>();
+			fileInfo->setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+			fileInfo->setHash("552da749930852c69ae5d2141d3766b1");
+			fileInfo->setSize(1022);
+			fileInfo->setName("test.txt");
+			fileInfo->setDescription("This is a test. If this were a real file...");
+			fileInfo->setSupportsRangeRequests(true);
+			
+			boost::shared_ptr<StreamInitiationFileInfoSerializer> serializer = boost::make_shared<StreamInitiationFileInfoSerializer>();
+			CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo));
+		}
+		
+		void testSerialize_StreamInitiationFileInfoRange() {
+			std::string expected =	"<file hash=\"552da749930852c69ae5d2141d3766b1\""
+							" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+							"<range offset=\"270336\"/>"
+						"</file>";
+			
+			StreamInitiationFileInfo::ref fileInfo = boost::make_shared<StreamInitiationFileInfo>();
+			fileInfo->setHash("552da749930852c69ae5d2141d3766b1");
+			fileInfo->setSupportsRangeRequests(true);
+			fileInfo->setRangeOffset(270336);
+			
+			boost::shared_ptr<StreamInitiationFileInfoSerializer> serializer = boost::make_shared<StreamInitiationFileInfoSerializer>();
+			CPPUNIT_ASSERT_EQUAL(expected, serializer->serializePayload(fileInfo));
+		}
+		
+		
+		// IBB Transport Method Examples
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-1
+		void testSerialize_Xep0261_Example1() {
+			std::string expected = 
+				"<jingle action=\"session-initiate\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"ex\">"
+						"<transport block-size=\"4096\""
+							" sid=\"ch3d9s71\""
+							" xmlns=\"urn:xmpp:jingle:transports:ibb:1\"/>"
+					"</content>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionInitiate);
+			payload->setSessionID("a73sjjvkla37jfea");
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			
+			JingleIBBTransportPayload::ref transport = boost::make_shared<JingleIBBTransportPayload>();
+			transport->setBlockSize(4096);
+			transport->setSessionID("ch3d9s71");
+			
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("ex");
+			content->addTransport(transport);
+			
+			payload->addPayload(content);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-9
+		void testSerialize_Xep0261_Example9() {
+			std::string expected =
+				"<jingle action=\"transport-info\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"ex\">"
+						"<transport block-size=\"2048\""
+							" sid=\"bt8a71h6\""
+							" xmlns=\"urn:xmpp:jingle:transports:ibb:1\"/>"
+					"</content>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::TransportInfo);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+			
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("ex");
+			
+			JingleIBBTransportPayload::ref transport = boost::make_shared<JingleIBBTransportPayload>();
+			transport->setBlockSize(2048);
+			transport->setSessionID("bt8a71h6");
+			
+			content->addTransport(transport);
+			payload->addPayload(content);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0261.html#example-13
+		void testSerialize_Xep0261_Example13() {
+			std::string expected =
+				"<jingle action=\"session-terminate\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<reason><success/></reason>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionTerminate);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+			payload->setReason(JinglePayload::Reason(JinglePayload::Reason::Success));
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-1
+		void testSerialize_Xep0234_Example1() {
+			std::string expected =	"<description xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+							"<offer>"
+								"<file"
+									" date=\"1969-07-21T02:56:15Z\""
+									" hash=\"552da749930852c69ae5d2141d3766b1\""
+									" name=\"test.txt\""
+									" size=\"1022\""
+									" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+									"<desc>This is a test. If this were a real file...</desc>"
+									"<range/>"
+								"</file>"
+							"</offer>"
+						"</description>";
+			JingleFileTransferDescription::ref desc = boost::make_shared<JingleFileTransferDescription>();
+			StreamInitiationFileInfo fileInfo;
+			
+			fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+			fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+			fileInfo.setSize(1022);
+			fileInfo.setName("test.txt");
+			fileInfo.setDescription("This is a test. If this were a real file...");
+			fileInfo.setSupportsRangeRequests(true);
+			
+			desc->addOffer(fileInfo);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, boost::make_shared<JingleFileTransferDescriptionSerializer>()->serialize(desc));
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-3
+		void testSerialize_Xep0234_Example3() {
+			std::string expected =
+				"<jingle action=\"session-accept\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"851ba2\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"a-file-offer\">"
+						"<description xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+							"<offer>"
+								"<file"
+									" date=\"1969-07-21T02:56:15Z\""
+									" hash=\"552da749930852c69ae5d2141d3766b1\""
+									" name=\"test.txt\""
+									" size=\"1022\""
+									" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+									"<desc>This is a test. If this were a real file...</desc>"
+									"<range/>"
+								"</file>"
+							"</offer>"
+						"</description>"
+						/*"<transport xmlns=\"urn:xmpp:jingle:transports:s5b:1\""
+							" mode=\"tcp\""
+							" sid=\"vj3hs98y\">"
+							"<candidate cid=\"ht567dq\""
+								" host=\"192.169.1.10\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"6539\""
+								" priority=\"8257636\""
+								" type=\"direct\"/>"
+							"<candidate cid=\"hr65dqyd\""
+								" host=\"134.102.201.180\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"16453\""
+								" priority=\"7929856\""
+								" type=\"assisted\"/>"
+							"<candidate cid=\"grt654q2\""
+								" host=\"2001:638:708:30c9:219:d1ff:fea4:a17d\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"6539\""
+								" priority=\"8257606\""
+								" type=\"direct\"/>"
+						"</transport>"*/
+					"</content>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionAccept);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("851ba2");
+			
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("a-file-offer");
+			
+			JingleFileTransferDescription::ref description = boost::make_shared<JingleFileTransferDescription>();
+			StreamInitiationFileInfo fileInfo;
+			fileInfo.setName("test.txt");
+			fileInfo.setSize(1022);
+			fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+			fileInfo.setDate(stringToDateTime("1969-07-21T02:56:15Z"));
+			fileInfo.setDescription("This is a test. If this were a real file...");
+			fileInfo.setSupportsRangeRequests(true);
+			
+			description->addOffer(fileInfo);
+			content->addDescription(description);
+			payload->addPayload(content);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-5
+		void testSerialize_Xep0234_Example5() {
+			std::string expected = 
+				"<jingle"
+					" action=\"transport-info\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"ex\"/>"
+						/*"<transport"
+							" sid=\"vj3hs98y\""
+							" xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+							"<candidate-used cid=\"hr65dqyd\"/>"
+						"</transport>"*/
+					//"</content>"
+				"</jingle>";
+
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::TransportInfo);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+			
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("ex");
+			payload->addPayload(content);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-8
+		void testSerialize_Xep0234_Example8() {
+			std::string expected = 
+				"<jingle"
+					" action=\"session-info\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<checksum xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+						"<file>"
+							"<hashes xmlns=\"urn:xmpp:hashes:0\">"
+								"<hash algo=\"sha-1\">552da749930852c69ae5d2141d3766b1</hash>"
+							"</hashes>"
+						"</file>"
+					"</checksum>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionInfo);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+			
+			JingleFileTransferHash::ref hash = boost::make_shared<JingleFileTransferHash>();
+			hash->setHash("sha-1", "552da749930852c69ae5d2141d3766b1");
+			
+			payload->addPayload(hash);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+		
+		// http://xmpp.org/extensions/xep-0234.html#example-10
+		void testSerialize_Xep0234_Example10() {
+			std::string expected = 
+				"<jingle"
+					" action=\"session-initiate\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"uj3b2\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"a-file-request\">"
+						"<description"
+							" xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+							"<request>"
+								"<file"
+									" hash=\"552da749930852c69ae5d2141d3766b1\""
+									" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\">"
+									"<range offset=\"270336\"/>"
+								"</file>"
+							"</request>"
+						"</description>"
+						/*"<transport"
+							" mode=\"tcp\""
+							" sid=\"xig361fj\""
+							" xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+							"<candidate"
+								" cid=\"ht567dq\""
+								" host=\"192.169.1.10\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"6539\""
+								" priority=\"8257636\""
+								" type=\"direct\"/>"
+							"<candidate"
+								" cid=\"hr65dqyd\""
+								" host=\"134.102.201.180\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"16453\""
+								" priority=\"7929856\""
+								" type=\"assisted\"/>"
+							"<candidate"
+								" cid=\"grt654q2\""
+								" host=\"2001:638:708:30c9:219:d1ff:fea4:a17d\""
+								" jid=\"juliet@capulet.lit/balcony\""
+								" port=\"6539\""
+								" priority=\"8257606\""
+								" type=\"direct\"/>"
+						"</transport>"*/
+					"</content>"
+				"</jingle>";
+			
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionInitiate);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("uj3b2");
+			
+			StreamInitiationFileInfo fileInfo;
+			fileInfo.setHash("552da749930852c69ae5d2141d3766b1");
+			fileInfo.setRangeOffset(270336);
+			
+			JingleFileTransferDescription::ref desc = boost::make_shared<JingleFileTransferDescription>();
+			desc->addRequest(fileInfo);
+			
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("a-file-request");
+			content->addDescription(desc);
+			
+			payload->addPayload(content);
+			
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+
+		// http://xmpp.org/extensions/xep-0234.html#example-10
+		void testSerialize_Xep0234_Example13() {
+			std::string expected =
+				"<jingle"
+					" action=\"session-info\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<received xmlns=\"urn:xmpp:jingle:apps:file-transfer:3\">"
+						"<file"
+							" hash=\"a749930852c69ae5d2141d3766b1552d\""
+							" xmlns=\"http://jabber.org/protocol/si/profile/file-transfer\"/>"
+					"</received>"
+				"</jingle>";
+
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionInfo);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+
+			JingleFileTransferReceived::ref received = boost::make_shared<JingleFileTransferReceived>();
+
+			StreamInitiationFileInfo fileInfo;
+			fileInfo.setHash("a749930852c69ae5d2141d3766b1552d");
+
+			received->setFileInfo(fileInfo);
+			payload->addPayload(received);
+
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+
+		// http://xmpp.org/extensions/xep-0260.html#example-1
+		void testSerialize_Xep0260_Example1() {
+			std::string expected =
+				"<jingle"
+					" action=\"session-initiate\""
+					" initiator=\"romeo@montague.lit/orchard\""
+					" sid=\"a73sjjvkla37jfea\""
+					" xmlns=\"urn:xmpp:jingle:1\">"
+					"<content creator=\"initiator\" name=\"ex\">"
+						"<transport"
+							" mode=\"tcp\""
+							" sid=\"vj3hs98y\""
+							" xmlns=\"urn:xmpp:jingle:transports:s5b:1\">"
+							"<candidate cid=\"hft54dqy\""
+								" host=\"192.168.4.1\""
+								" jid=\"romeo@montague.lit/orchard\""
+								" port=\"5086\""
+								" priority=\"8257636\""
+								" type=\"direct\"/>"
+							"<candidate cid=\"hutr46fe\""
+								" host=\"24.24.24.1\""
+								" jid=\"romeo@montague.lit/orchard\""
+								" port=\"5087\""
+								" priority=\"8258636\""
+								" type=\"direct\"/>"
+						"</transport>"
+					"</content>"
+				"</jingle>";
+
+			JinglePayload::ref payload = boost::make_shared<JinglePayload>();
+			payload->setAction(JinglePayload::SessionInitiate);
+			payload->setInitiator(JID("romeo@montague.lit/orchard"));
+			payload->setSessionID("a73sjjvkla37jfea");
+
+			JingleContentPayload::ref content = boost::make_shared<JingleContentPayload>();
+			content->setCreator(JingleContentPayload::InitiatorCreator);
+			content->setName("ex");
+
+			JingleS5BTransportPayload::ref transport = boost::make_shared<JingleS5BTransportPayload>();
+			transport->setMode(JingleS5BTransportPayload::TCPMode);
+			transport->setSessionID("vj3hs98y");
+
+			JingleS5BTransportPayload::Candidate candidate1;
+			candidate1.cid = "hft54dqy";
+			candidate1.hostPort = HostAddressPort(HostAddress("192.168.4.1"), 5086);
+			candidate1.jid = JID("romeo@montague.lit/orchard");
+			candidate1.priority = 8257636;
+			candidate1.type = JingleS5BTransportPayload::Candidate::DirectType;
+			transport->addCandidate(candidate1);
+
+			JingleS5BTransportPayload::Candidate candidate2;
+			candidate2.cid = "hutr46fe";
+			candidate2.hostPort = HostAddressPort(HostAddress("24.24.24.1"), 5087);
+			candidate2.jid = JID("romeo@montague.lit/orchard");
+			candidate2.priority = 8258636;
+			candidate2.type = JingleS5BTransportPayload::Candidate::DirectType;
+			transport->addCandidate(candidate2);
+
+			content->addTransport(transport);
+
+			payload->addPayload(content);
+
+			CPPUNIT_ASSERT_EQUAL(expected, createTestling()->serialize(payload));
+		}
+
+	private:
+		FullPayloadSerializerCollection collection;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(JingleSerializersTest);
+
diff --git a/Swiften/StringCodecs/Hexify.cpp b/Swiften/StringCodecs/Hexify.cpp
index 367743c..668079b 100644
--- a/Swiften/StringCodecs/Hexify.cpp
+++ b/Swiften/StringCodecs/Hexify.cpp
@@ -31,4 +31,22 @@ std::string Hexify::hexify(const ByteArray& data) {
 	return std::string(result.str());
 }
 
+ByteArray Hexify::unhexify(const std::string& hexstring) {
+	if (hexstring.size() % 2) {
+		return ByteArray();
+	}
+	ByteArray result = ByteArray(hexstring.size() / 2);
+	for (size_t pos = 0; pos < hexstring.size() - 1; pos += 2) {
+		char c;
+		c = hexstring[pos];
+		int a = (c>='0'&&c<='9') ? c-'0' : (c>='A'&&c<='Z') ? c-'A' + 10 : (c>='a'&&c<='z') ? c-'a' + 10 : -1;
+		c = hexstring[pos+1];
+		int b = (c>='0'&&c<='9') ? c-'0' : (c>='A'&&c<='Z') ? c-'A' + 10 : (c>='a'&&c<='z') ? c-'a' + 10 : -1;
+		if (a == -1 || b == -1) return ByteArray(); // fail
+		result[pos/2] = (a<<4) | b;
+
+	}
+	return result;
+}
+
 }
diff --git a/Swiften/StringCodecs/Hexify.h b/Swiften/StringCodecs/Hexify.h
index 9815e21..c016448 100644
--- a/Swiften/StringCodecs/Hexify.h
+++ b/Swiften/StringCodecs/Hexify.h
@@ -13,5 +13,6 @@ namespace Swift {
 		public:
 			static std::string hexify(unsigned char byte);
 			static std::string hexify(const ByteArray& data);
+			static ByteArray unhexify(const std::string& hexstring);
 	};
 }
diff --git a/Swiften/StringCodecs/MD5.cpp b/Swiften/StringCodecs/MD5.cpp
index 0d36254..6871f79 100644
--- a/Swiften/StringCodecs/MD5.cpp
+++ b/Swiften/StringCodecs/MD5.cpp
@@ -366,6 +366,27 @@ namespace {
 	}
 }
 
+MD5::MD5() {
+	state = new md5_state_t;
+	md5_init(state);
+}
+
+MD5::~MD5() {
+	delete state;
+}
+
+MD5& MD5::update(const std::vector<unsigned char>& input) {
+	md5_append(state, reinterpret_cast<const md5_byte_t*>(vecptr(input)), input.size());
+	return *this;
+}
+
+std::vector<unsigned char> MD5::getHash() {
+	ByteArray digest;
+	digest.resize(16);
+	md5_finish(state, reinterpret_cast<md5_byte_t*>(vecptr(digest)));
+	return digest;
+}
+
 ByteArray MD5::getHash(const ByteArray& data) {
 	return getMD5Hash(data);
 }
diff --git a/Swiften/StringCodecs/MD5.h b/Swiften/StringCodecs/MD5.h
index b1d610c..09473c2 100644
--- a/Swiften/StringCodecs/MD5.h
+++ b/Swiften/StringCodecs/MD5.h
@@ -10,9 +10,20 @@
 #include <Swiften/Base/SafeByteArray.h>
 
 namespace Swift {
+	struct md5_state_s;
+
 	class MD5 {
 		public:
+			MD5();
+			~MD5();
+
+			MD5& update(const std::vector<unsigned char>& data);
+			std::vector<unsigned char> getHash();
+
 			static ByteArray getHash(const ByteArray& data);
 			static ByteArray getHash(const SafeByteArray& data);
+
+		private:
+			md5_state_s* state;
 	};
 }
diff --git a/Swiften/StringCodecs/UnitTest/HexifyTest.cpp b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
index 9cbd0d4..38233f9 100644
--- a/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
+++ b/Swiften/StringCodecs/UnitTest/HexifyTest.cpp
@@ -17,6 +17,7 @@ class HexifyTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(HexifyTest);
 		CPPUNIT_TEST(testHexify);
 		CPPUNIT_TEST(testHexify_Byte);
+		CPPUNIT_TEST(testUnhexify);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -27,6 +28,11 @@ class HexifyTest : public CppUnit::TestFixture {
 		void testHexify_Byte() {
 			CPPUNIT_ASSERT_EQUAL(std::string("b2"), Hexify::hexify(0xb2));
 		}
+
+		void testUnhexify() {
+			CPPUNIT_ASSERT_EQUAL(std::string("ffaf02"), Hexify::hexify(Hexify::unhexify("ffaf02")));
+			CPPUNIT_ASSERT(createByteArray("\x01\x23\xf2", 3) == Hexify::unhexify("0123f2"));
+		}
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(HexifyTest);
diff --git a/Swiften/StringCodecs/UnitTest/MD5Test.cpp b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
index ce7e422..c62c46a 100644
--- a/Swiften/StringCodecs/UnitTest/MD5Test.cpp
+++ b/Swiften/StringCodecs/UnitTest/MD5Test.cpp
@@ -19,6 +19,7 @@ class MD5Test : public CppUnit::TestFixture {
 		CPPUNIT_TEST_SUITE(MD5Test);
 		CPPUNIT_TEST(testGetHash_Empty);
 		CPPUNIT_TEST(testGetHash_Alphabet);
+		CPPUNIT_TEST(testIncrementalTest);
 		CPPUNIT_TEST_SUITE_END();
 
 	public:
@@ -33,6 +34,16 @@ class MD5Test : public CppUnit::TestFixture {
 
 			CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
 		}
+
+		void testIncrementalTest() {
+			MD5 testling;
+			testling.update(createByteArray("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+			testling.update(createByteArray("abcdefghijklmnopqrstuvwxyz0123456789"));
+
+			ByteArray result = testling.getHash();
+
+			CPPUNIT_ASSERT_EQUAL(createByteArray("\xd1\x74\xab\x98\xd2\x77\xd9\xf5\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f", 16), result);
+		}
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(MD5Test);
-- 
cgit v0.10.2-6-g49f6