util.datamanager: Add function for listing stores
[prosody.git] / util / datamanager.lua
index 57cd2594e954d81f9c9cc56310eb6434183a8caa..c29fb416cfaa9433321bee9db7d779a382c75e8e 100644 (file)
@@ -1,7 +1,7 @@
 -- Prosody IM
 -- Copyright (C) 2008-2010 Matthew Wild
 -- Copyright (C) 2008-2010 Waqas Hussain
--- 
+--
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
 --
@@ -11,7 +11,7 @@ local format = string.format;
 local setmetatable, type = setmetatable, type;
 local pairs, ipairs = pairs, ipairs;
 local char = string.char;
-local loadfile, setfenv, pcall = loadfile, setfenv, pcall;
+local pcall = pcall;
 local log = require "util.logger".init("datamanager");
 local io_open = io.open;
 local os_remove = os.remove;
@@ -20,8 +20,10 @@ local error = error;
 local next = next;
 local t_insert = table.insert;
 local append = require "util.serialization".append;
-local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end
+local envloadfile = require"util.envload".envloadfile;
+local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua)
 local lfs = require "lfs";
+local prosody = prosody;
 local raw_mkdir;
 
 if prosody.platform == "posix" then
@@ -56,7 +58,7 @@ local function mkdir(path)
        return path;
 end
 
-local data_path = "data";
+local data_path = (prosody and prosody.paths and prosody.paths.data) or ".";
 local callbacks = {};
 
 ------- API -------------
@@ -71,7 +73,7 @@ local function callback(username, host, datastore, data)
                username, host, datastore, data = f(username, host, datastore, data);
                if username == false then break; end
        end
-       
+
        return username, host, datastore, data;
 end
 function add_callback(func)
@@ -110,22 +112,22 @@ function getpath(username, host, datastore, ext, create)
 end
 
 function load(username, host, datastore)
-       local data, ret = loadfile(getpath(username, host, datastore));
+       local data, ret = envloadfile(getpath(username, host, datastore), {});
        if not data then
                local mode = lfs.attributes(getpath(username, host, datastore), "mode");
                if not mode then
-                       log("debug", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+                       log("debug", "Assuming empty %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
                        return nil;
                else -- file exists, but can't be read
                        -- TODO more detailed error checking and logging?
-                       log("error", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+                       log("error", "Failed to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
                        return nil, "Error reading storage";
                end
        end
-       setfenv(data, {});
+
        local success, ret = pcall(data);
        if not success then
-               log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
+               log("error", "Unable to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
                return nil, "Error reading storage";
        end
        return ret;
@@ -144,7 +146,7 @@ function store(username, host, datastore, data)
        -- save the datastore
        local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+");
        if not f then
-               log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+               log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
                return nil, "Error saving to storage";
        end
        f:write("return ");
@@ -165,7 +167,7 @@ function list_append(username, host, datastore, data)
        -- save the datastore
        local f, msg = io_open(getpath(username, host, datastore, "list", true), "a+");
        if not f then
-               log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+               log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
                return;
        end
        f:write("item(");
@@ -183,7 +185,7 @@ function list_store(username, host, datastore, data)
        -- save the datastore
        local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+");
        if not f then
-               log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
+               log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
                return;
        end
        for _, d in ipairs(data) do
@@ -202,19 +204,53 @@ function list_store(username, host, datastore, data)
 end
 
 function list_load(username, host, datastore)
-       local data, ret = loadfile(getpath(username, host, datastore, "list"));
+       local items = {};
+       local data, ret = envloadfile(getpath(username, host, datastore, "list"), {item = function(i) t_insert(items, i); end});
        if not data then
-               log("debug", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
-               return nil;
+               local mode = lfs.attributes(getpath(username, host, datastore, "list"), "mode");
+               if not mode then
+                       log("debug", "Assuming empty %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
+                       return nil;
+               else -- file exists, but can't be read
+                       -- TODO more detailed error checking and logging?
+                       log("error", "Failed to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
+                       return nil, "Error reading storage";
+               end
        end
-       local items = {};
-       setfenv(data, {item = function(i) t_insert(items, i); end});
+
        local success, ret = pcall(data);
        if not success then
-               log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
-               return nil;
+               log("error", "Unable to load %s storage ('%s') for user: %s@%s", datastore, ret, username or "nil", host or "nil");
+               return nil, "Error reading storage";
        end
        return items;
 end
 
+function list_stores(username, host)
+       if not host then
+               return nil, "bad argument #2 to 'list_stores' (string expected, got nothing)";
+       end
+       local list = {};
+       local host_dir = format("%s/%s/", data_path, encode(host));
+       for node in lfs.dir(host_dir) do
+               if not node:match"^%." then -- dots should be encoded, this is probably . or ..
+                       local store = decode(node);
+                       local path = host_dir..node;
+                       if username == true then
+                               if lfs.attributes(path, "mode") == "directory" then
+                                       list[#list+1] = store;
+                               end
+                       elseif username then
+                               if lfs.attributes(getpath(username, host, store), "mode")
+                                       or lfs.attributes(getpath(username, host, store, "list"), "mode") then
+                                       list[#list+1] = store;
+                               end
+                       elseif lfs.attributes(path, "mode") == "file" then
+                               list[#list+1] = store:gsub("%.[dalist]+$","");
+                       end
+               end
+       end
+       return list;
+end
+
 return _M;