--- a/libupnpp/soaphelp.cxx
+++ b/libupnpp/soaphelp.cxx
@@ -19,7 +19,9 @@
 #include <iostream>
 using namespace std;
 
-#include "soaphelp.hxx"
+#include "libupnpp/log.hxx"
+#include "libupnpp/upnpp_p.hxx"
+#include "libupnpp/soaphelp.hxx"
 
 
 /* Example Soap XML doc passed by libupnp is like: 
@@ -98,18 +100,7 @@
     if (it == args.end() || it->second.empty()) {
         return false;
     }
-    if (it->second[0] == 'F' || it->second[0] == 'f' || 
-        it->second[0] == 'N' || it->second[0] == 'n' || 
-        it->second[0] == '0') {
-        *value = false;
-    } else if (it->second[0] == 'T' || it->second[0] == 't' || 
-               it->second[0] == 'Y' || it->second[0] == 'y' || 
-               it->second[0] == '1') {
-        *value = true;
-    } else {
-        return false;
-    }
-    return true;
+    return stringToBool(it->second, value);
 }
 
 bool SoapDecodeOutput::getInt(const char *nm, int *value) const
@@ -132,7 +123,8 @@
     return true;
 }
 
-string SoapDecodeOutput::xmlQuote(const string& in)
+namespace SoapHelp {
+string xmlQuote(const string& in)
 {
     string out;
     for (unsigned int i = 0; i < in.size(); i++) {
@@ -148,18 +140,51 @@
     return out;
 }
 
+string xmlUnquote(const string& in)
+{
+    string out;
+    for (unsigned int i = 0; i < in.size(); i++) {
+        if (in[i] == '&') {
+            unsigned int j;
+            for (j = i; j < in.size(); j++) {
+                if (in[j] == ';')
+                    break;
+            }
+            if (in[j] != ';') {
+                out += in.substr(i);
+                return out;
+            }
+            string entname = in.substr(i+1, j-i-1);
+            cout << "entname [" << entname << "]" << endl;
+            if (!entname.compare("quot")) {
+                out += '"';
+            } else if (!entname.compare("amp")) {
+                out += '&';
+            } else if (!entname.compare("lt")) {
+                out += '<';
+            } else if (!entname.compare("gt")) {
+                out += '>';
+            } else if (!entname.compare("apos")) {
+                out += '\'';
+            } else {
+                out += in.substr(i, j-i+1);
+            }
+            i = j;
+        } else {
+            out += in[i];
+        }
+    }
+    return out;
+}
+
 // Yes inefficient. whatever...
-string SoapDecodeOutput::i2s(int val)
+string i2s(int val)
 {
     char cbuf[30];
     sprintf(cbuf, "%d", val);
     return string(cbuf);
 }
-string SoapEncodeInput::i2s(int val)
-{
-    char cbuf[30];
-    sprintf(cbuf, "%d", val);
-    return string(cbuf);
+
 }
 
 IXML_Document *buildSoapBody(const SoapEncodeInput& data, bool isResponse)
@@ -192,3 +217,76 @@
     return doc;
 }
 
+// Decoding UPnP Event data. The variable values are contained in a
+// propertyset XML document:
+//     <?xml version="1.0"?>
+//     <e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
+//       <e:property>
+//         <variableName>new value</variableName>
+//       </e:property>
+//       <!-- Other variable names and values (if any) go here. -->
+//     </e:propertyset>
+
+bool decodePropertySet(IXML_Document *doc, 
+                       unordered_map<string,string>& out)
+{
+    bool ret = false;
+    IXML_Node* topNode = ixmlNode_getFirstChild((IXML_Node *)doc);
+    if (topNode == 0) {
+        LOGERR("decodePropertySet: (no topNode) ??" << endl);
+        return false;
+    }
+    //LOGDEB("decodePropertySet: topnode name: " << 
+    //       ixmlNode_getNodeName(topNode) << endl);
+
+    IXML_NodeList* nl = ixmlNode_getChildNodes(topNode);
+    if (nl == 0) {
+        LOGDEB("decodePropertySet: empty list" << endl);
+        return true;
+    }
+    for (unsigned long i = 0; i <  ixmlNodeList_length(nl); i++) {
+        IXML_Node *cld = ixmlNodeList_item(nl, i);
+        if (cld == 0) {
+            LOGDEB("decodePropertySet: got null node  from nlist at index " <<
+                   i << " ??" << endl);
+            // Seems to happen with empty arg list?? This looks like a bug, 
+            // should we not get an empty node instead?
+            if (i == 0) {
+                ret = true;
+            }
+            goto out;
+        }
+        const char *name = ixmlNode_getNodeName(cld);
+        //LOGDEB("decodePropertySet: got node name:     " << 
+        //   ixmlNode_getNodeName(cld) << endl);
+        if (cld == 0) {
+            DOMString pnode = ixmlPrintNode(cld);
+            //LOGDEB("decodePropertySet: got null name ??:" << pnode << endl);
+            ixmlFreeDOMString(pnode);
+            goto out;
+        }
+        IXML_Node *subnode = ixmlNode_getFirstChild(cld);
+        name = ixmlNode_getNodeName(subnode);
+        //LOGDEB("decodePropertySet: got subnode name:         " << 
+        //   name << endl);
+        
+        IXML_Node *txtnode = ixmlNode_getFirstChild(subnode);
+        //LOGDEB("decodePropertySet: got txtnode name:             " << 
+        //   ixmlNode_getNodeName(txtnode) << endl);
+        
+        const char *value = "";
+        if (txtnode != 0) {
+            value = ixmlNode_getNodeValue(txtnode);
+        }
+        // Can we get an empty value here ?
+        if (value == 0)
+            value = "";
+        out[name] = SoapHelp::xmlUnquote(value);
+    }
+
+    ret = true;
+out:
+    if (nl)
+        ixmlNodeList_free(nl);
+    return ret;
+}