Switch to side-by-side view

--- a/libupnpp/device/vdir.cxx
+++ b/libupnpp/device/vdir.cxx
@@ -18,16 +18,16 @@
 
 #include "vdir.hxx"
 
-#include <string.h>                     // for memcpy
-#include <sys/types.h>                  // for off_t
-#include <upnp/ixml.h>                  // for ixmlCloneDOMString
-#include <upnp/upnp.h>                  // for File_Info, etc
-
-#include <iostream>                     // for endl, basic_ostream, etc
-#include <utility>                      // for pair
-
-#include "libupnpp/log.hxx"             // for LOGERR
-#include "libupnpp/upnpp_p.hxx"         // for path_getfather, etc
+#include <string.h>
+#include <upnp/ixml.h>
+#include <upnp/upnp.h>
+
+#include <iostream>
+#include <utility>
+#include <unordered_map>
+
+#include "libupnpp/log.hxx"
+#include "libupnpp/upnpp_p.hxx"
 
 using namespace std;
 using namespace UPnPP;
@@ -36,40 +36,140 @@
 
 static VirtualDir *theDir;
 
+class FileEnt {
+public:
+    time_t mtime;
+    std::string mimetype;
+    std::string content;
+};
+
+struct DirEnt {
+public:
+    DirEnt(bool vd = false) : isvd(vd) {}
+    bool isvd;
+    unordered_map<string, FileEnt> files;
+    VirtualDir::FileOps ops;
+};
+static unordered_map<string,  DirEnt> m_dirs;
+
+static void pathcatslash(string& path)
+{
+    if (path.empty() || path[path.size() - 1] != '/') {
+        path += '/';
+    }
+}
+
+// Look up entry for pathname
+static FileEnt *vdgetentry(const char *pathname, DirEnt **de)
+{
+    //LOGDEB("vdgetentry: [" << pathname << "]" << endl);
+    *de = nullptr;
+    
+    VirtualDir *thedir = VirtualDir::getVirtualDir();
+    if (thedir == 0) {
+        return 0;
+    }
+
+    string path = path_getfather(pathname);
+    string name= path_getsimple(pathname);
+
+    pathcatslash(path);
+    
+    auto dir = m_dirs.find(path);
+    if (dir == m_dirs.end()) {
+        LOGERR("VirtualDir::vdgetentry: no dir: " << path << endl);
+        return 0;
+    }
+    *de = &dir->second;
+    if (dir->second.isvd) {
+	return 0;
+    } else {
+	auto f = dir->second.files.find(name);
+	if (f == dir->second.files.end()) {
+	    LOGERR("VirtualDir::vdgetentry: no file: " << path << endl);
+	    return 0;
+	}
+	return &(f->second);
+    }
+}
+
+bool VirtualDir::addFile(const string& _path, const string& name,
+                         const string& content, const string& mimetype)
+{
+    string path(_path);
+    pathcatslash(path);
+    
+    //LOGDEB("VirtualDir::addFile: path " << path << " name " << name << endl);
+
+    if (m_dirs.find(path) == m_dirs.end()) {
+	m_dirs[path] = DirEnt();
+        UpnpAddVirtualDir(path.c_str());
+    }
+
+    FileEnt entry;
+    entry.mtime = time(0);
+    entry.mimetype = mimetype;
+    entry.content = content;
+    m_dirs[path].files[name] = entry;
+    // LOGDEB("VirtualDir::addFile: added entry for dir " <<
+    // path << " name " << name << endl);
+    return true;
+}
+
+bool VirtualDir::addVDir(const std::string& _path, FileOps fops)
+{
+    string path(_path);
+    pathcatslash(path);
+    if (m_dirs.find(path) == m_dirs.end()) {
+	m_dirs[path] = DirEnt(true);
+        UpnpAddVirtualDir(path.c_str());
+    }
+    m_dirs[path].ops = fops;
+    return true;
+}
+
+
+//////////
+// Methods for the libupnp interface:
+
 struct Handle {
-    Handle(VirtualDir::FileEnt *e)
-        : entry(e), offset(0) {
-    }
-    VirtualDir::FileEnt *entry;
+    Handle(FileEnt *e, DirEnt *d = nullptr, void *vh = 0)
+        : entry(e), dir(d), vhandle(vh), offset(0) {
+    }
+    FileEnt *entry;
+    DirEnt *dir;
+    void *vhandle;
     size_t offset;
 };
 
 static int vdclose(UpnpWebFileHandle fileHnd)
 {
     Handle *h = (Handle*)fileHnd;
+    if (h->vhandle) {
+	h->dir->ops.close(h->vhandle);
+    }
     delete h;
     return 0;
 }
 
