Comparison

tools/migration/migrator/jabberd14.lua @ 4417:33c149394dcb

migrator/jabberd14: Support for reading jabberd14 spool files.
author Waqas Hussain <waqas20@gmail.com>
date Sat, 19 Nov 2011 21:12:23 +0500
child 4445:0434eb77d18c
comparison
equal deleted inserted replaced
4416:03e1295d599a 4417:33c149394dcb
1
2 local lfs = require "lfs";
3 local lxp = require "lxp";
4 local st = require "util.stanza";
5 local os_getenv = os.getenv;
6 local io_open = io.open;
7 local assert = assert;
8 local ipairs = ipairs;
9 local coroutine = coroutine;
10 local print = print;
11
12 module "jabberd14"
13
14 local function is_dir(path) return lfs.attributes(path, "mode") == "directory"; end
15 local function is_file(path) return lfs.attributes(path, "mode") == "file"; end
16 local function clean_path(path)
17 return path:gsub("\\", "/"):gsub("//+", "/"):gsub("^~", os_getenv("HOME") or "~");
18 end
19
20 local parse_xml = (function()
21 local ns_prefixes = {
22 ["http://www.w3.org/XML/1998/namespace"] = "xml";
23 };
24 local ns_separator = "\1";
25 local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
26 return function(xml)
27 local handler = {};
28 local stanza = st.stanza("root");
29 function handler:StartElement(tagname, attr)
30 local curr_ns,name = tagname:match(ns_pattern);
31 if name == "" then
32 curr_ns, name = "", curr_ns;
33 end
34 if curr_ns ~= "" then
35 attr.xmlns = curr_ns;
36 end
37 for i=1,#attr do
38 local k = attr[i];
39 attr[i] = nil;
40 local ns, nm = k:match(ns_pattern);
41 if nm ~= "" then
42 ns = ns_prefixes[ns];
43 if ns then
44 attr[ns..":"..nm] = attr[k];
45 attr[k] = nil;
46 end
47 end
48 end
49 stanza:tag(name, attr);
50 end
51 function handler:CharacterData(data)
52 stanza:text(data);
53 end
54 function handler:EndElement(tagname)
55 stanza:up();
56 end
57 local parser = lxp.new(handler, "\1");
58 local ok, err, line, col = parser:parse(xml);
59 if ok then ok, err, line, col = parser:parse(); end
60 --parser:close();
61 if ok then
62 return stanza.tags[1];
63 else
64 return ok, err.." (line "..line..", col "..col..")";
65 end
66 end;
67 end)();
68
69 local function load_xml(path)
70 if path then
71 local f, err = io_open(path);
72 if not f then return f, err; end
73 local data = f:read("*a");
74 f:close();
75 if data then
76 return parse_xml(data);
77 end
78 end
79 end
80
81 local function load_spool_file(host, filename, path)
82 local xml = load_xml(path);
83 if not xml then return; end
84
85 local register_element = xml:get_child("query", "jabber:iq:register");
86 local username_element = register_element and register_element:get_child("username", "jabber:iq:register");
87 local password_element = register_element and register_element:get_child("password", "jabber:iq:auth");
88 local username = username_element and username_element:get_text();
89 local password = password_element and password_element:get_text();
90 if not username then
91 print("[warn] Missing /xdb/{jabber:iq:register}register/username> in file "..filename)
92 return;
93 elseif username..".xml" ~= filename then
94 print("[warn] Missing /xdb/{jabber:iq:register}register/username does not match filename "..filename);
95 return;
96 end
97
98 local userdata = {
99 user = username;
100 host = host;
101 stores = {};
102 };
103 local stores = userdata.stores;
104 stores.accounts = { password = password };
105
106 for i=1,#xml.tags do
107 local tag = xml.tags[i];
108 local xname = (tag.attr.xmlns or "")..":"..tag.name;
109 if tag.attr.j_private_flag == "1" and tag.attr.xmlns then
110 -- Private XML
111 stores.private = stores.private or {};
112 tag.attr.j_private_flag = nil;
113 stores.private[tag.attr.xmlns] = st.preserialize(tag);
114 elseif xname == "jabber:iq:auth:password" then
115 if stores.accounts.password ~= tag:get_text() then
116 if password then
117 print("[warn] conflicting passwords")
118 else
119 stores.accounts.password = tag:get_text();
120 end
121 end
122 elseif xname == "jabber:iq:register:query" then
123 -- already processed
124 elseif xname == "jabber:xdb:nslist:foo" then
125 -- ignore
126 elseif xname == "jabber:iq:auth:0k:zerok" then
127 -- ignore
128 elseif xname == "jabber:iq:roster:query" then
129 -- Roster
130 local roster = {};
131 local subscription_types = { from = true, to = true, both = true, none = true };
132 for _,item_element in ipairs(tag.tags) do
133 assert(item_element.name == "item");
134 assert(item_element.attr.jid);
135 assert(subscription_types[item_element.attr.subscription]);
136 assert((item_element.attr.ask or "subscribe") == "subscribe")
137 if item_element.name == "item" then
138 local groups = {};
139 for _,group_element in ipairs(item_element.tags) do
140 assert(group_element.name == "group");
141 groups[group_element:get_text()] = true;
142 end
143 local item = {
144 name = item_element.attr.name;
145 subscription = item_element.attr.subscription;
146 ask = item_element.attr.ask;
147 groups = groups;
148 };
149 roster[item_element.attr.jid] = item;
150 end
151 end
152 stores.roster = roster;
153 elseif xname == "jabber:iq:last:query" then
154 -- Last activity
155 elseif xname == "jabber:x:offline:foo" then
156 -- Offline messages
157 elseif xname == "vcard-temp:vCard" then
158 -- vCards
159 stores.vcard = st.preserialize(tag);
160 else
161 print("[warn] Unknown tag: "..xname);
162 end
163 end
164 return userdata;
165 end
166
167 local function loop_over_users(path, host, cb)
168 for file in lfs.dir(path) do
169 if file:match("%.xml$") then
170 local user = load_spool_file(host, file, path.."/"..file);
171 if user then cb(user); end
172 end
173 end
174 end
175 local function loop_over_hosts(path, cb)
176 for host in lfs.dir(path) do
177 if host ~= "." and host ~= ".." and is_dir(path.."/"..host) then
178 loop_over_users(path.."/"..host, host, cb);
179 end
180 end
181 end
182
183 function reader(input)
184 local path = clean_path(assert(input.path, "no input.path specified"));
185 assert(is_dir(path), "input.path is not a directory");
186
187 if input.host then
188 return coroutine.wrap(function() loop_over_users(input.path, input.host, coroutine.yield) end);
189 else
190 return coroutine.wrap(function() loop_over_hosts(input.path, coroutine.yield) end);
191 end
192 end
193
194 return _M;