Switch to side-by-side view

--- a
+++ b/libupnpp/device.cxx
@@ -0,0 +1,242 @@
+/* Copyright (C) 2014 J.F.Dockes
+ *	 This program is free software; you can redistribute it and/or modify
+ *	 it under the terms of the GNU General Public License as published by
+ *	 the Free Software Foundation; either version 2 of the License, or
+ *	 (at your option) any later version.
+ *
+ *	 This program is distributed in the hope that it will be useful,
+ *	 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	 GNU General Public License for more details.
+ *
+ *	 You should have received a copy of the GNU General Public License
+ *	 along with this program; if not, write to the
+ *	 Free Software Foundation, Inc.,
+ *	 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <iostream>
+using namespace std;
+
+#include "upnpplib.hxx"
+#include "device.hxx"
+
+unordered_map<std::string, UpnpDevice *> UpnpDevice::o_devices;
+
+UpnpDevice::UpnpDevice(const string& deviceId)
+    : m_deviceId(deviceId)
+{
+    cerr << "UpnpDevice::UpnpDevice(" << m_deviceId << ")" << endl;
+
+    m_lib = LibUPnP::getLibUPnP(true);
+    if (!m_lib) {
+        cerr << " Can't get LibUPnP" << endl;
+        return;
+    }
+    if (!m_lib->ok()) {
+        cerr << "Lib init failed: " <<
+            m_lib->errAsString("main", m_lib->getInitError()) << endl;
+        m_lib = 0;
+        return;
+    }
+
+    if (o_devices.empty()) {
+        // First call: init callbacks
+        m_lib->registerHandler(UPNP_CONTROL_ACTION_REQUEST, sCallBack, this);
+	m_lib->registerHandler(UPNP_CONTROL_GET_VAR_REQUEST, sCallBack, this);
+	m_lib->registerHandler(UPNP_EVENT_SUBSCRIPTION_REQUEST, sCallBack,this);
+    }
+
+    cerr << "UpnpDevice::UpnpDevice: adding myself[" << this << "] to dev table" << endl;
+    o_devices[m_deviceId] = this;
+}
+
+int UpnpDevice::sCallBack(Upnp_EventType et, void* evp, void* tok)
+{
+    cerr << "UpnpDevice::sCallBack" << endl;
+
+    string deviceid;
+    switch (et) {
+    case UPNP_CONTROL_ACTION_REQUEST:
+        deviceid = ((struct Upnp_Action_Request *)evp)->DevUDN;
+    break;
+
+    case UPNP_CONTROL_GET_VAR_REQUEST:
+        deviceid = ((struct Upnp_State_Var_Request *)evp)->DevUDN;
+    break;
+
+    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+        deviceid = ((struct  Upnp_Subscription_Request*)evp)->UDN;
+    break;
+
+    default:
+        cerr << "UpnpDevice::sCallBack: unknown event " << et << endl;
+        return UPNP_E_INVALID_PARAM;
+    }
+    // cerr << "UpnpDevice::sCallBack: deviceid[" << deviceid << "]" << endl;
+
+    unordered_map<std::string, UpnpDevice *> *devmap = 
+        (unordered_map<std::string, UpnpDevice *> *) tok;
+    unordered_map<std::string, UpnpDevice *>::iterator it =
+        o_devices.find(deviceid);
+
+    if (it == o_devices.end()) {
+        cerr << "UpnpDevice::sCallBack: Device not found: [" << 
+            deviceid << "]" << endl;
+        return UPNP_E_INVALID_PARAM;
+    }
+    // cerr << "UpnpDevice::sCallBack: device found: [" << it->second 
+    // << "]" << endl;
+    return (it->second)->callBack(et, evp);
+}
+
+static PTMutexInit cblock;
+int UpnpDevice::callBack(Upnp_EventType et, void* evp)
+{
+    PTMutexLocker lock(cblock);
+    cerr << "UpnpDevice::callBack: evt type:" << 
+        LibUPnP::evTypeAsString(et).c_str() << endl;
+
+    switch (et) {
+    case UPNP_CONTROL_ACTION_REQUEST:
+    {
+        struct Upnp_Action_Request *act = (struct Upnp_Action_Request *)evp;
+        cerr << "UPNP_CONTROL_ACTION_REQUEST: " << act->ActionName <<
+            " Params: " << ixmlPrintDocument(act->ActionRequest) << endl;
+
+        unordered_map<string, string>::const_iterator servit = 
+            m_serviceTypes.find(serviceKey(act->DevUDN,act->ServiceID));
+        if (servit == m_serviceTypes.end()) {
+            cerr << "Bad serviceID" << endl;
+            return UPNP_E_INVALID_PARAM;
+        }
+        const string& servicetype = servit->second;
+
+        unordered_map<string, soapfun>::iterator callit = 
+            m_calls.find(act->ActionName);
+        if (callit == m_calls.end()) {
+            cerr << "No such action: " << act->ActionName << endl;
+            return UPNP_E_INVALID_PARAM;
+        }
+
+        SoapArgs sc;
+        if (!decodeSoapBody(act->ActionName, act->ActionRequest, &sc)) {
+            cerr << "Error decoding Action call arguments" << endl;
+            return UPNP_E_INVALID_PARAM;
+        }
+        SoapData dt;
+        dt.name = act->ActionName;
+        dt.serviceType = servicetype;
+
+        // Call the action routine
+        int ret = callit->second(sc, dt);
+        if (ret != UPNP_E_SUCCESS) {
+            cerr << "Action failed: " << sc.name << endl;
+            return ret;
+        }
+
+        // Encode result data
+        act->ActionResult = buildSoapBody(dt);
+        cerr << "Response data: " << 
+            ixmlPrintDocument(act->ActionResult) << endl;
+
+        return ret;
+
+    }
+    break;
+
+    case UPNP_CONTROL_GET_VAR_REQUEST:
+        // 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;
+        cerr << "UPNP_CONTROL_GET_VAR__REQUEST?: " << act->StateVarName << endl;
+    }
+    break;
+
+    case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+    {
+        struct Upnp_Subscription_Request *act = 
+            (struct  Upnp_Subscription_Request*)evp;
+        cerr << "UPNP_EVENT_SUBSCRIPTION_REQUEST: " << act->ServiceId << endl;
+
+        vector<string> names;
+        vector<string> values;
+        if (!getEventData(names, values)) {
+            break;
+        }
+        if (names.size() != values.size()) {
+            break;
+        }
+
+        vector<const char *>cnames;
+        cnames.reserve(names.size());
+        for (unsigned int i = 0; i < names.size(); i++) {
+            cnames.push_back(names[i].c_str());
+        }
+
+        vector<const char *>cvalues;
+        cvalues.reserve(values.size());
+        for (unsigned int i = 0; i < values.size(); i++) {
+            cvalues.push_back(values[i].c_str());
+        }
+
+        int ret = 
+            UpnpAcceptSubscription(m_lib->getdvh(), act->UDN, act->ServiceId,
+                                   &cnames[0], &cvalues[0],
+                                   int(cnames.size()), act->Sid);
+        if (ret != UPNP_E_SUCCESS) {
+            cerr << "UpnpDevice::callBack: UpnpAcceptSubscription failed: " 
+                 << ret << endl;
+        }
+
+        return ret;
+    }
+    break;
+
+    default:
+        cerr << "UpnpDevice::callBack: unknown op" << endl;
+        return UPNP_E_INVALID_PARAM;
+    }
+    return UPNP_E_INVALID_PARAM;
+}
+
+void UpnpDevice::addServiceType(const std::string& serviceId, 
+                                const std::string& serviceType)
+{
+    cerr << "UpnpDevice::addServiceType: [" << 
+        serviceKey(m_deviceId, serviceId) << "] -> [" << serviceType << endl;
+    m_serviceTypes[serviceKey(m_deviceId, serviceId)] = serviceType;
+}
+
+void UpnpDevice::addActionMapping(const std::string& actName, soapfun fun)
+{
+    cerr << "UpnpDevice::addActionMapping:" << actName << endl;
+    m_calls[actName] = fun;
+}
+
+void UpnpDevice::notifyEvent(const string& serviceId,
+                             const vector<string>& names, 
+                             const vector<string>& values)
+{
+    cerr << "UpnpDevice::notifyEvent" << endl;
+    vector<const char *>cnames;
+    cnames.reserve(names.size());
+    for (unsigned int i = 0; i < names.size(); i++) {
+        cnames.push_back(names[i].c_str());
+    }
+
+    vector<const char *>cvalues;
+    cvalues.reserve(values.size());
+    for (unsigned int i = 0; i < values.size(); i++) {
+        cvalues.push_back(values[i].c_str());
+    }
+
+    int ret = UpnpNotify(m_lib->getdvh(), m_deviceId.c_str(), 
+                         serviceId.c_str(),
+                         &cnames[0], &cvalues[0],
+                         int(cnames.size()));
+    if (ret != UPNP_E_SUCCESS) {
+        cerr << "UpnpDevice::notifyEvent: UpnpNotify failed: " << ret << endl;
+    }
+}