-static VirtualDir::FileEnt *vdgetentry(const char *pathname)
-{
-    //LOGDEB("vdgetentry: [" << pathname << "]" << endl);
-    VirtualDir *thedir = VirtualDir::getVirtualDir();
-    if (thedir == 0) {
-        return 0;
-    }
-
-    string dir = path_getfather(pathname);
-    string fn = path_getsimple(pathname);
-
-    return theDir->getFile(dir, fn);
-}
-
 static int vdgetinfo(const char *fn, struct File_Info* info)
 {
     //LOGDEB("vdgetinfo: [" << fn << "] off_t " << sizeof(off_t) <<
     // " time_t " << sizeof(time_t) << endl);
-    VirtualDir::FileEnt *entry = vdgetentry(fn);
+    DirEnt *dir;
+    FileEnt *entry = vdgetentry(fn, &dir);
+    if (dir && dir->isvd) {
+	VirtualDir::FileInfo inf;
+	int ret = dir->ops.getinfo(fn, &inf);
+	if (ret >= 0) {
+	    info->file_length = inf.file_length;
+	    info->last_modified = inf.last_modified;
+	    info->is_directory = inf.is_directory;
+	    info->is_readable =  inf.is_readable;
+	    info->content_type = ixmlCloneDOMString(inf.mime.c_str());
+	}
+	return ret;
+    }
     if (entry == 0) {
         LOGERR("vdgetinfo: no entry for " << fn << endl);
         return -1;
@@ -87,7 +187,18 @@
 static UpnpWebFileHandle vdopen(const char* fn, enum UpnpOpenFileMode)
 {
     //LOGDEB("vdopen: " << fn << endl);
-    VirtualDir::FileEnt *entry = vdgetentry(fn);
+    DirEnt *dir;
+    FileEnt *entry = vdgetentry(fn, &dir);
+
+    if (dir && dir->isvd) {
+	void *vh = dir->ops.open(fn);
+	if (vh) {
+	    return new Handle(nullptr, dir, vh);
+	} else {
+	    return NULL;
+	}
+    }
+
     if (entry == 0) {
         LOGERR("vdopen: no entry for " << fn << endl);
         return NULL;
@@ -102,6 +213,10 @@
         return 0;
     }
     Handle *h = (Handle *)fileHnd;
+    if (h->vhandle) {
+	return h->dir->ops.read(h->vhandle, buf, buflen);
+    }
+
     if (h->offset >= h->entry->content.size()) {
         return 0;
     }
@@ -116,6 +231,9 @@
 {
     // LOGDEB("vdseek: " << endl);
     Handle *h = (Handle *)fileHnd;
+    if (h->vhandle) {
+	return h->dir->ops.seek(h->vhandle, offset, origin);
+    }
     if (origin == 0) {
         h->offset = offset;
     } else if (origin == 1) {
@@ -152,53 +270,4 @@
     return theDir;
 }
 
-bool VirtualDir::addFile(const string& _path, const string& name,
-                         const string& content, const string& mimetype)
-{
-    string path(_path);
-    if (path.empty() || path[path.size() - 1] != '/') {
-        path += '/';
-    }
-    //LOGDEB("VirtualDir::addFile: path " << path << " name " << name << endl);
-
-    if (m_dirs.find(path) == m_dirs.end()) {
-        m_dirs[path] = std::unordered_map<string, VirtualDir::FileEnt>();
-        UpnpAddVirtualDir(path.c_str());
-    }
-
-    VirtualDir::FileEnt entry;
-    entry.mtime = time(0);
-    entry.mimetype = mimetype;
-    entry.content = content;
-    m_dirs[path][name] = entry;
-    // LOGDEB("VirtualDir::addFile: added entry for dir " <<
-    // path << " name " << name << endl);
-    return true;
-}
-
-VirtualDir::FileEnt *VirtualDir::getFile(const string& _path,
-        const string& name)
-{
-    string path(_path);
-    if (path.empty() || path[path.size() - 1] != '/') {
-        path += '/';
-    }
-
-    // LOGDEB("VirtualDir::getFile: path " << path << " name " << name << endl);
-
-    std::unordered_map<string, std::unordered_map<string, VirtualDir::FileEnt> >::iterator dir =
-        m_dirs.find(path);
-    if (dir == m_dirs.end()) {
-        LOGERR("VirtualDir::getFile: no dir: " << path << endl);
-        return 0;
-    }
-    std::unordered_map<string, FileEnt>::iterator f = dir->second.find(name);
-    if (f == dir->second.end()) {
-        LOGERR("VirtualDir::getFile: no file: " << path << endl);
-        return 0;
-    }
-
-    return &(f->second);
-}
-
-}
+}