Software /
code /
prosody
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; |