--- a/libupnpp/device/device.cxx
+++ b/libupnpp/device/device.cxx
@@ -29,8 +29,10 @@
 #include <algorithm>
 #include <mutex>
 #include <condition_variable>
+#include <thread>
 
 #include "libupnpp/log.hxx"
+#include "libupnpp/smallut.h"
 #include "libupnpp/ixmlwrap.hxx"
 #include "libupnpp/upnpplib.hxx"
 #include "libupnpp/upnpputils.hxx"
@@ -40,10 +42,40 @@
 using namespace std;
 using namespace UPnPP;
 
+#if UPNP_VERSION_MINOR < 8 && !defined(UpnpEvent_get_SID_cstr)
+typedef struct Upnp_Event UpnpEvent;
+#define UpnpEvent_get_SID_cstr(x) ((x)->Sid)
+#define UpnpEvent_get_EventKey(x) ((x)->EventKey)
+#define UpnpEvent_get_ChangedVariables(x) ((x)->ChangedVariables)
+typedef struct Upnp_Action_Request UpnpActionRequest;
+#define UpnpActionRequest_get_ErrCode(x) ((x)->ErrCode)
+#define UpnpActionRequest_get_ActionName_cstr(x) ((x)->ActionName)
+#define UpnpActionRequest_get_DevUDN_cstr(x) ((x)->DevUDN)
+#define UpnpActionRequest_get_ActionRequest(x) ((x)->ActionRequest)
+#define UpnpActionRequest_get_ActionResult(x) ((x)->ActionResult)
+typedef struct Upnp_State_Var_Request UpnpStateVarRequest;
+#define UpnpStateVarRequest_get_DevUDN_cstr(x) ((x)->DevUDN)
+#define UpnpStateVarRequest_get_ServiceID_cstr(x) ((x)->ServiceID)
+#define UpnpStateVarRequest_get_StateVarName_cstr(x) ((x)->StateVarName)
+typedef struct Upnp_Subscription_Request UpnpSubscriptionRequest;
+#define UpnpSubscriptionRequest_get_ServiceId_cstr(x) ((x)->ServiceId)
+#define UpnpSubscriptionRequest_get_UDN_cstr(x) ((x)->UDN)
+#define UpnpSubscriptionRequest_get_SID_cstr(x) ((x)->Sid)
+#endif
+
+#if UPNP_VERSION_MAJOR > 1 || (UPNP_VERSION_MAJOR==1 && UPNP_VERSION_MINOR >= 8)
+#define CBCONST const
+#else
+#define CBCONST 
+#endif
+
 namespace UPnPProvider {
 
 class UpnpDevice::Internal {
 public:
+    Internal(UpnpDevice *dev)
+        : me(dev) {}
+    
     /* Generate event: called by the device event loop, which polls
      * the services. */
     void notifyEvent(const std::string& serviceId,
@@ -55,21 +87,44 @@
     findService(const std::string& serviceid);
 
     /* Per-device callback */
-    int callBack(Upnp_EventType et, void* evp);
-
-    UPnPP::LibUPnP *lib;
-    std::string deviceId;
-    std::string description;
-
+    int callBack(Upnp_EventType et, const void* evp);
+
+    UpnpDevice *me{nullptr};
+    UpnpDevice *rootdev{nullptr};
+    UPnPP::LibUPnP *lib{nullptr};
+    string deviceId;
+
+    // In case startloop has been called: the event loop thread.
+    std::thread loopthread;
+
+    // Subdirectory of the virtual file tree where we store the XML files
+    string devsubd;
+
+    struct DevXML {
+        // Device properties XML fragment: friendlyName, deviceType
+        // must be in there UDN must not because we add it. Others can
+        // be added: modelName, serialNumber, manufacturer etc.
+        string propsxml;
+        // Description xml service list. Incrementally built as the
+        // services are added
+        string servicexml;
+    };
+    // This object description XML
+    DevXML myxml;
+    
+    // In case there are embedded devices, here is where they store their XML
+    // fragments. The key is the embedded device UDN/aka deviceId
+    map<string, DevXML> embedxml;
+    
     // We keep the services in a map for easy access from id and in a
     // vector for ordered walking while fetching status. Order is
     // determine by addService() call sequence.
     std::unordered_map<std::string, UpnpService*> servicemap;
     std::vector<std::string> serviceids;
     std::unordered_map<std::string, soapfun> calls;
-    bool needExit;
-    /* My device handle */
-    UpnpDevice_Handle dvh;
+    bool needExit{false};
+    /* My libupnp device handle */
+    UpnpDevice_Handle dvh{0};
     /* Lock for device operations. Held during a service callback
        Must not be held when using dvh to call into libupnp */
     std::mutex devlock;
@@ -80,9 +135,9 @@
 class UpnpDevice::InternalStatic {
 public:
     /* Static callback for libupnp. This looks up the appropriate
-     * device using the device ID (UDN), the calls its callback
+     * device using the device ID (UDN), then calls its callback
      * method */
-    static int sCallBack(Upnp_EventType et, void* evp, void*);
+    static int sCallBack(Upnp_EventType et, CBCONST void* evp, void*);
 
     /** Static array of devices for dispatching */
     static std::unordered_map<std::string, UpnpDevice *> devices;
@@ -120,20 +175,23 @@
 
 static const int expiretime = 3600;
 
-UpnpDevice::UpnpDevice(const string& deviceId,
-                       const std::unordered_map<string, VDirContent>& files)
+// Stuff we transform in the uuids when we use them in urls
+static string replchars{"\"#%;<>?[\\]^`{|}:/ "};
+
+UpnpDevice::UpnpDevice(const string& deviceId)
 {
     if (o == 0 && (o = new InternalStatic()) == 0) {
         LOGERR("UpnpDevice::UpnpDevice: out of memory" << endl);
         return;
     }
-    if ((m = new Internal()) == 0) {
+    if ((m = new Internal(this)) == 0) {
         LOGERR("UpnpDevice::UpnpDevice: out of memory" << endl);
         return;
     }
     m->deviceId = deviceId;
-    m->needExit = false;
-    //LOGDEB("UpnpDevice::UpnpDevice(" << m->deviceId << ")" << endl);
+    m->devsubd = string("/") + neutchars(deviceId, replchars, '-') +
+        string("/");
+    //LOGDEB("UpnpDevice::UpnpDevice(" << deviceId << ")" << endl);
 
     m->lib = LibUPnP::getLibUPnP(true);
     if (!m->lib) {
@@ -160,47 +218,24 @@
         }
         o->devices[m->deviceId] = this;
     }
-
-    if (!files.empty()) {
-        // files will be empty for embedded devices, and hence
-        // description too (we test description emptyness to
-        // discriminate root/embedded in other places).
-        VirtualDir* theVD = VirtualDir::getVirtualDir();
-        if (theVD == 0) {
-            LOGFAT("UpnpDevice::UpnpDevice: can't get VirtualDir" << endl);
-            return;
-        }
-
-        for (const auto& it : files) {
-            if (!path_getsimple(it.first).compare("description.xml")) {
-                m->description = it.second.content;
-                break;
-            }
-        }
-        if (m->description.empty()) {
-            LOGFAT("UpnpDevice::UpnpDevice: no description.xml in xmlfiles"
-                   << endl);
-            return;
-        }
-
-        for (const auto& it : files) {
-            string dir = path_getfather(it.first);
-            string fn = path_getsimple(it.first);
-            // description.xml will be served by libupnp from / after
-            // inserting the URLBase element (which it knows how to
-            // compute), so we make sure not to serve our version from
-            // the virtual dir (if it is in /, it would override
-            // libupnp's).
-            if (fn.compare("description.xml")) {
-                theVD->addFile(dir, fn, it.second.content, it.second.mimetype);
-            }
-        }
+}
+
+UpnpDevice::UpnpDevice(UpnpDevice* rootdev, const string& deviceId)
+    : UpnpDevice(deviceId)
+{
+    if (nullptr != rootdev) {
+        m->rootdev = rootdev;
+        rootdev->m->embedxml[deviceId] = UpnpDevice::Internal::DevXML();
     }
 }
 
 UpnpDevice::~UpnpDevice()
 {
-    if (!m->description.empty()) {
+    shouldExit();
+    if (m->loopthread.joinable())
+        m->loopthread.join();
+
+    if (nullptr != m->rootdev) {
         UpnpUnRegisterRootDevice(m->dvh);
     }
 
@@ -210,7 +245,12 @@
         o->devices.erase(it);
 }
 
-bool UpnpDevice::ipv4(string *host, unsigned short *port) const
+const string& UpnpDevice::getDeviceId() const
+{
+    return m->deviceId;
+}
+
+bool UpnpDevice::ipv4(string *host, unsigned short *port)
 {
     char *hst = UpnpGetServerIpAddress();
     if (hst == 0) {
@@ -225,31 +265,100 @@
     return true;
 }
 
+static string ipv4tostrurl(const string& host, unsigned short port)
+{
+    return string("http://") + host + ":" + SoapHelp::i2s(port);
+}
+
+// Calls registerRootDevice() to register us with the lib and start up
+// the web server for sending out description files.
 bool UpnpDevice::Internal::start()
 {
-    // Start up the web server for sending out description files. This also
-    // calls registerRootDevice()
+    // Nothing to do if I am an embedded device: advertisement is
+    // handled by my root device
+    if (nullptr != rootdev) {
+        return true;
+    }
+    
+    // Finish building the device description XML file, and add it to
+    // the virtual directory.
+    string descxml(
+        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+        "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+        "  <specVersion>\n"
+        "    <major>1</major>\n"
+        "    <minor>1</minor>\n"
+        "  </specVersion>\n"
+        "  <device>\n"
+        );
+    descxml += myxml.propsxml;
+    descxml += "    <UDN>" + deviceId + "</UDN>\n";
+    descxml += "    <serviceList>\n";
+    descxml += myxml.servicexml;
+    descxml += "    </serviceList>\n";
+    if (!embedxml.empty()) {
+        descxml += "    <devicelist>\n";
+        for  (const auto& entry : embedxml) {
+            descxml += "      <device>\n";
+            descxml += entry.second.propsxml;
+            descxml += "        <UDN>" + entry.first + "</UDN>\n";
+            descxml += "        <serviceList>\n";
+            descxml += entry.second.servicexml;
+            descxml += "        </serviceList>\n";
+            descxml += "      </device>\n";
+        }
+        descxml += "</devicelist>\n";
+    }
+        
+    descxml += "  </device>\n</root>\n";
+
+    VirtualDir* theVD = VirtualDir::getVirtualDir();
+    if (nullptr != theVD) {
+        theVD->addFile(devsubd, "description.xml", descxml, "application/xml");
+    } else {
+        LOGERR("UpnpDevice: can't start: no VirtualDir??\n");
+        return false;
+    }
+
+    // Tell the pupnp lib to serve the description.
+    string host(UpnpGetServerIpAddress());
+    unsigned short port = UpnpGetServerPort();
     int ret;
-    if (!description.empty()) {
-        // Description is empty for embedded devices
-        if ((ret = lib->setupWebServer(description, &dvh)) != 0) {
-            LOGFAT("UpnpDevice: libupnp can't start service. Err " <<
-                   ret << endl);
+    string url = ipv4tostrurl(host, port) + devsubd + "description.xml";
+    if ((ret = lib->setupWebServer(url, &dvh)) != 0) {
+        LOGERR("UpnpDevice: libupnp can't start service. Err " << ret <<
+               endl);
+        return false;
+    }
+
+    if ((ret = UpnpSendAdvertisement(dvh, expiretime)) != 0) {
+        LOGERR("UpnpDevice::Internal::start(): sendAvertisement failed: " <<
+               lib->errAsString("UpnpDevice: UpnpSendAdvertisement", ret) <<
+               endl);
+        return false;
+    }
+    return true;
+}
+
+
+bool UpnpDevice::addVFile(const string& name, const string& contents,
+                          const std::string& mime, string& path)
+{
+    VirtualDir* theVD = VirtualDir::getVirtualDir();
+    if (theVD) {
+        if (!theVD->addFile(m->devsubd, name, contents, mime)) {
             return false;
         }
-    }
-
-    if ((ret = UpnpSendAdvertisement(dvh, expiretime)) != 0) {
-        LOGERR(lib->errAsString("UpnpDevice: UpnpSendAdvertisement", ret)
-               << endl);
+        path = m->devsubd + name;
+        return true;
+    } else {
         return false;
     }
-    return true;
 }
 
     
 // Main libupnp callback: use the device id and call the right device
-int UpnpDevice::InternalStatic::sCallBack(Upnp_EventType et, void* evp,
+int UpnpDevice::InternalStatic::sCallBack(Upnp_EventType et, CBCONST void* evp,
         void*)
 {
     //LOGDEB("UpnpDevice::sCallBack" << endl);
@@ -257,15 +366,16 @@
     string deviceid;
     switch (et) {
     case UPNP_CONTROL_ACTION_REQUEST:
-        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
+        deviceid = UpnpActionRequest_get_DevUDN_cstr((UpnpActionRequest *)evp);
         break;
 
     case UPNP_CONTROL_GET_VAR_REQUEST:
-        deviceid = ((struct Upnp_State_Var_Request *)evp)->DevUDN;
+        deviceid=UpnpStateVarRequest_get_DevUDN_cstr((UpnpStateVarRequest *)evp);
         break;
 
     case UPNP_EVENT_SUBSCRIPTION_REQUEST:
-        deviceid = ((struct  Upnp_Subscription_Request*)evp)->UDN;
+        deviceid =
+            UpnpSubscriptionRequest_get_UDN_cstr((UpnpSubscriptionRequest*)evp);
         break;
 
     default:
@@ -308,36 +418,38 @@
     return servit;
 }
 
-int UpnpDevice::Internal::callBack(Upnp_EventType et, void* evp)
+int UpnpDevice::Internal::callBack(Upnp_EventType et, const void* evp)
 {
     switch (et) {
     case UPNP_CONTROL_ACTION_REQUEST:
     {
-        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
-
-        LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
-               ". Params: " << ixmlwPrintDoc(act->ActionRequest) << endl);
+        UpnpActionRequest *act = (UpnpActionRequest *)evp;
+
+        LOGDEB("UPNP_CONTROL_ACTION_REQUEST: " <<
+               UpnpActionRequest_get_ActionName_cstr(act) << ". Params: " <<
+               ixmlwPrintDoc(UpnpActionRequest_get_ActionRequest(act)) << endl);
 
         std::unordered_map<string, UpnpService*>::const_iterator servit =
-            findService(act->ServiceID);
+            findService(UpnpActionRequest_get_ServiceID_cstr(act));
         if (servit == servicemap.end()) {
             return UPNP_E_INVALID_PARAM;
         }
 
-        SoapOutgoing dt(servit->second->getServiceType(), act->ActionName);
+        string actname{UpnpActionRequest_get_ActionName_cstr(act)};
+        SoapOutgoing dt(servit->second->getServiceType(), actname);
         {
             std::unique_lock<std::mutex> lock(devlock);
 
             std::unordered_map<std::string, soapfun>::iterator callit =
-                calls.find(string(act->ActionName) + string(act->ServiceID));
+                calls.find(actname + UpnpActionRequest_get_ServiceID_cstr(act));
             if (callit == calls.end()) {
-                LOGINF("UpnpDevice: No such action: " <<
-                       act->ActionName << endl);
+                LOGINF("UpnpDevice: No such action: " << actname << endl);
                 return UPNP_E_INVALID_PARAM;
             }
 
             SoapIncoming sc;
-            if (!sc.decode(act->ActionName, act->ActionRequest)) {
+            if (!sc.decode(actname.c_str(),
+                           UpnpActionRequest_get_ActionRequest(act))) {
                 LOGERR("Error decoding Action call arguments" << endl);
                 return UPNP_E_INVALID_PARAM;
             }
@@ -346,10 +458,16 @@
             int ret = callit->second(sc, dt);
             if (ret != UPNP_E_SUCCESS) {
                 if (ret > 0) {
+#if UPNP_VERSION_MINOR < 8
                     act->ErrCode = ret;
-                    strncpy(act->ErrStr, servit->second->errString(ret).c_str(),
-                            LINE_SIZE-1);
+                    strncpy(act->ErrStr,
+                            servit->second->errString(ret).c_str(), LINE_SIZE-1);
                     act->ErrStr[LINE_SIZE-1] = 0;
+#else
+                    UpnpActionRequest_set_ErrCode(act, ret);
+                    UpnpActionRequest_strcpy_ErrStr(
+                        act, servit->second->errString(ret).c_str());
+#endif
                 }
                 LOGERR("UpnpDevice: Action failed: " << sc.getName() <<
                        " code " << ret << endl);
@@ -358,9 +476,15 @@
         }
 
         // Encode result data
+#if UPNP_VERSION_MINOR < 8
+        act->ErrCode = UPNP_E_SUCCESS;
         act->ActionResult = dt.buildSoapBody();
-
-        //LOGDEB("Response data: " << ixmlwPrintDoc(act->ActionResult) << endl);
+#else
+        UpnpActionRequest_set_ErrCode(act, UPNP_E_SUCCESS);
+        UpnpActionRequest_set_ActionResult(act, dt.buildSoapBody());
+#endif
+        LOGDEB1("Response data: " <<
+                ixmlwPrintDoc(UpnpActionRequest_get_ActionResult(act)) << endl);
 
         return UPNP_E_SUCCESS;
     }
@@ -370,19 +494,19 @@
         // Note that the "Control: query for variable" action is
         // deprecated (upnp arch v1), and we should never get these.
     {
-        struct Upnp_State_Var_Request *act =
-            (struct Upnp_State_Var_Request *)evp;
-        LOGDEB("UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl);
+        UpnpStateVarRequest *act = (UpnpStateVarRequest *)evp;
+        LOGDEB("UPNP_CONTROL_GET_VAR__REQUEST?: " <<
+               UpnpStateVarRequest_get_StateVarName_cstr(act) << endl);
     }
     break;
 
     case UPNP_EVENT_SUBSCRIPTION_REQUEST:
     {
-        struct Upnp_Subscription_Request *act =
-            (struct  Upnp_Subscription_Request*)evp;
-        LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl);
-
-        auto servit = findService(act->ServiceId);
+        UpnpSubscriptionRequest *act = (UpnpSubscriptionRequest*)evp;
+        LOGDEB("UPNP_EVENT_SUBSCRIPTION_REQUEST: " <<
+               UpnpSubscriptionRequest_get_ServiceId_cstr(act) << endl);
+
+        auto servit=findService(UpnpSubscriptionRequest_get_ServiceId_cstr(act));
         if (servit == servicemap.end()) {
             return UPNP_E_INVALID_PARAM;
         }
@@ -397,10 +521,11 @@
 
         vector<const char *> cnames, cvalues;
         vectorstoargslists(names, values, qvalues, cnames, cvalues);
-        int ret =
-            UpnpAcceptSubscription(dvh, act->UDN, act->ServiceId,
-                                   &cnames[0], &cvalues[0],
-                                   int(cnames.size()), act->Sid);
+        int ret = UpnpAcceptSubscription(
+                dvh, UpnpSubscriptionRequest_get_UDN_cstr(act),
+                UpnpSubscriptionRequest_get_ServiceId_cstr(act),
+                &cnames[0], &cvalues[0], int(cnames.size()),
+                UpnpSubscriptionRequest_get_SID_cstr(act));
         if (ret != UPNP_E_SUCCESS) {
             LOGERR(lib->errAsString(
                        "UpnpDevice::callBack: UpnpAcceptSubscription", ret) <<
@@ -419,17 +544,65 @@
     return UPNP_E_INVALID_PARAM;
 }
 
-void UpnpDevice::addService(UpnpService *serv, const std::string& serviceId)
-{
+bool UpnpDevice::addService(UpnpService *serv)
+{
+    const string& serviceId = serv->getServiceId();
     LOGDEB("UpnpDevice::addService: [" << serviceId << "]\n");
     std::unique_lock<std::mutex> lock(m->devlock);
 
+    string* propsxml;
+    string* servicexml;
+    if (nullptr == m->rootdev) {
+        propsxml = &m->myxml.propsxml;
+        servicexml = &m->myxml.servicexml;
+    } else {
+        auto it = m->rootdev->m->embedxml.find(m->deviceId);
+        if (it == m->rootdev->m->embedxml.end()) {
+            LOGERR("UpnpDevice::addservice: my Id " << m->deviceId <<
+                   " not found in root dev " << m->rootdev->m->deviceId << endl);
+            return false;
+        }
+        propsxml = &it->second.propsxml;
+        servicexml = &it->second.servicexml;
+    }
+
+    if (propsxml->empty()) {
+        // Retrieve the XML fragment with friendlyname etc.
+        if (!readLibFile("", *propsxml)) {
+            LOGERR("UpnpDevice::start: Could not read description XML props\n");
+            return false;
+        }
+    }
+    
     m->servicemap[serviceId] = serv;
     vector<string>::iterator it =
         std::find(m->serviceids.begin(), m->serviceids.end(), serviceId);
     if (it != m->serviceids.end())
         m->serviceids.erase(it);
     m->serviceids.push_back(serviceId);
+
+    string servnick = neutchars(serv->getServiceType(), replchars, '-');
+
+    // Add the service description to the virtual directory
+    string scpdxml;
+    string xmlfn = serv->getXMLFn();
+    VirtualDir* theVD = VirtualDir::getVirtualDir();
+    if (!readLibFile(xmlfn, scpdxml)) {
+        LOGERR("UpnpDevice::addService: could not retrieve service definition"
+               "file: nm: [" << xmlfn << "]\n");
+    } else {
+        theVD->addFile(m->devsubd, servnick + ".xml",
+                       scpdxml, "application/xml");
+    }
+    *servicexml +=
+        string("<service>\n") +
+        " <serviceType>" + serv->getServiceType() + "</serviceType>\n" +
+        " <serviceId>"   + serv->getServiceId() + "</serviceId>\n" +
+        " <SCPDURL>"     + m->devsubd + servnick + ".xml</SCPDURL>\n" +
+        " <controlURL>"  + m->devsubd + "ctl-" + servnick + "</controlURL>\n" +
+        " <eventSubURL>" + m->devsubd + "evt-" + servnick + "</eventSubURL>\n" +
+        "</service>\n";
+    return true;
 }
 
 void UpnpDevice::forgetService(const std::string& serviceId)
@@ -461,8 +634,9 @@
                                        const vector<string>& names,
                                        const vector<string>& values)
 {
-//    LOGDEB1("UpnpDevice::notifyEvent " << serviceId << " " <<
-//           (names.empty() ? "Empty names??" : names[0]) << endl);
+    LOGDEB1("UpnpDevice::notifyEvent: deviceId " << deviceId << " serviceId " <<
+            serviceId << " nm[0] " << (names.empty()?"Empty names??" : names[0])
+            << endl);
     stringstream ss;
     for (unsigned int i = 0; i < names.size() && i < values.size(); i++) {
         ss << names[i] << "=" << values[i] << " ";
@@ -479,15 +653,21 @@
                          serviceId.c_str(), &cnames[0], &cvalues[0],
                          int(cnames.size()));
     if (ret != UPNP_E_SUCCESS) {
-        LOGERR(lib->errAsString("UpnpDevice::notifyEvent: id", ret) <<
+        LOGERR("UpnpDevice::notifyEvent: " << lib->errAsString("UpnpNotify", ret) <<
                " for " << serviceId << endl);
     }
 }
 
-int timespec_diffms(const struct timespec& old, const struct timespec& recent)
+static int timespec_diffms(const struct timespec& old,
+                           const struct timespec& recent)
 {
     return (recent.tv_sec - old.tv_sec) * 1000 +
            (recent.tv_nsec - old.tv_nsec) / (1000 * 1000);
+}
+
+void UpnpDevice::startloop()
+{
+    m->loopthread = std::thread(&UpnpDevice::eventloop, this);
 }
 
 // Loop on services, and poll each for changed data. Generate event
@@ -618,24 +798,23 @@
     Internal(bool noevs)
         : noevents(noevs), dev(0) {
     }
+    string serviceType;
+    string serviceId;
+    string xmlfn;
     bool noevents;
     UpnpDevice *dev;
 };
 
-UpnpService::UpnpService(const std::string& stp,
-                         const std::string& sid, UpnpDevice *dev)
-    : m_serviceType(stp), m_serviceId(sid), m(new Internal(false))
+UpnpService::UpnpService(
+    const std::string& stp, const std::string& sid, const std::string& xmlfn,
+    UpnpDevice *dev, bool noevs)
+    : m(new Internal(noevs))
 {
     m->dev = dev;
-    dev->addService(this, sid);
-}
-
-UpnpService::UpnpService(const std::string& stp,
-                         const std::string& sid, UpnpDevice *dev, bool noevs)
-    : m_serviceType(stp), m_serviceId(sid), m(new Internal(noevs))
-{
-    m->dev = dev;
-    dev->addService(this, sid);
+    m->serviceType = stp;
+    m->serviceId = sid;
+    m->xmlfn = xmlfn;
+    dev->addService(this);
 }
 
 UpnpDevice *UpnpService::getDevice()
@@ -651,7 +830,7 @@
 {
     if (m) {
         if (m->dev)
-            m->dev->forgetService(m_serviceId);
+            m->dev->forgetService(m->serviceId);
         delete m;
         m = 0;
     }
@@ -670,12 +849,17 @@
 
 const std::string& UpnpService::getServiceType() const
 {
-    return m_serviceType;
+    return m->serviceType;
 }
 
 const std::string& UpnpService::getServiceId() const
 {
-    return m_serviceId;
+    return m->serviceId;
+}
+
+const std::string& UpnpService::getXMLFn() const
+{
+    return m->xmlfn;
 }
 
 const std::string UpnpService::errString(int error) const