Diff

mod_archive_muc/mod_archive_muc.lua @ 237:d900be0dee3e

mod_archive: minor fix; mod_archive_muc: added new module mod_archive_muc; init commit; workable, not fully tested.
author shinysky<shinysky1986(AT)gmail.com>
date Mon, 09 Aug 2010 18:45:51 +0800
child 238:5343b3ebaffb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_archive_muc/mod_archive_muc.lua	Mon Aug 09 18:45:51 2010 +0800
@@ -0,0 +1,189 @@
+-- Prosody IM
+-- Copyright (C) 2010 Dai Zhiwei
+-- 
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+local st = require "util.stanza";
+local dm = require "util.datamanager";
+local jid = require "util.jid";
+local datetime = require "util.datetime";
+
+local PREFS_DIR = "archive_muc_prefs";
+local ARCHIVE_DIR = "archive_muc";
+
+local HOST = 'localhost';
+
+local AUTO_ARCHIVING_ENABLED = true;
+
+module:add_feature("urn:xmpp:archive#preferences");
+module:add_feature("urn:xmpp:archive#management");
+
+------------------------------------------------------------
+-- Utils
+------------------------------------------------------------
+local function load_prefs(node, host)
+    return st.deserialize(dm.load(node, host, PREFS_DIR));
+end
+
+local function store_prefs(data, node, host)
+    dm.store(node, host, PREFS_DIR, st.preserialize(data));
+end
+
+local function date_time(localtime)
+    return datetime.datetime(localtime);
+end
+
+local function match_jid(rule, id)
+    return not rule or jid.compare(id, rule);
+end
+
+local function is_earlier(start, coll_start)
+    return not start or start <= coll_start;
+end
+
+local function is_later(endtime, coll_start)
+    return not endtime or endtime >= coll_start;
+end
+
+------------------------------------------------------------
+-- Preferences
+------------------------------------------------------------
+local function preferences_handler(event)
+    local origin, stanza = event.origin, event.stanza;
+    module:log("debug", "-- Enter muc preferences_handler()");
+    module:log("debug", "-- muc pref:\n%s", tostring(stanza));
+    if stanza.attr.type == "get" then
+        local data = load_prefs(origin.username, origin.host);
+        if data then
+            origin.send(st.reply(stanza):add_child(data));
+        else
+            origin.send(st.reply(stanza));
+        end
+    elseif stanza.attr.type == "set" then
+        local node, host = origin.username, origin.host;
+        if stanza.tags[1] and stanza.tags[1].name == 'prefs' then
+            store_prefs(stanza.tags[1], node, host);
+            origin.send(st.reply(stanza));
+            local user = bare_sessions[node.."@"..host];
+            local push = st.iq({type="set"});
+            push:add_child(stanza.tags[1]);
+            for _, res in pairs(user and user.sessions or NULL) do -- broadcast to all resources
+                if res.presence then -- to resource
+                    push.attr.to = res.full_jid;
+                    res.send(push);
+                end
+            end
+        end
+    end
+    return true;
+end
+
+------------------------------------------------------------
+-- Archive Management
+------------------------------------------------------------
+local function management_handler(event)
+    module:log("debug", "-- Enter muc management_handler()");
+    local origin, stanza = event.origin, event.stanza;
+    local node, host = origin.username, origin.host;
+	local data = dm.list_load(node, host, ARCHIVE_DIR);
+    local elem = stanza.tags[1];
+    local resset = {}
+    if data then
+        for i = #data, 1, -1 do
+            local forwarded = st.deserialize(data[i]);
+            local res = (match_jid(elem.attr["with"], forwarded.tags[2].attr.from)
+                or match_jid(elem.attr["with"], forwarded.tags[2].attr.to))
+                and is_earlier(elem.attr["start"], forwarded.tags[1].attr["stamp"])
+                and is_later(elem.attr["end"], forwarded.tags[1].attr["stamp"]);
+            if res then
+                table.insert(resset, forwarded);
+            end
+        end
+        for i = #resset, 1, -1 do
+            local res = st.message({to = stanza.attr.from, id=st.new_id()});
+            res:add_child(resset[i]);
+            origin.send(res);
+        end
+    end
+    origin.send(st.reply(stanza));
+    return true;
+end
+
+------------------------------------------------------------
+-- Message Handler
+------------------------------------------------------------
+local function is_in(list, jid)
+    for _,v in ipairs(list) do
+        if match_jid(v:get_text(), jid) then -- JID Matching
+            return true;
+        end
+    end
+    return false;
+end
+
+local function is_in_roster(node, host, jid)
+    -- TODO
+    return true;
+end
+
+local function apply_pref(node, host, jid)
+    local pref = load_prefs(node, host);
+    if not pref then
+        return AUTO_ARCHIVING_ENABLED;
+    end
+    local always = pref:child_with_name('always');
+    if always and is_in(always, jid) then
+        return true;
+    end
+    local never = pref:child_with_name('never');
+    if never and is_in(never, jid) then
+        return false;
+    end
+    local default = pref.attr['default'];
+    if default == 'roster' then
+        return is_in_roster(node, host, jid);
+    elseif default == 'always' then
+        return true;
+    elseif default == 'never' then
+        return false;
+    end
+    return AUTO_ARCHIVING_ENABLED;
+end
+
+local function store_msg(msg, node, host)
+    local forwarded = st.stanza('forwarded', {xmlns='urn:xmpp:forward:tmp'});
+    forwarded:tag('delay', {xmlns='urn:xmpp:delay',stamp=date_time()}):up();
+    forwarded:add_child(msg);
+    dm.list_append(node, host, ARCHIVE_DIR, st.preserialize(forwarded));
+end
+
+local function msg_handler(data)
+    module:log("debug", "-- Enter muc msg_handler()");
+    local origin, stanza = data.origin, data.stanza;
+    local body = stanza:child_with_name("body");
+    if body then
+        local from_node, from_host = jid.split(stanza.attr.from);
+        local to_node, to_host = jid.split(stanza.attr.to);
+        -- FIXME only archive messages of users on this host
+        if from_host == HOST and apply_pref(from_node, from_host, stanza.attr.to) then
+            store_msg(stanza, from_node, from_host);
+        end
+        if to_host == HOST and apply_pref(to_node, to_host, stanza.attr.from) then
+            store_msg(stanza, to_node, to_host);
+        end
+    end
+
+    return nil;
+end
+
+-- Preferences
+module:hook("iq/self/urn:xmpp:archive#preferences:prefs", preferences_handler);
+-- Archive management
+module:hook("iq/self/urn:xmpp:archive#management:query", management_handler);
+
+module:hook("message/full", msg_handler, 20);
+module:hook("message/bare", msg_handler, 20);
+
+-- TODO prefs: [1] = "\n      ";