Software /
code /
prosody
Comparison
tools/xep227toprosody.lua @ 2507:a8ce11633597
xep227toprosody: Initial commit (thanks stefan).
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Wed, 27 Jan 2010 23:04:13 +0500 |
child | 2508:fed7f19db0da |
comparison
equal
deleted
inserted
replaced
2506:d11a781baf13 | 2507:a8ce11633597 |
---|---|
1 #!/usr/bin/env lua | |
2 -- Prosody IM | |
3 -- Copyright (C) 2008-2009 Matthew Wild | |
4 -- Copyright (C) 2008-2009 Waqas Hussain | |
5 -- Copyright (C) 2010 Stefan Gehn | |
6 -- | |
7 -- This project is MIT/X11 licensed. Please see the | |
8 -- COPYING file in the source package for more information. | |
9 -- | |
10 | |
11 -- FIXME: XEP-0227 supports XInclude but luaexpat does not | |
12 -- | |
13 -- XEP-227 elements and their current level of support: | |
14 -- Hosts : supported | |
15 -- Users : supported | |
16 -- Rosters : supported, needs testing | |
17 -- Offline Messages : supported, needs testing | |
18 -- Private XML Storage : supported, needs testing | |
19 -- vCards : supported, needs testing | |
20 -- Privacy Lists: UNSUPPORTED | |
21 -- http://xmpp.org/extensions/xep-0227.html#privacy-lists | |
22 -- mod_privacy uses dm.load(username, host, "privacy"); and stores stanzas 1:1 | |
23 -- Incoming Subscription Requests : supported | |
24 | |
25 package.path = package.path..";../?.lua"; | |
26 package.cpath = package.cpath..";../?.so"; -- needed for util.pposix used in datamanager | |
27 | |
28 -- ugly workaround for getting datamanager to work outside of prosody :( | |
29 prosody = { }; | |
30 prosody.platform = "unknown"; | |
31 if os.getenv("WINDIR") then | |
32 prosody.platform = "windows"; | |
33 elseif package.config:sub(1,1) == "/" then | |
34 prosody.platform = "posix"; | |
35 end | |
36 | |
37 local lxp = require "lxp"; | |
38 local st = require "util.stanza"; | |
39 local init_xmlhandlers = require "core.xmlhandlers"; | |
40 local dm = require "util.datamanager" | |
41 dm.set_data_path("data"); | |
42 | |
43 local ns_separator = "\1"; | |
44 local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$"; | |
45 local ns_xep227 = "http://www.xmpp.org/extensions/xep-0227.html#ns"; | |
46 | |
47 ----------------------------------------------------------------------- | |
48 | |
49 function store_vcard(username, host, stanza) | |
50 -- create or update vCard for username@host | |
51 local ret, err = dm.store(username, host, "vcard", st.preserialize(stanza)); | |
52 print("["..(err or "success").."] stored vCard: "..username.."@"..host); | |
53 end | |
54 | |
55 function store_password(username, host, password) | |
56 -- create or update account for username@host | |
57 local ret, err = dm.store(username, host, "accounts", {password = password}); | |
58 print("["..(err or "success").."] stored account: "..username.."@"..host.." = "..password); | |
59 end | |
60 | |
61 function store_roster(username, host, roster_items) | |
62 -- fetch current roster-table for username@host if he already has one | |
63 local roster = dm.load(username, host, "roster") or {}; | |
64 -- merge imported roster-items with loaded roster | |
65 for item_tag in roster_items:childtags() do | |
66 -- jid for this roster-item | |
67 local item_jid = item_tag.attr.jid | |
68 -- validate item stanzas | |
69 if (item_tag.name == "item") and (item_jid ~= "") then | |
70 -- prepare roster item | |
71 -- TODO: is the subscription attribute optional? | |
72 local item = {subscription = item_tag.attr.subscription, groups = {}}; | |
73 -- optional: give roster item a real name | |
74 if item_tag.attr.name then | |
75 item.name = item_tag.attr.name; | |
76 end | |
77 -- optional: iterate over group stanzas inside item stanza | |
78 for group_tag in item_tag:childtags() do | |
79 local group_name = group_tag:get_text(); | |
80 if (group_tag.name == "group") and (group_name ~= "") then | |
81 item.groups[group_name] = true; | |
82 else | |
83 print("[error] invalid group stanza: "..group_tag:pretty_print()); | |
84 end | |
85 end | |
86 -- store item in roster | |
87 roster[item_jid] = item; | |
88 print("[success] roster entry: " ..username.."@"..host.." - "..item_jid); | |
89 else | |
90 print("[error] invalid roster stanza: " ..item_tag:pretty_print()); | |
91 end | |
92 | |
93 end | |
94 -- store merged roster-table | |
95 local ret, err = dm.store(username, host, "roster", roster); | |
96 print("["..(err or "success").."] stored roster: " ..username.."@"..host); | |
97 end | |
98 | |
99 function store_private(username, host, private_items) | |
100 local private = dm.load(username, host, "private") or {}; | |
101 for ch in private_items:childtags() do | |
102 --print("private :"..ch:pretty_print()); | |
103 private[ch.name..":"..ch.attr.xmlns] = st.preserialize(ch); | |
104 print("[success] private item: " ..username.."@"..host.." - "..ch.name); | |
105 end | |
106 local ret, err = dm.store(username, host, "private", private); | |
107 print("["..(err or "success").."] stored private: " ..username.."@"..host); | |
108 end | |
109 | |
110 function store_offline_messages(username, host, offline_messages) | |
111 -- TODO: maybe use list_load(), append and list_store() instead | |
112 -- of constantly reopening the file with list_append()? | |
113 for ch in offline_messages:childtags() do | |
114 --print("message :"..ch:pretty_print()); | |
115 local ret, err = dm.list_append(username, host, "offline", st.preserialize(ch)); | |
116 print("["..(err or "success").."] stored offline message: " ..username.."@"..host.." - "..ch.attr.from); | |
117 end | |
118 end | |
119 | |
120 | |
121 function store_subscription_request(username, host, presence_stanza) | |
122 local from_bare = presence_stanza.attr.from; | |
123 | |
124 -- fetch current roster-table for username@host if he already has one | |
125 local roster = dm.load(username, host, "roster") or {}; | |
126 | |
127 local item = roster[from_bare]; | |
128 if item and (item.subscription == "from" or item.subscription == "both") then | |
129 return; -- already subscribed, do nothing | |
130 end | |
131 | |
132 -- add to table of pending subscriptions | |
133 if not roster.pending then roster.pending = {}; end | |
134 roster.pending[from_bare] = true; | |
135 | |
136 -- store updated roster-table | |
137 local ret, err = dm.store(username, host, "roster", roster); | |
138 print("["..(err or "success").."] stored subscription request: " ..username.."@"..host.." - "..from_bare); | |
139 end | |
140 | |
141 ----------------------------------------------------------------------- | |
142 | |
143 local curr_host = ""; | |
144 | |
145 | |
146 | |
147 local cb = { | |
148 stream_tag = "user", | |
149 stream_ns = ns_xep227, | |
150 user_name = "" | |
151 }; | |
152 function cb.streamopened(session, attr) | |
153 session.notopen = false; | |
154 user_name = attr.name; | |
155 store_password(user_name, curr_host, attr.password); | |
156 end | |
157 function cb.streamclosed(session) | |
158 session.notopen = true; | |
159 user_name = ""; | |
160 end | |
161 function cb.handlestanza(session, stanza) | |
162 --print("Parsed stanza "..stanza.name.." xmlns: "..(stanza.attr.xmlns or "")); | |
163 if (stanza.name == "vCard") and (stanza.attr.xmlns == "vcard-temp") then | |
164 store_vcard(user_name, curr_host, stanza); | |
165 elseif (stanza.name == "query") then | |
166 if (stanza.attr.xmlns == "jabber:iq:roster") then | |
167 store_roster(user_name, curr_host, stanza); | |
168 elseif (stanza.attr.xmlns == "jabber:iq:private") then | |
169 store_private(user_name, curr_host, stanza); | |
170 end | |
171 elseif (stanza.name == "offline-messages") then | |
172 store_offline_messages(user_name, curr_host, stanza); | |
173 elseif (stanza.name == "presence") and (stanza.attr.xmlns == "jabber:client") then | |
174 store_subscription_request(user_name, curr_host, stanza); | |
175 else | |
176 print("UNHANDLED stanza "..stanza.name.." xmlns: "..(stanza.attr.xmlns or "")); | |
177 end | |
178 end | |
179 | |
180 local user_handlers = init_xmlhandlers({ notopen = true, }, cb); | |
181 | |
182 ----------------------------------------------------------------------- | |
183 | |
184 local lxp_handlers = { | |
185 --count = 0 | |
186 }; | |
187 | |
188 -- TODO: error handling for invalid opening elements if curr_host is empty | |
189 function lxp_handlers.StartElement(parser, elementname, attributes) | |
190 local curr_ns, name = elementname:match(ns_pattern); | |
191 if name == "" then | |
192 curr_ns, name = "", curr_ns; | |
193 end | |
194 --io.write("+ ", string.rep(" ", count), name, " (", curr_ns, ")", "\n") | |
195 --count = count + 1; | |
196 if curr_host ~= "" then | |
197 -- forward to xmlhandlers | |
198 user_handlers:StartElement(elementname, attributes); | |
199 elseif (curr_ns == ns_xep227) and (name == "host") then | |
200 curr_host = attributes["jid"]; -- start of host element | |
201 print("Begin parsing host "..curr_host); | |
202 elseif (curr_ns ~= ns_xep227) or (name ~= "server-data") then | |
203 io.stderr:write("Unhandled XML element: ", name, "\n"); | |
204 os.exit(1); | |
205 end | |
206 end | |
207 | |
208 -- TODO: error handling for invalid closing elements if host is empty | |
209 function lxp_handlers.EndElement(parser, elementname) | |
210 local curr_ns, name = elementname:match(ns_pattern); | |
211 if name == "" then | |
212 curr_ns, name = "", curr_ns; | |
213 end | |
214 --count = count - 1; | |
215 --io.write("- ", string.rep(" ", count), name, " (", curr_ns, ")", "\n") | |
216 if curr_host ~= "" then | |
217 if (curr_ns == ns_xep227) and (name == "host") then | |
218 print("End parsing host "..curr_host); | |
219 curr_host = "" -- end of host element | |
220 else | |
221 -- forward to xmlhandlers | |
222 user_handlers:EndElement(elementname, attributes); | |
223 end | |
224 elseif (curr_ns ~= ns_xep227) or (name ~= "server-data") then | |
225 io.stderr:write("Unhandled XML element: ", name, "\n"); | |
226 os.exit(1); | |
227 end | |
228 end | |
229 | |
230 function lxp_handlers.CharacterData(parser, string) | |
231 if curr_host ~= "" then | |
232 -- forward to xmlhandlers | |
233 user_handlers:CharacterData(string); | |
234 end | |
235 end | |
236 | |
237 ----------------------------------------------------------------------- | |
238 | |
239 local arg = ...; | |
240 local help = "/? -? ? /h -h /help -help --help"; | |
241 if not arg or help:find(arg, 1, true) then | |
242 print([[XEP-227 importer for Prosody | |
243 | |
244 Usage: xep227toprosody.lua filename.xml | |
245 | |
246 ]]); | |
247 os.exit(1); | |
248 end | |
249 | |
250 local file = io.open(arg); | |
251 if not file then | |
252 io.stderr:write("Could not open file: ", arg, "\n"); | |
253 os.exit(0); | |
254 end | |
255 | |
256 local parser = lxp.new(lxp_handlers, ns_separator); | |
257 for l in file:lines() do | |
258 parser:parse(l); | |
259 end | |
260 parser:parse(); | |
261 parser:close(); | |
262 file:close(); |