--- a/libupnpp/upnpplib.cxx
+++ b/libupnpp/upnpplib.cxx
@@ -15,6 +15,8 @@
  *	 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include <string.h>
+
 #include <string>
 #include <iostream>
 #include <sstream>
@@ -32,19 +34,23 @@
 
 #include "upnpp_p.hxx"
 #include "upnpplib.hxx"
+extern "C" {
+#include "getsyshwaddr.h"
+}
+#include "md5.hxx"
 
 static LibUPnP *theLib;
 
-LibUPnP *LibUPnP::getLibUPnP()
+LibUPnP *LibUPnP::getLibUPnP(bool server)
 {
 	if (theLib == 0)
-		theLib = new LibUPnP;
+		theLib = new LibUPnP(server);
 	if (theLib && !theLib->ok())
 		return 0;
 	return theLib;
 }
 
-LibUPnP::LibUPnP()
+LibUPnP::LibUPnP(bool server)
 	: m_ok(false)
 {
 	m_init_error = UpnpInit(0, 0);
@@ -53,20 +59,48 @@
 		return;
 	}
 	setMaxContentLength(2000*1024);
+
 #ifdef DEBUG
+	const char *ip_address = UpnpGetServerIpAddress();
+	int port = UpnpGetServerPort();
+	cerr << "Using IP " << ip_address << " port " << port << endl;
 	UpnpCloseLog();
 #endif
-	m_init_error = UpnpRegisterClient(o_callback, (void *)this, &m_clh);
-	if (m_init_error == UPNP_E_SUCCESS) {
-		//	cerr << "Initialized on " << UpnpGetServerIpAddress() <<
-		//		"with cookie = " << this << endl;
+
+	if (server) {
 		m_ok = true;
 	} else {
-		cerr << errAsString("UpnpRegisterClient", m_init_error);
-	}
-
-	// Servers sometimes make error (e.g.: minidlna returns bad utf-8)
+		// Client initialization is simple, just do it. Defer device init
+		// completion because it's more complicated.
+		m_init_error = UpnpRegisterClient(o_callback, (void *)this, &m_clh);
+		
+		if (m_init_error == UPNP_E_SUCCESS) {
+			m_ok = true;
+		} else {
+			cerr << errAsString("UpnpRegisterClient", m_init_error) << endl;
+		}
+	}
+
+	// Servers sometimes make error (e.g.: minidlna returns bad
+	// utf-8). 
 	ixmlRelaxParser(1);
+}
+
+int LibUPnP::setupWebServer(const string& description)
+{
+	//static const string description("/tmp/description.xml");
+	//m_init_error = UpnpSetWebServerRootDir("/tmp/root");
+	int res = UpnpRegisterRootDevice2(
+		UPNPREG_BUF_DESC,
+		description.c_str(), 
+		description.size(), /* Desc filename len, ignored */
+		1, /* URLBase*/
+		o_callback, (void *)this, &m_dvh);
+
+	if (res != UPNP_E_SUCCESS) {
+		cerr << errAsString("UpnpRegisterRootDevice2", m_init_error) << endl;
+	}
+	return res;
 }
 
 void LibUPnP::setMaxContentLength(int bytes)
@@ -139,6 +173,28 @@
 	}
 	PLOGDEB("LibUPnP: done\n");
 }
+
+string LibUPnP::makeDevUUID(const string& name)
+{
+	string digest;
+	MD5String(name, digest);
+	// digest has 16 bytes of binary data. UUID is like:
+	// f81d4fae-7dec-11d0-a765-00a0c91e6bf6
+	// Where the last 12 chars are provided by the hw addr
+
+	char hw[20];
+	if (getsyshwaddr(hw, 13) < 0) {
+		cerr << "LibUPnP::makeDevUUID: failed retrieving hw addr" << endl;
+		strcpy(hw, "2e87682c5ce8");
+	}
+	char uuid[100];
+	sprintf(uuid, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%s",
+			digest[0]&0xff, digest[1]&0xff, digest[2]&0xff, digest[3]&0xff,
+			digest[4]&0xff, digest[5]&0xff,  digest[6]&0xff, digest[7]&0xff,
+			digest[8]&0xff, digest[9]&0xff, hw);
+	return uuid;
+}
+
 
 string LibUPnP::evTypeAsString(Upnp_EventType et)
 {
@@ -222,6 +278,19 @@
 	father.erase(slp);
 	path_catslash(father);
 	return father;
+}
+string path_getsimple(const string &s) {
+    string simple = s;
+
+    if (simple.empty())
+        return simple;
+
+    string::size_type slp = simple.rfind('/');
+    if (slp == string::npos)
+        return simple;
+
+    simple.erase(0, slp+1);
+    return simple;
 }
 
 template <class T> bool csvToStrings(const string &s, T &tokens)