Mercurial > prosody-modules
comparison mod_ircd/mod_ircd_broke.lua @ 455:52f2188ec47d
mod_default_vcard: Sets initial vCard from data enterd on registration
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 15 Oct 2011 13:43:37 +0200 |
parents | 21cb01de89a8 |
children | bbea8081c865 |
comparison
equal
deleted
inserted
replaced
454:3f101f7a26d0 | 455:52f2188ec47d |
---|---|
1 package.preload['verse'] = (function (...) | |
2 package.preload['util.encodings'] = (function (...) | |
3 local function not_impl() | |
4 error("Function not implemented"); | |
5 end | |
6 | |
7 local mime = require "mime"; | |
8 | |
9 module "encodings" | |
10 | |
11 stringprep = {}; | |
12 base64 = { encode = mime.b64, decode = not_impl }; --mime.unb64 is buggy with \0 | |
13 | |
14 return _M; | |
15 end) | |
16 package.preload['util.hashes'] = (function (...) | |
17 local sha1 = require "util.sha1"; | |
18 | |
19 return { sha1 = sha1.sha1 }; | |
20 end) | |
21 package.preload['util.logger'] = (function (...) | |
22 local select, tostring = select, tostring; | |
23 local io_write = function(...) return io.stderr:write(...) end; | |
24 module "logger" | |
25 | |
26 local function format(format, ...) | |
27 local n, maxn = 0, #arg; | |
28 return (format:gsub("%%(.)", function (c) if c ~= "%" and n <= maxn then n = n + 1; return tostring(arg[n]); end end)); | |
29 end | |
30 | |
31 local function format(format, ...) | |
32 local n, maxn = 0, select('#', ...); | |
33 local arg = { ... }; | |
34 return (format:gsub("%%(.)", function (c) if n <= maxn then n = n + 1; return tostring(arg[n]); end end)); | |
35 end | |
36 | |
37 function init(name) | |
38 return function (level, message, ...) | |
39 io_write(level, "\t", format(message, ...), "\n"); | |
40 end | |
41 end | |
42 | |
43 return _M; | |
44 end) | |
45 package.preload['util.sha1'] = (function (...) | |
46 ------------------------------------------------- | |
47 --- *** SHA-1 algorithm for Lua *** --- | |
48 ------------------------------------------------- | |
49 --- Author: Martin Huesser --- | |
50 --- Date: 2008-06-16 --- | |
51 --- License: You may use this code in your --- | |
52 --- projects as long as this header --- | |
53 --- stays intact. --- | |
54 ------------------------------------------------- | |
55 | |
56 local strlen = string.len | |
57 local strchar = string.char | |
58 local strbyte = string.byte | |
59 local strsub = string.sub | |
60 local floor = math.floor | |
61 local bit = require "bit" | |
62 local bnot = bit.bnot | |
63 local band = bit.band | |
64 local bor = bit.bor | |
65 local bxor = bit.bxor | |
66 local shl = bit.lshift | |
67 local shr = bit.rshift | |
68 local h0, h1, h2, h3, h4 | |
69 | |
70 ------------------------------------------------- | |
71 | |
72 local function LeftRotate(val, nr) | |
73 return shl(val, nr) + shr(val, 32 - nr) | |
74 end | |
75 | |
76 ------------------------------------------------- | |
77 | |
78 local function ToHex(num) | |
79 local i, d | |
80 local str = "" | |
81 for i = 1, 8 do | |
82 d = band(num, 15) | |
83 if (d < 10) then | |
84 str = strchar(d + 48) .. str | |
85 else | |
86 str = strchar(d + 87) .. str | |
87 end | |
88 num = floor(num / 16) | |
89 end | |
90 return str | |
91 end | |
92 | |
93 ------------------------------------------------- | |
94 | |
95 local function PreProcess(str) | |
96 local bitlen, i | |
97 local str2 = "" | |
98 bitlen = strlen(str) * 8 | |
99 str = str .. strchar(128) | |
100 i = 56 - band(strlen(str), 63) | |
101 if (i < 0) then | |
102 i = i + 64 | |
103 end | |
104 for i = 1, i do | |
105 str = str .. strchar(0) | |
106 end | |
107 for i = 1, 8 do | |
108 str2 = strchar(band(bitlen, 255)) .. str2 | |
109 bitlen = floor(bitlen / 256) | |
110 end | |
111 return str .. str2 | |
112 end | |
113 | |
114 ------------------------------------------------- | |
115 | |
116 local function MainLoop(str) | |
117 local a, b, c, d, e, f, k, t | |
118 local i, j | |
119 local w = {} | |
120 while (str ~= "") do | |
121 for i = 0, 15 do | |
122 w[i] = 0 | |
123 for j = 1, 4 do | |
124 w[i] = w[i] * 256 + strbyte(str, i * 4 + j) | |
125 end | |
126 end | |
127 for i = 16, 79 do | |
128 w[i] = LeftRotate(bxor(bxor(w[i - 3], w[i - 8]), bxor(w[i - 14], w[i - 16])), 1) | |
129 end | |
130 a = h0 | |
131 b = h1 | |
132 c = h2 | |
133 d = h3 | |
134 e = h4 | |
135 for i = 0, 79 do | |
136 if (i < 20) then | |
137 f = bor(band(b, c), band(bnot(b), d)) | |
138 k = 1518500249 | |
139 elseif (i < 40) then | |
140 f = bxor(bxor(b, c), d) | |
141 k = 1859775393 | |
142 elseif (i < 60) then | |
143 f = bor(bor(band(b, c), band(b, d)), band(c, d)) | |
144 k = 2400959708 | |
145 else | |
146 f = bxor(bxor(b, c), d) | |
147 k = 3395469782 | |
148 end | |
149 t = LeftRotate(a, 5) + f + e + k + w[i] | |
150 e = d | |
151 d = c | |
152 c = LeftRotate(b, 30) | |
153 b = a | |
154 a = t | |
155 end | |
156 h0 = band(h0 + a, 4294967295) | |
157 h1 = band(h1 + b, 4294967295) | |
158 h2 = band(h2 + c, 4294967295) | |
159 h3 = band(h3 + d, 4294967295) | |
160 h4 = band(h4 + e, 4294967295) | |
161 str = strsub(str, 65) | |
162 end | |
163 end | |
164 | |
165 ------------------------------------------------- | |
166 | |
167 local function sha1(str, hexres) | |
168 str = PreProcess(str) | |
169 h0 = 1732584193 | |
170 h1 = 4023233417 | |
171 h2 = 2562383102 | |
172 h3 = 0271733878 | |
173 h4 = 3285377520 | |
174 MainLoop(str) | |
175 local hex = ToHex(h0)..ToHex(h1)..ToHex(h2) | |
176 ..ToHex(h3)..ToHex(h4); | |
177 if hexres then | |
178 return hex; | |
179 else | |
180 return (hex:gsub("..", function (byte) | |
181 return string.char(tonumber(byte, 16)); | |
182 end)); | |
183 end | |
184 end | |
185 | |
186 _G.sha1 = {sha1 = sha1}; | |
187 return _G.sha1; | |
188 | |
189 ------------------------------------------------- | |
190 ------------------------------------------------- | |
191 ------------------------------------------------- | |
192 end) | |
193 package.preload['lib.adhoc'] = (function (...) | |
194 -- Copyright (C) 2009-2010 Florian Zeitz | |
195 -- | |
196 -- This file is MIT/X11 licensed. Please see the | |
197 -- COPYING file in the source package for more information. | |
198 -- | |
199 | |
200 local st, uuid = require "util.stanza", require "util.uuid"; | |
201 | |
202 local xmlns_cmd = "http://jabber.org/protocol/commands"; | |
203 | |
204 local states = {} | |
205 | |
206 local _M = {}; | |
207 | |
208 function _cmdtag(desc, status, sessionid, action) | |
209 local cmd = st.stanza("command", { xmlns = xmlns_cmd, node = desc.node, status = status }); | |
210 if sessionid then cmd.attr.sessionid = sessionid; end | |
211 if action then cmd.attr.action = action; end | |
212 | |
213 return cmd; | |
214 end | |
215 | |
216 function _M.new(name, node, handler, permission) | |
217 return { name = name, node = node, handler = handler, cmdtag = _cmdtag, permission = (permission or "user") }; | |
218 end | |
219 | |
220 function _M.handle_cmd(command, origin, stanza) | |
221 local sessionid = stanza.tags[1].attr.sessionid or uuid.generate(); | |
222 local dataIn = {}; | |
223 dataIn.to = stanza.attr.to; | |
224 dataIn.from = stanza.attr.from; | |
225 dataIn.action = stanza.tags[1].attr.action or "execute"; | |
226 dataIn.form = stanza.tags[1]:child_with_ns("jabber:x:data"); | |
227 | |
228 local data, state = command:handler(dataIn, states[sessionid]); | |
229 states[sessionid] = state; | |
230 local stanza = st.reply(stanza); | |
231 if data.status == "completed" then | |
232 states[sessionid] = nil; | |
233 cmdtag = command:cmdtag("completed", sessionid); | |
234 elseif data.status == "canceled" then | |
235 states[sessionid] = nil; | |
236 cmdtag = command:cmdtag("canceled", sessionid); | |
237 elseif data.status == "error" then | |
238 states[sessionid] = nil; | |
239 stanza = st.error_reply(stanza, data.error.type, data.error.condition, data.error.message); | |
240 origin.send(stanza); | |
241 return true; | |
242 else | |
243 cmdtag = command:cmdtag("executing", sessionid); | |
244 end | |
245 | |
246 for name, content in pairs(data) do | |
247 if name == "info" then | |
248 cmdtag:tag("note", {type="info"}):text(content):up(); | |
249 elseif name == "warn" then | |
250 cmdtag:tag("note", {type="warn"}):text(content):up(); | |
251 elseif name == "error" then | |
252 cmdtag:tag("note", {type="error"}):text(content.message):up(); | |
253 elseif name =="actions" then | |
254 local actions = st.stanza("actions"); | |
255 for _, action in ipairs(content) do | |
256 if (action == "prev") or (action == "next") or (action == "complete") then | |
257 actions:tag(action):up(); | |
258 else | |
259 module:log("error", 'Command "'..command.name.. | |
260 '" at node "'..command.node..'" provided an invalid action "'..action..'"'); | |
261 end | |
262 end | |
263 cmdtag:add_child(actions); | |
264 elseif name == "form" then | |
265 cmdtag:add_child((content.layout or content):form(content.values)); | |
266 elseif name == "result" then | |
267 cmdtag:add_child((content.layout or content):form(content.values, "result")); | |
268 elseif name == "other" then | |
269 cmdtag:add_child(content); | |
270 end | |
271 end | |
272 stanza:add_child(cmdtag); | |
273 origin.send(stanza); | |
274 | |
275 return true; | |
276 end | |
277 | |
278 return _M; | |
279 end) | |
280 package.preload['util.stanza'] = (function (...) | |
281 -- Prosody IM | |
282 -- Copyright (C) 2008-2010 Matthew Wild | |
283 -- Copyright (C) 2008-2010 Waqas Hussain | |
284 -- | |
285 -- This project is MIT/X11 licensed. Please see the | |
286 -- COPYING file in the source package for more information. | |
287 -- | |
288 | |
289 | |
290 local t_insert = table.insert; | |
291 local t_concat = table.concat; | |
292 local t_remove = table.remove; | |
293 local t_concat = table.concat; | |
294 local s_format = string.format; | |
295 local s_match = string.match; | |
296 local tostring = tostring; | |
297 local setmetatable = setmetatable; | |
298 local getmetatable = getmetatable; | |
299 local pairs = pairs; | |
300 local ipairs = ipairs; | |
301 local type = type; | |
302 local next = next; | |
303 local print = print; | |
304 local unpack = unpack; | |
305 local s_gsub = string.gsub; | |
306 local s_char = string.char; | |
307 local s_find = string.find; | |
308 local os = os; | |
309 | |
310 local do_pretty_printing = not os.getenv("WINDIR"); | |
311 local getstyle, getstring; | |
312 if do_pretty_printing then | |
313 local ok, termcolours = pcall(require, "util.termcolours"); | |
314 if ok then | |
315 getstyle, getstring = termcolours.getstyle, termcolours.getstring; | |
316 else | |
317 do_pretty_printing = nil; | |
318 end | |
319 end | |
320 | |
321 local xmlns_stanzas = "urn:ietf:params:xml:ns:xmpp-stanzas"; | |
322 | |
323 module "stanza" | |
324 | |
325 stanza_mt = { __type = "stanza" }; | |
326 stanza_mt.__index = stanza_mt; | |
327 local stanza_mt = stanza_mt; | |
328 | |
329 function stanza(name, attr) | |
330 local stanza = { name = name, attr = attr or {}, tags = {} }; | |
331 return setmetatable(stanza, stanza_mt); | |
332 end | |
333 local stanza = stanza; | |
334 | |
335 function stanza_mt:query(xmlns) | |
336 return self:tag("query", { xmlns = xmlns }); | |
337 end | |
338 | |
339 function stanza_mt:body(text, attr) | |
340 return self:tag("body", attr):text(text); | |
341 end | |
342 | |
343 function stanza_mt:tag(name, attrs) | |
344 local s = stanza(name, attrs); | |
345 local last_add = self.last_add; | |
346 if not last_add then last_add = {}; self.last_add = last_add; end | |
347 (last_add[#last_add] or self):add_direct_child(s); | |
348 t_insert(last_add, s); | |
349 return self; | |
350 end | |
351 | |
352 function stanza_mt:text(text) | |
353 local last_add = self.last_add; | |
354 (last_add and last_add[#last_add] or self):add_direct_child(text); | |
355 return self; | |
356 end | |
357 | |
358 function stanza_mt:up() | |
359 local last_add = self.last_add; | |
360 if last_add then t_remove(last_add); end | |
361 return self; | |
362 end | |
363 | |
364 function stanza_mt:reset() | |
365 self.last_add = nil; | |
366 return self; | |
367 end | |
368 | |
369 function stanza_mt:add_direct_child(child) | |
370 if type(child) == "table" then | |
371 t_insert(self.tags, child); | |
372 end | |
373 t_insert(self, child); | |
374 end | |
375 | |
376 function stanza_mt:add_child(child) | |
377 local last_add = self.last_add; | |
378 (last_add and last_add[#last_add] or self):add_direct_child(child); | |
379 return self; | |
380 end | |
381 | |
382 function stanza_mt:get_child(name, xmlns) | |
383 for _, child in ipairs(self.tags) do | |
384 if (not name or child.name == name) | |
385 and ((not xmlns and self.attr.xmlns == child.attr.xmlns) | |
386 or child.attr.xmlns == xmlns) then | |
387 | |
388 return child; | |
389 end | |
390 end | |
391 end | |
392 | |
393 function stanza_mt:get_child_text(name, xmlns) | |
394 local tag = self:get_child(name, xmlns); | |
395 if tag then | |
396 return tag:get_text(); | |
397 end | |
398 return nil; | |
399 end | |
400 | |
401 function stanza_mt:child_with_name(name) | |
402 for _, child in ipairs(self.tags) do | |
403 if child.name == name then return child; end | |
404 end | |
405 end | |
406 | |
407 function stanza_mt:child_with_ns(ns) | |
408 for _, child in ipairs(self.tags) do | |
409 if child.attr.xmlns == ns then return child; end | |
410 end | |
411 end | |
412 | |
413 function stanza_mt:children() | |
414 local i = 0; | |
415 return function (a) | |
416 i = i + 1 | |
417 return a[i]; | |
418 end, self, i; | |
419 end | |
420 | |
421 function stanza_mt:childtags(name, xmlns) | |
422 xmlns = xmlns or self.attr.xmlns; | |
423 local tags = self.tags; | |
424 local start_i, max_i = 1, #tags; | |
425 return function () | |
426 for i = start_i, max_i do | |
427 local v = tags[i]; | |
428 if (not name or v.name == name) | |
429 and (not xmlns or xmlns == v.attr.xmlns) then | |
430 start_i = i+1; | |
431 return v; | |
432 end | |
433 end | |
434 end; | |
435 end | |
436 | |
437 function stanza_mt:maptags(callback) | |
438 local tags, curr_tag = self.tags, 1; | |
439 local n_children, n_tags = #self, #tags; | |
440 | |
441 local i = 1; | |
442 while curr_tag <= n_tags do | |
443 if self[i] == tags[curr_tag] then | |
444 local ret = callback(self[i]); | |
445 if ret == nil then | |
446 t_remove(self, i); | |
447 t_remove(tags, curr_tag); | |
448 n_children = n_children - 1; | |
449 n_tags = n_tags - 1; | |
450 else | |
451 self[i] = ret; | |
452 tags[i] = ret; | |
453 end | |
454 i = i + 1; | |
455 curr_tag = curr_tag + 1; | |
456 end | |
457 end | |
458 return self; | |
459 end | |
460 | |
461 local xml_escape | |
462 do | |
463 local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; | |
464 function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end | |
465 _M.xml_escape = xml_escape; | |
466 end | |
467 | |
468 local function _dostring(t, buf, self, xml_escape, parentns) | |
469 local nsid = 0; | |
470 local name = t.name | |
471 t_insert(buf, "<"..name); | |
472 for k, v in pairs(t.attr) do | |
473 if s_find(k, "\1", 1, true) then | |
474 local ns, attrk = s_match(k, "^([^\1]*)\1?(.*)$"); | |
475 nsid = nsid + 1; | |
476 t_insert(buf, " xmlns:ns"..nsid.."='"..xml_escape(ns).."' ".."ns"..nsid..":"..attrk.."='"..xml_escape(v).."'"); | |
477 elseif not(k == "xmlns" and v == parentns) then | |
478 t_insert(buf, " "..k.."='"..xml_escape(v).."'"); | |
479 end | |
480 end | |
481 local len = #t; | |
482 if len == 0 then | |
483 t_insert(buf, "/>"); | |
484 else | |
485 t_insert(buf, ">"); | |
486 for n=1,len do | |
487 local child = t[n]; | |
488 if child.name then | |
489 self(child, buf, self, xml_escape, t.attr.xmlns); | |
490 else | |
491 t_insert(buf, xml_escape(child)); | |
492 end | |
493 end | |
494 t_insert(buf, "</"..name..">"); | |
495 end | |
496 end | |
497 function stanza_mt.__tostring(t) | |
498 local buf = {}; | |
499 _dostring(t, buf, _dostring, xml_escape, nil); | |
500 return t_concat(buf); | |
501 end | |
502 | |
503 function stanza_mt.top_tag(t) | |
504 local attr_string = ""; | |
505 if t.attr then | |
506 for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(" %s='%s'", k, xml_escape(tostring(v))); end end | |
507 end | |
508 return s_format("<%s%s>", t.name, attr_string); | |
509 end | |
510 | |
511 function stanza_mt.get_text(t) | |
512 if #t.tags == 0 then | |
513 return t_concat(t); | |
514 end | |
515 end | |
516 | |
517 function stanza_mt.get_error(stanza) | |
518 local type, condition, text; | |
519 | |
520 local error_tag = stanza:get_child("error"); | |
521 if not error_tag then | |
522 return nil, nil, nil; | |
523 end | |
524 type = error_tag.attr.type; | |
525 | |
526 for child in error_tag:childtags() do | |
527 if child.attr.xmlns == xmlns_stanzas then | |
528 if not text and child.name == "text" then | |
529 text = child:get_text(); | |
530 elseif not condition then | |
531 condition = child.name; | |
532 end | |
533 if condition and text then | |
534 break; | |
535 end | |
536 end | |
537 end | |
538 return type, condition or "undefined-condition", text; | |
539 end | |
540 | |
541 function stanza_mt.__add(s1, s2) | |
542 return s1:add_direct_child(s2); | |
543 end | |
544 | |
545 | |
546 do | |
547 local id = 0; | |
548 function new_id() | |
549 id = id + 1; | |
550 return "lx"..id; | |
551 end | |
552 end | |
553 | |
554 function preserialize(stanza) | |
555 local s = { name = stanza.name, attr = stanza.attr }; | |
556 for _, child in ipairs(stanza) do | |
557 if type(child) == "table" then | |
558 t_insert(s, preserialize(child)); | |
559 else | |
560 t_insert(s, child); | |
561 end | |
562 end | |
563 return s; | |
564 end | |
565 | |
566 function deserialize(stanza) | |
567 -- Set metatable | |
568 if stanza then | |
569 local attr = stanza.attr; | |
570 for i=1,#attr do attr[i] = nil; end | |
571 local attrx = {}; | |
572 for att in pairs(attr) do | |
573 if s_find(att, "|", 1, true) and not s_find(att, "\1", 1, true) then | |
574 local ns,na = s_match(att, "^([^|]+)|(.+)$"); | |
575 attrx[ns.."\1"..na] = attr[att]; | |
576 attr[att] = nil; | |
577 end | |
578 end | |
579 for a,v in pairs(attrx) do | |
580 attr[a] = v; | |
581 end | |
582 setmetatable(stanza, stanza_mt); | |
583 for _, child in ipairs(stanza) do | |
584 if type(child) == "table" then | |
585 deserialize(child); | |
586 end | |
587 end | |
588 if not stanza.tags then | |
589 -- Rebuild tags | |
590 local tags = {}; | |
591 for _, child in ipairs(stanza) do | |
592 if type(child) == "table" then | |
593 t_insert(tags, child); | |
594 end | |
595 end | |
596 stanza.tags = tags; | |
597 end | |
598 end | |
599 | |
600 return stanza; | |
601 end | |
602 | |
603 local function _clone(stanza) | |
604 local attr, tags = {}, {}; | |
605 for k,v in pairs(stanza.attr) do attr[k] = v; end | |
606 local new = { name = stanza.name, attr = attr, tags = tags }; | |
607 for i=1,#stanza do | |
608 local child = stanza[i]; | |
609 if child.name then | |
610 child = _clone(child); | |
611 t_insert(tags, child); | |
612 end | |
613 t_insert(new, child); | |
614 end | |
615 return setmetatable(new, stanza_mt); | |
616 end | |
617 clone = _clone; | |
618 | |
619 function message(attr, body) | |
620 if not body then | |
621 return stanza("message", attr); | |
622 else | |
623 return stanza("message", attr):tag("body"):text(body):up(); | |
624 end | |
625 end | |
626 function iq(attr) | |
627 if attr and not attr.id then attr.id = new_id(); end | |
628 return stanza("iq", attr or { id = new_id() }); | |
629 end | |
630 | |
631 function reply(orig) | |
632 return stanza(orig.name, orig.attr and { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, type = ((orig.name == "iq" and "result") or orig.attr.type) }); | |
633 end | |
634 | |
635 do | |
636 local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; | |
637 function error_reply(orig, type, condition, message) | |
638 local t = reply(orig); | |
639 t.attr.type = "error"; | |
640 t:tag("error", {type = type}) --COMPAT: Some day xmlns:stanzas goes here | |
641 :tag(condition, xmpp_stanzas_attr):up(); | |
642 if (message) then t:tag("text", xmpp_stanzas_attr):text(message):up(); end | |
643 return t; -- stanza ready for adding app-specific errors | |
644 end | |
645 end | |
646 | |
647 function presence(attr) | |
648 return stanza("presence", attr); | |
649 end | |
650 | |
651 if do_pretty_printing then | |
652 local style_attrk = getstyle("yellow"); | |
653 local style_attrv = getstyle("red"); | |
654 local style_tagname = getstyle("red"); | |
655 local style_punc = getstyle("magenta"); | |
656 | |
657 local attr_format = " "..getstring(style_attrk, "%s")..getstring(style_punc, "=")..getstring(style_attrv, "'%s'"); | |
658 local top_tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">"); | |
659 --local tag_format = getstring(style_punc, "<")..getstring(style_tagname, "%s").."%s"..getstring(style_punc, ">").."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">"); | |
660 local tag_format = top_tag_format.."%s"..getstring(style_punc, "</")..getstring(style_tagname, "%s")..getstring(style_punc, ">"); | |
661 function stanza_mt.pretty_print(t) | |
662 local children_text = ""; | |
663 for n, child in ipairs(t) do | |
664 if type(child) == "string" then | |
665 children_text = children_text .. xml_escape(child); | |
666 else | |
667 children_text = children_text .. child:pretty_print(); | |
668 end | |
669 end | |
670 | |
671 local attr_string = ""; | |
672 if t.attr then | |
673 for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(attr_format, k, tostring(v)); end end | |
674 end | |
675 return s_format(tag_format, t.name, attr_string, children_text, t.name); | |
676 end | |
677 | |
678 function stanza_mt.pretty_top_tag(t) | |
679 local attr_string = ""; | |
680 if t.attr then | |
681 for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(attr_format, k, tostring(v)); end end | |
682 end | |
683 return s_format(top_tag_format, t.name, attr_string); | |
684 end | |
685 else | |
686 -- Sorry, fresh out of colours for you guys ;) | |
687 stanza_mt.pretty_print = stanza_mt.__tostring; | |
688 stanza_mt.pretty_top_tag = stanza_mt.top_tag; | |
689 end | |
690 | |
691 return _M; | |
692 end) | |
693 package.preload['util.timer'] = (function (...) | |
694 -- Prosody IM | |
695 -- Copyright (C) 2008-2010 Matthew Wild | |
696 -- Copyright (C) 2008-2010 Waqas Hussain | |
697 -- | |
698 -- This project is MIT/X11 licensed. Please see the | |
699 -- COPYING file in the source package for more information. | |
700 -- | |
701 | |
702 | |
703 local ns_addtimer = require "net.server".addtimer; | |
704 local event = require "net.server".event; | |
705 local event_base = require "net.server".event_base; | |
706 | |
707 local math_min = math.min | |
708 local math_huge = math.huge | |
709 local get_time = require "socket".gettime; | |
710 local t_insert = table.insert; | |
711 local t_remove = table.remove; | |
712 local ipairs, pairs = ipairs, pairs; | |
713 local type = type; | |
714 | |
715 local data = {}; | |
716 local new_data = {}; | |
717 | |
718 module "timer" | |
719 | |
720 local _add_task; | |
721 if not event then | |
722 function _add_task(delay, func) | |
723 local current_time = get_time(); | |
724 delay = delay + current_time; | |
725 if delay >= current_time then | |
726 t_insert(new_data, {delay, func}); | |
727 else | |
728 func(); | |
729 end | |
730 end | |
731 | |
732 ns_addtimer(function() | |
733 local current_time = get_time(); | |
734 if #new_data > 0 then | |
735 for _, d in pairs(new_data) do | |
736 t_insert(data, d); | |
737 end | |
738 new_data = {}; | |
739 end | |
740 | |
741 local next_time = math_huge; | |
742 for i, d in pairs(data) do | |
743 local t, func = d[1], d[2]; | |
744 if t <= current_time then | |
745 data[i] = nil; | |
746 local r = func(current_time); | |
747 if type(r) == "number" then | |
748 _add_task(r, func); | |
749 next_time = math_min(next_time, r); | |
750 end | |
751 else | |
752 next_time = math_min(next_time, t - current_time); | |
753 end | |
754 end | |
755 return next_time; | |
756 end); | |
757 else | |
758 local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1; | |
759 function _add_task(delay, func) | |
760 local event_handle; | |
761 event_handle = event_base:addevent(nil, 0, function () | |
762 local ret = func(); | |
763 if ret then | |
764 return 0, ret; | |
765 elseif event_handle then | |
766 return EVENT_LEAVE; | |
767 end | |
768 end | |
769 , delay); | |
770 end | |
771 end | |
772 | |
773 add_task = _add_task; | |
774 | |
775 return _M; | |
776 end) | |
777 package.preload['util.termcolours'] = (function (...) | |
778 -- Prosody IM | |
779 -- Copyright (C) 2008-2010 Matthew Wild | |
780 -- Copyright (C) 2008-2010 Waqas Hussain | |
781 -- | |
782 -- This project is MIT/X11 licensed. Please see the | |
783 -- COPYING file in the source package for more information. | |
784 -- | |
785 | |
786 | |
787 local t_concat, t_insert = table.concat, table.insert; | |
788 local char, format = string.char, string.format; | |
789 local ipairs = ipairs; | |
790 local io_write = io.write; | |
791 | |
792 local windows; | |
793 if os.getenv("WINDIR") then | |
794 windows = require "util.windows"; | |
795 end | |
796 local orig_color = windows and windows.get_consolecolor and windows.get_consolecolor(); | |
797 | |
798 module "termcolours" | |
799 | |
800 local stylemap = { | |
801 reset = 0; bright = 1, dim = 2, underscore = 4, blink = 5, reverse = 7, hidden = 8; | |
802 black = 30; red = 31; green = 32; yellow = 33; blue = 34; magenta = 35; cyan = 36; white = 37; | |
803 ["black background"] = 40; ["red background"] = 41; ["green background"] = 42; ["yellow background"] = 43; ["blue background"] = 44; ["magenta background"] = 45; ["cyan background"] = 46; ["white background"] = 47; | |
804 bold = 1, dark = 2, underline = 4, underlined = 4, normal = 0; | |
805 } | |
806 | |
807 local winstylemap = { | |
808 ["0"] = orig_color, -- reset | |
809 ["1"] = 7+8, -- bold | |
810 ["1;33"] = 2+4+8, -- bold yellow | |
811 ["1;31"] = 4+8 -- bold red | |
812 } | |
813 | |
814 local fmt_string = char(0x1B).."[%sm%s"..char(0x1B).."[0m"; | |
815 function getstring(style, text) | |
816 if style then | |
817 return format(fmt_string, style, text); | |
818 else | |
819 return text; | |
820 end | |
821 end | |
822 | |
823 function getstyle(...) | |
824 local styles, result = { ... }, {}; | |
825 for i, style in ipairs(styles) do | |
826 style = stylemap[style]; | |
827 if style then | |
828 t_insert(result, style); | |
829 end | |
830 end | |
831 return t_concat(result, ";"); | |
832 end | |
833 | |
834 local last = "0"; | |
835 function setstyle(style) | |
836 style = style or "0"; | |
837 if style ~= last then | |
838 io_write("\27["..style.."m"); | |
839 last = style; | |
840 end | |
841 end | |
842 | |
843 if windows then | |
844 function setstyle(style) | |
845 style = style or "0"; | |
846 if style ~= last then | |
847 windows.set_consolecolor(winstylemap[style] or orig_color); | |
848 last = style; | |
849 end | |
850 end | |
851 if not orig_color then | |
852 function setstyle(style) end | |
853 end | |
854 end | |
855 | |
856 return _M; | |
857 end) | |
858 package.preload['util.uuid'] = (function (...) | |
859 -- Prosody IM | |
860 -- Copyright (C) 2008-2010 Matthew Wild | |
861 -- Copyright (C) 2008-2010 Waqas Hussain | |
862 -- | |
863 -- This project is MIT/X11 licensed. Please see the | |
864 -- COPYING file in the source package for more information. | |
865 -- | |
866 | |
867 | |
868 local m_random = math.random; | |
869 local tostring = tostring; | |
870 local os_time = os.time; | |
871 local os_clock = os.clock; | |
872 local sha1 = require "util.hashes".sha1; | |
873 | |
874 module "uuid" | |
875 | |
876 local last_uniq_time = 0; | |
877 local function uniq_time() | |
878 local new_uniq_time = os_time(); | |
879 if last_uniq_time >= new_uniq_time then new_uniq_time = last_uniq_time + 1; end | |
880 last_uniq_time = new_uniq_time; | |
881 return new_uniq_time; | |
882 end | |
883 | |
884 local function new_random(x) | |
885 return sha1(x..os_clock()..tostring({}), true); | |
886 end | |
887 | |
888 local buffer = new_random(uniq_time()); | |
889 local function _seed(x) | |
890 buffer = new_random(buffer..x); | |
891 end | |
892 local function get_nibbles(n) | |
893 if #buffer < n then _seed(uniq_time()); end | |
894 local r = buffer:sub(0, n); | |
895 buffer = buffer:sub(n+1); | |
896 return r; | |
897 end | |
898 local function get_twobits() | |
899 return ("%x"):format(get_nibbles(1):byte() % 4 + 8); | |
900 end | |
901 | |
902 function generate() | |
903 -- generate RFC 4122 complaint UUIDs (version 4 - random) | |
904 return get_nibbles(8).."-"..get_nibbles(4).."-4"..get_nibbles(3).."-"..(get_twobits())..get_nibbles(3).."-"..get_nibbles(12); | |
905 end | |
906 seed = _seed; | |
907 | |
908 return _M; | |
909 end) | |
910 package.preload['net.dns'] = (function (...) | |
911 -- Prosody IM | |
912 -- This file is included with Prosody IM. It has modifications, | |
913 -- which are hereby placed in the public domain. | |
914 | |
915 | |
916 -- todo: quick (default) header generation | |
917 -- todo: nxdomain, error handling | |
918 -- todo: cache results of encodeName | |
919 | |
920 | |
921 -- reference: http://tools.ietf.org/html/rfc1035 | |
922 -- reference: http://tools.ietf.org/html/rfc1876 (LOC) | |
923 | |
924 | |
925 local socket = require "socket"; | |
926 local timer = require "util.timer"; | |
927 | |
928 local _, windows = pcall(require, "util.windows"); | |
929 local is_windows = (_ and windows) or os.getenv("WINDIR"); | |
930 | |
931 local coroutine, io, math, string, table = | |
932 coroutine, io, math, string, table; | |
933 | |
934 local ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select, type= | |
935 ipairs, next, pairs, print, setmetatable, tostring, assert, error, unpack, select, type; | |
936 | |
937 local ztact = { -- public domain 20080404 lua@ztact.com | |
938 get = function(parent, ...) | |
939 local len = select('#', ...); | |
940 for i=1,len do | |
941 parent = parent[select(i, ...)]; | |
942 if parent == nil then break; end | |
943 end | |
944 return parent; | |
945 end; | |
946 set = function(parent, ...) | |
947 local len = select('#', ...); | |
948 local key, value = select(len-1, ...); | |
949 local cutpoint, cutkey; | |
950 | |
951 for i=1,len-2 do | |
952 local key = select (i, ...) | |
953 local child = parent[key] | |
954 | |
955 if value == nil then | |
956 if child == nil then | |
957 return; | |
958 elseif next(child, next(child)) then | |
959 cutpoint = nil; cutkey = nil; | |
960 elseif cutpoint == nil then | |
961 cutpoint = parent; cutkey = key; | |
962 end | |
963 elseif child == nil then | |
964 child = {}; | |
965 parent[key] = child; | |
966 end | |
967 parent = child | |
968 end | |
969 | |
970 if value == nil and cutpoint then | |
971 cutpoint[cutkey] = nil; | |
972 else | |
973 parent[key] = value; | |
974 return value; | |
975 end | |
976 end; | |
977 }; | |
978 local get, set = ztact.get, ztact.set; | |
979 | |
980 local default_timeout = 15; | |
981 | |
982 -------------------------------------------------- module dns | |
983 module('dns') | |
984 local dns = _M; | |
985 | |
986 | |
987 -- dns type & class codes ------------------------------ dns type & class codes | |
988 | |
989 | |
990 local append = table.insert | |
991 | |
992 | |
993 local function highbyte(i) -- - - - - - - - - - - - - - - - - - - highbyte | |
994 return (i-(i%0x100))/0x100; | |
995 end | |
996 | |
997 | |
998 local function augment (t) -- - - - - - - - - - - - - - - - - - - - augment | |
999 local a = {}; | |
1000 for i,s in pairs(t) do | |
1001 a[i] = s; | |
1002 a[s] = s; | |
1003 a[string.lower(s)] = s; | |
1004 end | |
1005 return a; | |
1006 end | |
1007 | |
1008 | |
1009 local function encode (t) -- - - - - - - - - - - - - - - - - - - - - encode | |
1010 local code = {}; | |
1011 for i,s in pairs(t) do | |
1012 local word = string.char(highbyte(i), i%0x100); | |
1013 code[i] = word; | |
1014 code[s] = word; | |
1015 code[string.lower(s)] = word; | |
1016 end | |
1017 return code; | |
1018 end | |
1019 | |
1020 | |
1021 dns.types = { | |
1022 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR', 'NULL', 'WKS', | |
1023 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT', | |
1024 [ 28] = 'AAAA', [ 29] = 'LOC', [ 33] = 'SRV', | |
1025 [252] = 'AXFR', [253] = 'MAILB', [254] = 'MAILA', [255] = '*' }; | |
1026 | |
1027 | |
1028 dns.classes = { 'IN', 'CS', 'CH', 'HS', [255] = '*' }; | |
1029 | |
1030 | |
1031 dns.type = augment (dns.types); | |
1032 dns.class = augment (dns.classes); | |
1033 dns.typecode = encode (dns.types); | |
1034 dns.classcode = encode (dns.classes); | |
1035 | |
1036 | |
1037 | |
1038 local function standardize(qname, qtype, qclass) -- - - - - - - standardize | |
1039 if string.byte(qname, -1) ~= 0x2E then qname = qname..'.'; end | |
1040 qname = string.lower(qname); | |
1041 return qname, dns.type[qtype or 'A'], dns.class[qclass or 'IN']; | |
1042 end | |
1043 | |
1044 | |
1045 local function prune(rrs, time, soft) -- - - - - - - - - - - - - - - prune | |
1046 time = time or socket.gettime(); | |
1047 for i,rr in pairs(rrs) do | |
1048 if rr.tod then | |
1049 -- rr.tod = rr.tod - 50 -- accelerated decripitude | |
1050 rr.ttl = math.floor(rr.tod - time); | |
1051 if rr.ttl <= 0 then | |
1052 table.remove(rrs, i); | |
1053 return prune(rrs, time, soft); -- Re-iterate | |
1054 end | |
1055 elseif soft == 'soft' then -- What is this? I forget! | |
1056 assert(rr.ttl == 0); | |
1057 rrs[i] = nil; | |
1058 end | |
1059 end | |
1060 end | |
1061 | |
1062 | |
1063 -- metatables & co. ------------------------------------------ metatables & co. | |
1064 | |
1065 | |
1066 local resolver = {}; | |
1067 resolver.__index = resolver; | |
1068 | |
1069 resolver.timeout = default_timeout; | |
1070 | |
1071 local function default_rr_tostring(rr) | |
1072 local rr_val = rr.type and rr[rr.type:lower()]; | |
1073 if type(rr_val) ~= "string" then | |
1074 return "<UNKNOWN RDATA TYPE>"; | |
1075 end | |
1076 return rr_val; | |
1077 end | |
1078 | |
1079 local special_tostrings = { | |
1080 LOC = resolver.LOC_tostring; | |
1081 MX = function (rr) | |
1082 return string.format('%2i %s', rr.pref, rr.mx); | |
1083 end; | |
1084 SRV = function (rr) | |
1085 local s = rr.srv; | |
1086 return string.format('%5d %5d %5d %s', s.priority, s.weight, s.port, s.target); | |
1087 end; | |
1088 }; | |
1089 | |
1090 local rr_metatable = {}; -- - - - - - - - - - - - - - - - - - - rr_metatable | |
1091 function rr_metatable.__tostring(rr) | |
1092 local rr_string = (special_tostrings[rr.type] or default_rr_tostring)(rr); | |
1093 return string.format('%2s %-5s %6i %-28s %s', rr.class, rr.type, rr.ttl, rr.name, rr_string); | |
1094 end | |
1095 | |
1096 | |
1097 local rrs_metatable = {}; -- - - - - - - - - - - - - - - - - - rrs_metatable | |
1098 function rrs_metatable.__tostring(rrs) | |
1099 local t = {}; | |
1100 for i,rr in pairs(rrs) do | |
1101 append(t, tostring(rr)..'\n'); | |
1102 end | |
1103 return table.concat(t); | |
1104 end | |
1105 | |
1106 | |
1107 local cache_metatable = {}; -- - - - - - - - - - - - - - - - cache_metatable | |
1108 function cache_metatable.__tostring(cache) | |
1109 local time = socket.gettime(); | |
1110 local t = {}; | |
1111 for class,types in pairs(cache) do | |
1112 for type,names in pairs(types) do | |
1113 for name,rrs in pairs(names) do | |
1114 prune(rrs, time); | |
1115 append(t, tostring(rrs)); | |
1116 end | |
1117 end | |
1118 end | |
1119 return table.concat(t); | |
1120 end | |
1121 | |
1122 | |
1123 function resolver:new() -- - - - - - - - - - - - - - - - - - - - - resolver | |
1124 local r = { active = {}, cache = {}, unsorted = {} }; | |
1125 setmetatable(r, resolver); | |
1126 setmetatable(r.cache, cache_metatable); | |
1127 setmetatable(r.unsorted, { __mode = 'kv' }); | |
1128 return r; | |
1129 end | |
1130 | |
1131 | |
1132 -- packet layer -------------------------------------------------- packet layer | |
1133 | |
1134 | |
1135 function dns.random(...) -- - - - - - - - - - - - - - - - - - - dns.random | |
1136 math.randomseed(math.floor(10000*socket.gettime())); | |
1137 dns.random = math.random; | |
1138 return dns.random(...); | |
1139 end | |
1140 | |
1141 | |
1142 local function encodeHeader(o) -- - - - - - - - - - - - - - - encodeHeader | |
1143 o = o or {}; | |
1144 o.id = o.id or dns.random(0, 0xffff); -- 16b (random) id | |
1145 | |
1146 o.rd = o.rd or 1; -- 1b 1 recursion desired | |
1147 o.tc = o.tc or 0; -- 1b 1 truncated response | |
1148 o.aa = o.aa or 0; -- 1b 1 authoritative response | |
1149 o.opcode = o.opcode or 0; -- 4b 0 query | |
1150 -- 1 inverse query | |
1151 -- 2 server status request | |
1152 -- 3-15 reserved | |
1153 o.qr = o.qr or 0; -- 1b 0 query, 1 response | |
1154 | |
1155 o.rcode = o.rcode or 0; -- 4b 0 no error | |
1156 -- 1 format error | |
1157 -- 2 server failure | |
1158 -- 3 name error | |
1159 -- 4 not implemented | |
1160 -- 5 refused | |
1161 -- 6-15 reserved | |
1162 o.z = o.z or 0; -- 3b 0 resvered | |
1163 o.ra = o.ra or 0; -- 1b 1 recursion available | |
1164 | |
1165 o.qdcount = o.qdcount or 1; -- 16b number of question RRs | |
1166 o.ancount = o.ancount or 0; -- 16b number of answers RRs | |
1167 o.nscount = o.nscount or 0; -- 16b number of nameservers RRs | |
1168 o.arcount = o.arcount or 0; -- 16b number of additional RRs | |
1169 | |
1170 -- string.char() rounds, so prevent roundup with -0.4999 | |
1171 local header = string.char( | |
1172 highbyte(o.id), o.id %0x100, | |
1173 o.rd + 2*o.tc + 4*o.aa + 8*o.opcode + 128*o.qr, | |
1174 o.rcode + 16*o.z + 128*o.ra, | |
1175 highbyte(o.qdcount), o.qdcount %0x100, | |
1176 highbyte(o.ancount), o.ancount %0x100, | |
1177 highbyte(o.nscount), o.nscount %0x100, | |
1178 highbyte(o.arcount), o.arcount %0x100 | |
1179 ); | |
1180 | |
1181 return header, o.id; | |
1182 end | |
1183 | |
1184 | |
1185 local function encodeName(name) -- - - - - - - - - - - - - - - - encodeName | |
1186 local t = {}; | |
1187 for part in string.gmatch(name, '[^.]+') do | |
1188 append(t, string.char(string.len(part))); | |
1189 append(t, part); | |
1190 end | |
1191 append(t, string.char(0)); | |
1192 return table.concat(t); | |
1193 end | |
1194 | |
1195 | |
1196 local function encodeQuestion(qname, qtype, qclass) -- - - - encodeQuestion | |
1197 qname = encodeName(qname); | |
1198 qtype = dns.typecode[qtype or 'a']; | |
1199 qclass = dns.classcode[qclass or 'in']; | |
1200 return qname..qtype..qclass; | |
1201 end | |
1202 | |
1203 | |
1204 function resolver:byte(len) -- - - - - - - - - - - - - - - - - - - - - byte | |
1205 len = len or 1; | |
1206 local offset = self.offset; | |
1207 local last = offset + len - 1; | |
1208 if last > #self.packet then | |
1209 error(string.format('out of bounds: %i>%i', last, #self.packet)); | |
1210 end | |
1211 self.offset = offset + len; | |
1212 return string.byte(self.packet, offset, last); | |
1213 end | |
1214 | |
1215 | |
1216 function resolver:word() -- - - - - - - - - - - - - - - - - - - - - - word | |
1217 local b1, b2 = self:byte(2); | |
1218 return 0x100*b1 + b2; | |
1219 end | |
1220 | |
1221 | |
1222 function resolver:dword () -- - - - - - - - - - - - - - - - - - - - - dword | |
1223 local b1, b2, b3, b4 = self:byte(4); | |
1224 --print('dword', b1, b2, b3, b4); | |
1225 return 0x1000000*b1 + 0x10000*b2 + 0x100*b3 + b4; | |
1226 end | |
1227 | |
1228 | |
1229 function resolver:sub(len) -- - - - - - - - - - - - - - - - - - - - - - sub | |
1230 len = len or 1; | |
1231 local s = string.sub(self.packet, self.offset, self.offset + len - 1); | |
1232 self.offset = self.offset + len; | |
1233 return s; | |
1234 end | |
1235 | |
1236 | |
1237 function resolver:header(force) -- - - - - - - - - - - - - - - - - - header | |
1238 local id = self:word(); | |
1239 --print(string.format(':header id %x', id)); | |
1240 if not self.active[id] and not force then return nil; end | |
1241 | |
1242 local h = { id = id }; | |
1243 | |
1244 local b1, b2 = self:byte(2); | |
1245 | |
1246 h.rd = b1 %2; | |
1247 h.tc = b1 /2%2; | |
1248 h.aa = b1 /4%2; | |
1249 h.opcode = b1 /8%16; | |
1250 h.qr = b1 /128; | |
1251 | |
1252 h.rcode = b2 %16; | |
1253 h.z = b2 /16%8; | |
1254 h.ra = b2 /128; | |
1255 | |
1256 h.qdcount = self:word(); | |
1257 h.ancount = self:word(); | |
1258 h.nscount = self:word(); | |
1259 h.arcount = self:word(); | |
1260 | |
1261 for k,v in pairs(h) do h[k] = v-v%1; end | |
1262 | |
1263 return h; | |
1264 end | |
1265 | |
1266 | |
1267 function resolver:name() -- - - - - - - - - - - - - - - - - - - - - - name | |
1268 local remember, pointers = nil, 0; | |
1269 local len = self:byte(); | |
1270 local n = {}; | |
1271 while len > 0 do | |
1272 if len >= 0xc0 then -- name is "compressed" | |
1273 pointers = pointers + 1; | |
1274 if pointers >= 20 then error('dns error: 20 pointers'); end; | |
1275 local offset = ((len-0xc0)*0x100) + self:byte(); | |
1276 remember = remember or self.offset; | |
1277 self.offset = offset + 1; -- +1 for lua | |
1278 else -- name is not compressed | |
1279 append(n, self:sub(len)..'.'); | |
1280 end | |
1281 len = self:byte(); | |
1282 end | |
1283 self.offset = remember or self.offset; | |
1284 return table.concat(n); | |
1285 end | |
1286 | |
1287 | |
1288 function resolver:question() -- - - - - - - - - - - - - - - - - - question | |
1289 local q = {}; | |
1290 q.name = self:name(); | |
1291 q.type = dns.type[self:word()]; | |
1292 q.class = dns.class[self:word()]; | |
1293 return q; | |
1294 end | |
1295 | |
1296 | |
1297 function resolver:A(rr) -- - - - - - - - - - - - - - - - - - - - - - - - A | |
1298 local b1, b2, b3, b4 = self:byte(4); | |
1299 rr.a = string.format('%i.%i.%i.%i', b1, b2, b3, b4); | |
1300 end | |
1301 | |
1302 function resolver:AAAA(rr) | |
1303 local addr = {}; | |
1304 for i = 1, rr.rdlength, 2 do | |
1305 local b1, b2 = self:byte(2); | |
1306 table.insert(addr, ("%02x%02x"):format(b1, b2)); | |
1307 end | |
1308 rr.aaaa = table.concat(addr, ":"); | |
1309 end | |
1310 | |
1311 function resolver:CNAME(rr) -- - - - - - - - - - - - - - - - - - - - CNAME | |
1312 rr.cname = self:name(); | |
1313 end | |
1314 | |
1315 | |
1316 function resolver:MX(rr) -- - - - - - - - - - - - - - - - - - - - - - - MX | |
1317 rr.pref = self:word(); | |
1318 rr.mx = self:name(); | |
1319 end | |
1320 | |
1321 | |
1322 function resolver:LOC_nibble_power() -- - - - - - - - - - LOC_nibble_power | |
1323 local b = self:byte(); | |
1324 --print('nibbles', ((b-(b%0x10))/0x10), (b%0x10)); | |
1325 return ((b-(b%0x10))/0x10) * (10^(b%0x10)); | |
1326 end | |
1327 | |
1328 | |
1329 function resolver:LOC(rr) -- - - - - - - - - - - - - - - - - - - - - - LOC | |
1330 rr.version = self:byte(); | |
1331 if rr.version == 0 then | |
1332 rr.loc = rr.loc or {}; | |
1333 rr.loc.size = self:LOC_nibble_power(); | |
1334 rr.loc.horiz_pre = self:LOC_nibble_power(); | |
1335 rr.loc.vert_pre = self:LOC_nibble_power(); | |
1336 rr.loc.latitude = self:dword(); | |
1337 rr.loc.longitude = self:dword(); | |
1338 rr.loc.altitude = self:dword(); | |
1339 end | |
1340 end | |
1341 | |
1342 | |
1343 local function LOC_tostring_degrees(f, pos, neg) -- - - - - - - - - - - - - | |
1344 f = f - 0x80000000; | |
1345 if f < 0 then pos = neg; f = -f; end | |
1346 local deg, min, msec; | |
1347 msec = f%60000; | |
1348 f = (f-msec)/60000; | |
1349 min = f%60; | |
1350 deg = (f-min)/60; | |
1351 return string.format('%3d %2d %2.3f %s', deg, min, msec/1000, pos); | |
1352 end | |
1353 | |
1354 | |
1355 function resolver.LOC_tostring(rr) -- - - - - - - - - - - - - LOC_tostring | |
1356 local t = {}; | |
1357 | |
1358 --[[ | |
1359 for k,name in pairs { 'size', 'horiz_pre', 'vert_pre', 'latitude', 'longitude', 'altitude' } do | |
1360 append(t, string.format('%4s%-10s: %12.0f\n', '', name, rr.loc[name])); | |
1361 end | |
1362 --]] | |
1363 | |
1364 append(t, string.format( | |
1365 '%s %s %.2fm %.2fm %.2fm %.2fm', | |
1366 LOC_tostring_degrees (rr.loc.latitude, 'N', 'S'), | |
1367 LOC_tostring_degrees (rr.loc.longitude, 'E', 'W'), | |
1368 (rr.loc.altitude - 10000000) / 100, | |
1369 rr.loc.size / 100, | |
1370 rr.loc.horiz_pre / 100, | |
1371 rr.loc.vert_pre / 100 | |
1372 )); | |
1373 | |
1374 return table.concat(t); | |
1375 end | |
1376 | |
1377 | |
1378 function resolver:NS(rr) -- - - - - - - - - - - - - - - - - - - - - - - NS | |
1379 rr.ns = self:name(); | |
1380 end | |
1381 | |
1382 | |
1383 function resolver:SOA(rr) -- - - - - - - - - - - - - - - - - - - - - - SOA | |
1384 end | |
1385 | |
1386 | |
1387 function resolver:SRV(rr) -- - - - - - - - - - - - - - - - - - - - - - SRV | |
1388 rr.srv = {}; | |
1389 rr.srv.priority = self:word(); | |
1390 rr.srv.weight = self:word(); | |
1391 rr.srv.port = self:word(); | |
1392 rr.srv.target = self:name(); | |
1393 end | |
1394 | |
1395 function resolver:PTR(rr) | |
1396 rr.ptr = self:name(); | |
1397 end | |
1398 | |
1399 function resolver:TXT(rr) -- - - - - - - - - - - - - - - - - - - - - - TXT | |
1400 rr.txt = self:sub (self:byte()); | |
1401 end | |
1402 | |
1403 | |
1404 function resolver:rr() -- - - - - - - - - - - - - - - - - - - - - - - - rr | |
1405 local rr = {}; | |
1406 setmetatable(rr, rr_metatable); | |
1407 rr.name = self:name(self); | |
1408 rr.type = dns.type[self:word()] or rr.type; | |
1409 rr.class = dns.class[self:word()] or rr.class; | |
1410 rr.ttl = 0x10000*self:word() + self:word(); | |
1411 rr.rdlength = self:word(); | |
1412 | |
1413 if rr.ttl <= 0 then | |
1414 rr.tod = self.time + 30; | |
1415 else | |
1416 rr.tod = self.time + rr.ttl; | |
1417 end | |
1418 | |
1419 local remember = self.offset; | |
1420 local rr_parser = self[dns.type[rr.type]]; | |
1421 if rr_parser then rr_parser(self, rr); end | |
1422 self.offset = remember; | |
1423 rr.rdata = self:sub(rr.rdlength); | |
1424 return rr; | |
1425 end | |
1426 | |
1427 | |
1428 function resolver:rrs (count) -- - - - - - - - - - - - - - - - - - - - - rrs | |
1429 local rrs = {}; | |
1430 for i = 1,count do append(rrs, self:rr()); end | |
1431 return rrs; | |
1432 end | |
1433 | |
1434 | |
1435 function resolver:decode(packet, force) -- - - - - - - - - - - - - - decode | |
1436 self.packet, self.offset = packet, 1; | |
1437 local header = self:header(force); | |
1438 if not header then return nil; end | |
1439 local response = { header = header }; | |
1440 | |
1441 response.question = {}; | |
1442 local offset = self.offset; | |
1443 for i = 1,response.header.qdcount do | |
1444 append(response.question, self:question()); | |
1445 end | |
1446 response.question.raw = string.sub(self.packet, offset, self.offset - 1); | |
1447 | |
1448 if not force then | |
1449 if not self.active[response.header.id] or not self.active[response.header.id][response.question.raw] then | |
1450 return nil; | |
1451 end | |
1452 end | |
1453 | |
1454 response.answer = self:rrs(response.header.ancount); | |
1455 response.authority = self:rrs(response.header.nscount); | |
1456 response.additional = self:rrs(response.header.arcount); | |
1457 | |
1458 return response; | |
1459 end | |
1460 | |
1461 | |
1462 -- socket layer -------------------------------------------------- socket layer | |
1463 | |
1464 | |
1465 resolver.delays = { 1, 3 }; | |
1466 | |
1467 | |
1468 function resolver:addnameserver(address) -- - - - - - - - - - addnameserver | |
1469 self.server = self.server or {}; | |
1470 append(self.server, address); | |
1471 end | |
1472 | |
1473 | |
1474 function resolver:setnameserver(address) -- - - - - - - - - - setnameserver | |
1475 self.server = {}; | |
1476 self:addnameserver(address); | |
1477 end | |
1478 | |
1479 | |
1480 function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers | |
1481 if is_windows then | |
1482 if windows and windows.get_nameservers then | |
1483 for _, server in ipairs(windows.get_nameservers()) do | |
1484 self:addnameserver(server); | |
1485 end | |
1486 end | |
1487 if not self.server or #self.server == 0 then | |
1488 -- TODO log warning about no nameservers, adding opendns servers as fallback | |
1489 self:addnameserver("208.67.222.222"); | |
1490 self:addnameserver("208.67.220.220"); | |
1491 end | |
1492 else -- posix | |
1493 local resolv_conf = io.open("/etc/resolv.conf"); | |
1494 if resolv_conf then | |
1495 for line in resolv_conf:lines() do | |
1496 line = line:gsub("#.*$", "") | |
1497 :match('^%s*nameserver%s+(.*)%s*$'); | |
1498 if line then | |
1499 line:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]", function (address) | |
1500 self:addnameserver(address) | |
1501 end); | |
1502 end | |
1503 end | |
1504 end | |
1505 if not self.server or #self.server == 0 then | |
1506 -- TODO log warning about no nameservers, adding localhost as the default nameserver | |
1507 self:addnameserver("127.0.0.1"); | |
1508 end | |
1509 end | |
1510 end | |
1511 | |
1512 | |
1513 function resolver:getsocket(servernum) -- - - - - - - - - - - - - getsocket | |
1514 self.socket = self.socket or {}; | |
1515 self.socketset = self.socketset or {}; | |
1516 | |
1517 local sock = self.socket[servernum]; | |
1518 if sock then return sock; end | |
1519 | |
1520 local err; | |
1521 sock, err = socket.udp(); | |
1522 if not sock then | |
1523 return nil, err; | |
1524 end | |
1525 if self.socket_wrapper then sock = self.socket_wrapper(sock, self); end | |
1526 sock:settimeout(0); | |
1527 -- todo: attempt to use a random port, fallback to 0 | |
1528 sock:setsockname('*', 0); | |
1529 sock:setpeername(self.server[servernum], 53); | |
1530 self.socket[servernum] = sock; | |
1531 self.socketset[sock] = servernum; | |
1532 return sock; | |
1533 end | |
1534 | |
1535 function resolver:voidsocket(sock) | |
1536 if self.socket[sock] then | |
1537 self.socketset[self.socket[sock]] = nil; | |
1538 self.socket[sock] = nil; | |
1539 elseif self.socketset[sock] then | |
1540 self.socket[self.socketset[sock]] = nil; | |
1541 self.socketset[sock] = nil; | |
1542 end | |
1543 end | |
1544 | |
1545 function resolver:socket_wrapper_set(func) -- - - - - - - socket_wrapper_set | |
1546 self.socket_wrapper = func; | |
1547 end | |
1548 | |
1549 | |
1550 function resolver:closeall () -- - - - - - - - - - - - - - - - - - closeall | |
1551 for i,sock in ipairs(self.socket) do | |
1552 self.socket[i] = nil; | |
1553 self.socketset[sock] = nil; | |
1554 sock:close(); | |
1555 end | |
1556 end | |
1557 | |
1558 | |
1559 function resolver:remember(rr, type) -- - - - - - - - - - - - - - remember | |
1560 --print ('remember', type, rr.class, rr.type, rr.name) | |
1561 local qname, qtype, qclass = standardize(rr.name, rr.type, rr.class); | |
1562 | |
1563 if type ~= '*' then | |
1564 type = qtype; | |
1565 local all = get(self.cache, qclass, '*', qname); | |
1566 --print('remember all', all); | |
1567 if all then append(all, rr); end | |
1568 end | |
1569 | |
1570 self.cache = self.cache or setmetatable({}, cache_metatable); | |
1571 local rrs = get(self.cache, qclass, type, qname) or | |
1572 set(self.cache, qclass, type, qname, setmetatable({}, rrs_metatable)); | |
1573 append(rrs, rr); | |
1574 | |
1575 if type == 'MX' then self.unsorted[rrs] = true; end | |
1576 end | |
1577 | |
1578 | |
1579 local function comp_mx(a, b) -- - - - - - - - - - - - - - - - - - - comp_mx | |
1580 return (a.pref == b.pref) and (a.mx < b.mx) or (a.pref < b.pref); | |
1581 end | |
1582 | |
1583 | |
1584 function resolver:peek (qname, qtype, qclass) -- - - - - - - - - - - - peek | |
1585 qname, qtype, qclass = standardize(qname, qtype, qclass); | |
1586 local rrs = get(self.cache, qclass, qtype, qname); | |
1587 if not rrs then return nil; end | |
1588 if prune(rrs, socket.gettime()) and qtype == '*' or not next(rrs) then | |
1589 set(self.cache, qclass, qtype, qname, nil); | |
1590 return nil; | |
1591 end | |
1592 if self.unsorted[rrs] then table.sort (rrs, comp_mx); end | |
1593 return rrs; | |
1594 end | |
1595 | |
1596 | |
1597 function resolver:purge(soft) -- - - - - - - - - - - - - - - - - - - purge | |
1598 if soft == 'soft' then | |
1599 self.time = socket.gettime(); | |
1600 for class,types in pairs(self.cache or {}) do | |
1601 for type,names in pairs(types) do | |
1602 for name,rrs in pairs(names) do | |
1603 prune(rrs, self.time, 'soft') | |
1604 end | |
1605 end | |
1606 end | |
1607 else self.cache = {}; end | |
1608 end | |
1609 | |
1610 | |
1611 function resolver:query(qname, qtype, qclass) -- - - - - - - - - - -- query | |
1612 qname, qtype, qclass = standardize(qname, qtype, qclass) | |
1613 | |
1614 if not self.server then self:adddefaultnameservers(); end | |
1615 | |
1616 local question = encodeQuestion(qname, qtype, qclass); | |
1617 local peek = self:peek (qname, qtype, qclass); | |
1618 if peek then return peek; end | |
1619 | |
1620 local header, id = encodeHeader(); | |
1621 --print ('query id', id, qclass, qtype, qname) | |
1622 local o = { | |
1623 packet = header..question, | |
1624 server = self.best_server, | |
1625 delay = 1, | |
1626 retry = socket.gettime() + self.delays[1] | |
1627 }; | |
1628 | |
1629 -- remember the query | |
1630 self.active[id] = self.active[id] or {}; | |
1631 self.active[id][question] = o; | |
1632 | |
1633 -- remember which coroutine wants the answer | |
1634 local co = coroutine.running(); | |
1635 if co then | |
1636 set(self.wanted, qclass, qtype, qname, co, true); | |
1637 --set(self.yielded, co, qclass, qtype, qname, true); | |
1638 end | |
1639 | |
1640 local conn, err = self:getsocket(o.server) | |
1641 if not conn then | |
1642 return nil, err; | |
1643 end | |
1644 conn:send (o.packet) | |
1645 | |
1646 if timer and self.timeout then | |
1647 local num_servers = #self.server; | |
1648 local i = 1; | |
1649 timer.add_task(self.timeout, function () | |
1650 if get(self.wanted, qclass, qtype, qname, co) then | |
1651 if i < num_servers then | |
1652 i = i + 1; | |
1653 self:servfail(conn); | |
1654 o.server = self.best_server; | |
1655 conn, err = self:getsocket(o.server); | |
1656 if conn then | |
1657 conn:send(o.packet); | |
1658 return self.timeout; | |
1659 end | |
1660 end | |
1661 -- Tried everything, failed | |
1662 self:cancel(qclass, qtype, qname, co, true); | |
1663 end | |
1664 end) | |
1665 end | |
1666 return true; | |
1667 end | |
1668 | |
1669 function resolver:servfail(sock) | |
1670 -- Resend all queries for this server | |
1671 | |
1672 local num = self.socketset[sock] | |
1673 | |
1674 -- Socket is dead now | |
1675 self:voidsocket(sock); | |
1676 | |
1677 -- Find all requests to the down server, and retry on the next server | |
1678 self.time = socket.gettime(); | |
1679 for id,queries in pairs(self.active) do | |
1680 for question,o in pairs(queries) do | |
1681 if o.server == num then -- This request was to the broken server | |
1682 o.server = o.server + 1 -- Use next server | |
1683 if o.server > #self.server then | |
1684 o.server = 1; | |
1685 end | |
1686 | |
1687 o.retries = (o.retries or 0) + 1; | |
1688 if o.retries >= #self.server then | |
1689 --print('timeout'); | |
1690 queries[question] = nil; | |
1691 else | |
1692 local _a = self:getsocket(o.server); | |
1693 if _a then _a:send(o.packet); end | |
1694 end | |
1695 end | |
1696 end | |
1697 end | |
1698 | |
1699 if num == self.best_server then | |
1700 self.best_server = self.best_server + 1; | |
1701 if self.best_server > #self.server then | |
1702 -- Exhausted all servers, try first again | |
1703 self.best_server = 1; | |
1704 end | |
1705 end | |
1706 end | |
1707 | |
1708 function resolver:settimeout(seconds) | |
1709 self.timeout = seconds; | |
1710 end | |
1711 | |
1712 function resolver:receive(rset) -- - - - - - - - - - - - - - - - - receive | |
1713 --print('receive'); print(self.socket); | |
1714 self.time = socket.gettime(); | |
1715 rset = rset or self.socket; | |
1716 | |
1717 local response; | |
1718 for i,sock in pairs(rset) do | |
1719 | |
1720 if self.socketset[sock] then | |
1721 local packet = sock:receive(); | |
1722 if packet then | |
1723 response = self:decode(packet); | |
1724 if response and self.active[response.header.id] | |
1725 and self.active[response.header.id][response.question.raw] then | |
1726 --print('received response'); | |
1727 --self.print(response); | |
1728 | |
1729 for j,rr in pairs(response.answer) do | |
1730 if rr.name:sub(-#response.question[1].name, -1) == response.question[1].name then | |
1731 self:remember(rr, response.question[1].type) | |
1732 end | |
1733 end | |
1734 | |
1735 -- retire the query | |
1736 local queries = self.active[response.header.id]; | |
1737 queries[response.question.raw] = nil; | |
1738 | |
1739 if not next(queries) then self.active[response.header.id] = nil; end | |
1740 if not next(self.active) then self:closeall(); end | |
1741 | |
1742 -- was the query on the wanted list? | |
1743 local q = response.question[1]; | |
1744 local cos = get(self.wanted, q.class, q.type, q.name); | |
1745 if cos then | |
1746 for co in pairs(cos) do | |
1747 set(self.yielded, co, q.class, q.type, q.name, nil); | |
1748 if coroutine.status(co) == "suspended" then coroutine.resume(co); end | |
1749 end | |
1750 set(self.wanted, q.class, q.type, q.name, nil); | |
1751 end | |
1752 end | |
1753 end | |
1754 end | |
1755 end | |
1756 | |
1757 return response; | |
1758 end | |
1759 | |
1760 | |
1761 function resolver:feed(sock, packet, force) | |
1762 --print('receive'); print(self.socket); | |
1763 self.time = socket.gettime(); | |
1764 | |
1765 local response = self:decode(packet, force); | |
1766 if response and self.active[response.header.id] | |
1767 and self.active[response.header.id][response.question.raw] then | |
1768 --print('received response'); | |
1769 --self.print(response); | |
1770 | |
1771 for j,rr in pairs(response.answer) do | |
1772 self:remember(rr, response.question[1].type); | |
1773 end | |
1774 | |
1775 -- retire the query | |
1776 local queries = self.active[response.header.id]; | |
1777 queries[response.question.raw] = nil; | |
1778 if not next(queries) then self.active[response.header.id] = nil; end | |
1779 if not next(self.active) then self:closeall(); end | |
1780 | |
1781 -- was the query on the wanted list? | |
1782 local q = response.question[1]; | |
1783 if q then | |
1784 local cos = get(self.wanted, q.class, q.type, q.name); | |
1785 if cos then | |
1786 for co in pairs(cos) do | |
1787 set(self.yielded, co, q.class, q.type, q.name, nil); | |
1788 if coroutine.status(co) == "suspended" then coroutine.resume(co); end | |
1789 end | |
1790 set(self.wanted, q.class, q.type, q.name, nil); | |
1791 end | |
1792 end | |
1793 end | |
1794 | |
1795 return response; | |
1796 end | |
1797 | |
1798 function resolver:cancel(qclass, qtype, qname, co, call_handler) | |
1799 local cos = get(self.wanted, qclass, qtype, qname); | |
1800 if cos then | |
1801 if call_handler then | |
1802 coroutine.resume(co); | |
1803 end | |
1804 cos[co] = nil; | |
1805 end | |
1806 end | |
1807 | |
1808 function resolver:pulse() -- - - - - - - - - - - - - - - - - - - - - pulse | |
1809 --print(':pulse'); | |
1810 while self:receive() do end | |
1811 if not next(self.active) then return nil; end | |
1812 | |
1813 self.time = socket.gettime(); | |
1814 for id,queries in pairs(self.active) do | |
1815 for question,o in pairs(queries) do | |
1816 if self.time >= o.retry then | |
1817 | |
1818 o.server = o.server + 1; | |
1819 if o.server > #self.server then | |
1820 o.server = 1; | |
1821 o.delay = o.delay + 1; | |
1822 end | |
1823 | |
1824 if o.delay > #self.delays then | |
1825 --print('timeout'); | |
1826 queries[question] = nil; | |
1827 if not next(queries) then self.active[id] = nil; end | |
1828 if not next(self.active) then return nil; end | |
1829 else | |
1830 --print('retry', o.server, o.delay); | |
1831 local _a = self.socket[o.server]; | |
1832 if _a then _a:send(o.packet); end | |
1833 o.retry = self.time + self.delays[o.delay]; | |
1834 end | |
1835 end | |
1836 end | |
1837 end | |
1838 | |
1839 if next(self.active) then return true; end | |
1840 return nil; | |
1841 end | |
1842 | |
1843 | |
1844 function resolver:lookup(qname, qtype, qclass) -- - - - - - - - - - lookup | |
1845 self:query (qname, qtype, qclass) | |
1846 while self:pulse() do | |
1847 local recvt = {} | |
1848 for i, s in ipairs(self.socket) do | |
1849 recvt[i] = s | |
1850 end | |
1851 socket.select(recvt, nil, 4) | |
1852 end | |
1853 --print(self.cache); | |
1854 return self:peek(qname, qtype, qclass); | |
1855 end | |
1856 | |
1857 function resolver:lookupex(handler, qname, qtype, qclass) -- - - - - - - - - - lookup | |
1858 return self:peek(qname, qtype, qclass) or self:query(qname, qtype, qclass); | |
1859 end | |
1860 | |
1861 function resolver:tohostname(ip) | |
1862 return dns.lookup(ip:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)", "%4.%3.%2.%1.in-addr.arpa."), "PTR"); | |
1863 end | |
1864 | |
1865 --print ---------------------------------------------------------------- print | |
1866 | |
1867 | |
1868 local hints = { -- - - - - - - - - - - - - - - - - - - - - - - - - - - hints | |
1869 qr = { [0]='query', 'response' }, | |
1870 opcode = { [0]='query', 'inverse query', 'server status request' }, | |
1871 aa = { [0]='non-authoritative', 'authoritative' }, | |
1872 tc = { [0]='complete', 'truncated' }, | |
1873 rd = { [0]='recursion not desired', 'recursion desired' }, | |
1874 ra = { [0]='recursion not available', 'recursion available' }, | |
1875 z = { [0]='(reserved)' }, | |
1876 rcode = { [0]='no error', 'format error', 'server failure', 'name error', 'not implemented' }, | |
1877 | |
1878 type = dns.type, | |
1879 class = dns.class | |
1880 }; | |
1881 | |
1882 | |
1883 local function hint(p, s) -- - - - - - - - - - - - - - - - - - - - - - hint | |
1884 return (hints[s] and hints[s][p[s]]) or ''; | |
1885 end | |
1886 | |
1887 | |
1888 function resolver.print(response) -- - - - - - - - - - - - - resolver.print | |
1889 for s,s in pairs { 'id', 'qr', 'opcode', 'aa', 'tc', 'rd', 'ra', 'z', | |
1890 'rcode', 'qdcount', 'ancount', 'nscount', 'arcount' } do | |
1891 print( string.format('%-30s', 'header.'..s), response.header[s], hint(response.header, s) ); | |
1892 end | |
1893 | |
1894 for i,question in ipairs(response.question) do | |
1895 print(string.format ('question[%i].name ', i), question.name); | |
1896 print(string.format ('question[%i].type ', i), question.type); | |
1897 print(string.format ('question[%i].class ', i), question.class); | |
1898 end | |
1899 | |
1900 local common = { name=1, type=1, class=1, ttl=1, rdlength=1, rdata=1 }; | |
1901 local tmp; | |
1902 for s,s in pairs({'answer', 'authority', 'additional'}) do | |
1903 for i,rr in pairs(response[s]) do | |
1904 for j,t in pairs({ 'name', 'type', 'class', 'ttl', 'rdlength' }) do | |
1905 tmp = string.format('%s[%i].%s', s, i, t); | |
1906 print(string.format('%-30s', tmp), rr[t], hint(rr, t)); | |
1907 end | |
1908 for j,t in pairs(rr) do | |
1909 if not common[j] then | |
1910 tmp = string.format('%s[%i].%s', s, i, j); | |
1911 print(string.format('%-30s %s', tostring(tmp), tostring(t))); | |
1912 end | |
1913 end | |
1914 end | |
1915 end | |
1916 end | |
1917 | |
1918 | |
1919 -- module api ------------------------------------------------------ module api | |
1920 | |
1921 | |
1922 function dns.resolver () -- - - - - - - - - - - - - - - - - - - - - resolver | |
1923 -- this function seems to be redundant with resolver.new () | |
1924 | |
1925 local r = { active = {}, cache = {}, unsorted = {}, wanted = {}, yielded = {}, best_server = 1 }; | |
1926 setmetatable (r, resolver); | |
1927 setmetatable (r.cache, cache_metatable); | |
1928 setmetatable (r.unsorted, { __mode = 'kv' }); | |
1929 return r; | |
1930 end | |
1931 | |
1932 local _resolver = dns.resolver(); | |
1933 dns._resolver = _resolver; | |
1934 | |
1935 function dns.lookup(...) -- - - - - - - - - - - - - - - - - - - - - lookup | |
1936 return _resolver:lookup(...); | |
1937 end | |
1938 | |
1939 function dns.tohostname(...) | |
1940 return _resolver:tohostname(...); | |
1941 end | |
1942 | |
1943 function dns.purge(...) -- - - - - - - - - - - - - - - - - - - - - - purge | |
1944 return _resolver:purge(...); | |
1945 end | |
1946 | |
1947 function dns.peek(...) -- - - - - - - - - - - - - - - - - - - - - - - peek | |
1948 return _resolver:peek(...); | |
1949 end | |
1950 | |
1951 function dns.query(...) -- - - - - - - - - - - - - - - - - - - - - - query | |
1952 return _resolver:query(...); | |
1953 end | |
1954 | |
1955 function dns.feed(...) -- - - - - - - - - - - - - - - - - - - - - - - feed | |
1956 return _resolver:feed(...); | |
1957 end | |
1958 | |
1959 function dns.cancel(...) -- - - - - - - - - - - - - - - - - - - - - - cancel | |
1960 return _resolver:cancel(...); | |
1961 end | |
1962 | |
1963 function dns.settimeout(...) | |
1964 return _resolver:settimeout(...); | |
1965 end | |
1966 | |
1967 function dns.socket_wrapper_set(...) -- - - - - - - - - socket_wrapper_set | |
1968 return _resolver:socket_wrapper_set(...); | |
1969 end | |
1970 | |
1971 return dns; | |
1972 end) | |
1973 package.preload['net.adns'] = (function (...) | |
1974 -- Prosody IM | |
1975 -- Copyright (C) 2008-2010 Matthew Wild | |
1976 -- Copyright (C) 2008-2010 Waqas Hussain | |
1977 -- | |
1978 -- This project is MIT/X11 licensed. Please see the | |
1979 -- COPYING file in the source package for more information. | |
1980 -- | |
1981 | |
1982 local server = require "net.server"; | |
1983 local dns = require "net.dns"; | |
1984 | |
1985 local log = require "util.logger".init("adns"); | |
1986 | |
1987 local t_insert, t_remove = table.insert, table.remove; | |
1988 local coroutine, tostring, pcall = coroutine, tostring, pcall; | |
1989 | |
1990 local function dummy_send(sock, data, i, j) return (j-i)+1; end | |
1991 | |
1992 module "adns" | |
1993 | |
1994 function lookup(handler, qname, qtype, qclass) | |
1995 return coroutine.wrap(function (peek) | |
1996 if peek then | |
1997 log("debug", "Records for %s already cached, using those...", qname); | |
1998 handler(peek); | |
1999 return; | |
2000 end | |
2001 log("debug", "Records for %s not in cache, sending query (%s)...", qname, tostring(coroutine.running())); | |
2002 local ok, err = dns.query(qname, qtype, qclass); | |
2003 if ok then | |
2004 coroutine.yield({ qclass or "IN", qtype or "A", qname, coroutine.running()}); -- Wait for reply | |
2005 log("debug", "Reply for %s (%s)", qname, tostring(coroutine.running())); | |
2006 end | |
2007 if ok then | |
2008 ok, err = pcall(handler, dns.peek(qname, qtype, qclass)); | |
2009 else | |
2010 log("error", "Error sending DNS query: %s", err); | |
2011 ok, err = pcall(handler, nil, err); | |
2012 end | |
2013 if not ok then | |
2014 log("error", "Error in DNS response handler: %s", tostring(err)); | |
2015 end | |
2016 end)(dns.peek(qname, qtype, qclass)); | |
2017 end | |
2018 | |
2019 function cancel(handle, call_handler, reason) | |
2020 log("warn", "Cancelling DNS lookup for %s", tostring(handle[3])); | |
2021 dns.cancel(handle[1], handle[2], handle[3], handle[4], call_handler); | |
2022 end | |
2023 | |
2024 function new_async_socket(sock, resolver) | |
2025 local peername = "<unknown>"; | |
2026 local listener = {}; | |
2027 local handler = {}; | |
2028 function listener.onincoming(conn, data) | |
2029 if data then | |
2030 dns.feed(handler, data); | |
2031 end | |
2032 end | |
2033 function listener.ondisconnect(conn, err) | |
2034 if err then | |
2035 log("warn", "DNS socket for %s disconnected: %s", peername, err); | |
2036 local servers = resolver.server; | |
2037 if resolver.socketset[conn] == resolver.best_server and resolver.best_server == #servers then | |
2038 log("error", "Exhausted all %d configured DNS servers, next lookup will try %s again", #servers, servers[1]); | |
2039 end | |
2040 | |
2041 resolver:servfail(conn); -- Let the magic commence | |
2042 end | |
2043 end | |
2044 handler = server.wrapclient(sock, "dns", 53, listener); | |
2045 if not handler then | |
2046 log("warn", "handler is nil"); | |
2047 end | |
2048 | |
2049 handler.settimeout = function () end | |
2050 handler.setsockname = function (_, ...) return sock:setsockname(...); end | |
2051 handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _:set_send(dummy_send); return ret; end | |
2052 handler.connect = function (_, ...) return sock:connect(...) end | |
2053 --handler.send = function (_, data) _:write(data); return _.sendbuffer and _.sendbuffer(); end | |
2054 handler.send = function (_, data) | |
2055 local getpeername = sock.getpeername; | |
2056 log("debug", "Sending DNS query to %s", (getpeername and getpeername(sock)) or "<unconnected>"); | |
2057 return sock:send(data); | |
2058 end | |
2059 return handler; | |
2060 end | |
2061 | |
2062 dns.socket_wrapper_set(new_async_socket); | |
2063 | |
2064 return _M; | |
2065 end) | |
2066 package.preload['net.server'] = (function (...) | |
2067 -- | |
2068 -- server.lua by blastbeat of the luadch project | |
2069 -- Re-used here under the MIT/X Consortium License | |
2070 -- | |
2071 -- Modifications (C) 2008-2010 Matthew Wild, Waqas Hussain | |
2072 -- | |
2073 | |
2074 -- // wrapping luadch stuff // -- | |
2075 | |
2076 local use = function( what ) | |
2077 return _G[ what ] | |
2078 end | |
2079 local clean = function( tbl ) | |
2080 for i, k in pairs( tbl ) do | |
2081 tbl[ i ] = nil | |
2082 end | |
2083 end | |
2084 | |
2085 local log, table_concat = require ("util.logger").init("socket"), table.concat; | |
2086 local out_put = function (...) return log("debug", table_concat{...}); end | |
2087 local out_error = function (...) return log("warn", table_concat{...}); end | |
2088 local mem_free = collectgarbage | |
2089 | |
2090 ----------------------------------// DECLARATION //-- | |
2091 | |
2092 --// constants //-- | |
2093 | |
2094 local STAT_UNIT = 1 -- byte | |
2095 | |
2096 --// lua functions //-- | |
2097 | |
2098 local type = use "type" | |
2099 local pairs = use "pairs" | |
2100 local ipairs = use "ipairs" | |
2101 local tonumber = use "tonumber" | |
2102 local tostring = use "tostring" | |
2103 local collectgarbage = use "collectgarbage" | |
2104 | |
2105 --// lua libs //-- | |
2106 | |
2107 local os = use "os" | |
2108 local table = use "table" | |
2109 local string = use "string" | |
2110 local coroutine = use "coroutine" | |
2111 | |
2112 --// lua lib methods //-- | |
2113 | |
2114 local os_difftime = os.difftime | |
2115 local math_min = math.min | |
2116 local math_huge = math.huge | |
2117 local table_concat = table.concat | |
2118 local table_remove = table.remove | |
2119 local string_len = string.len | |
2120 local string_sub = string.sub | |
2121 local coroutine_wrap = coroutine.wrap | |
2122 local coroutine_yield = coroutine.yield | |
2123 | |
2124 --// extern libs //-- | |
2125 | |
2126 local luasec = use "ssl" | |
2127 local luasocket = use "socket" or require "socket" | |
2128 local luasocket_gettime = luasocket.gettime | |
2129 | |
2130 --// extern lib methods //-- | |
2131 | |
2132 local ssl_wrap = ( luasec and luasec.wrap ) | |
2133 local socket_bind = luasocket.bind | |
2134 local socket_sleep = luasocket.sleep | |
2135 local socket_select = luasocket.select | |
2136 local ssl_newcontext = ( luasec and luasec.newcontext ) | |
2137 | |
2138 --// functions //-- | |
2139 | |
2140 local id | |
2141 local loop | |
2142 local stats | |
2143 local idfalse | |
2144 local addtimer | |
2145 local closeall | |
2146 local addsocket | |
2147 local addserver | |
2148 local getserver | |
2149 local wrapserver | |
2150 local getsettings | |
2151 local closesocket | |
2152 local removesocket | |
2153 local removeserver | |
2154 local changetimeout | |
2155 local wrapconnection | |
2156 local changesettings | |
2157 | |
2158 --// tables //-- | |
2159 | |
2160 local _server | |
2161 local _readlist | |
2162 local _timerlist | |
2163 local _sendlist | |
2164 local _socketlist | |
2165 local _closelist | |
2166 local _readtimes | |
2167 local _writetimes | |
2168 | |
2169 --// simple data types //-- | |
2170 | |
2171 local _ | |
2172 local _readlistlen | |
2173 local _sendlistlen | |
2174 local _timerlistlen | |
2175 | |
2176 local _sendtraffic | |
2177 local _readtraffic | |
2178 | |
2179 local _selecttimeout | |
2180 local _sleeptime | |
2181 | |
2182 local _starttime | |
2183 local _currenttime | |
2184 | |
2185 local _maxsendlen | |
2186 local _maxreadlen | |
2187 | |
2188 local _checkinterval | |
2189 local _sendtimeout | |
2190 local _readtimeout | |
2191 | |
2192 local _cleanqueue | |
2193 | |
2194 local _timer | |
2195 | |
2196 local _maxclientsperserver | |
2197 | |
2198 local _maxsslhandshake | |
2199 | |
2200 ----------------------------------// DEFINITION //-- | |
2201 | |
2202 _server = { } -- key = port, value = table; list of listening servers | |
2203 _readlist = { } -- array with sockets to read from | |
2204 _sendlist = { } -- arrary with sockets to write to | |
2205 _timerlist = { } -- array of timer functions | |
2206 _socketlist = { } -- key = socket, value = wrapped socket (handlers) | |
2207 _readtimes = { } -- key = handler, value = timestamp of last data reading | |
2208 _writetimes = { } -- key = handler, value = timestamp of last data writing/sending | |
2209 _closelist = { } -- handlers to close | |
2210 | |
2211 _readlistlen = 0 -- length of readlist | |
2212 _sendlistlen = 0 -- length of sendlist | |
2213 _timerlistlen = 0 -- lenght of timerlist | |
2214 | |
2215 _sendtraffic = 0 -- some stats | |
2216 _readtraffic = 0 | |
2217 | |
2218 _selecttimeout = 1 -- timeout of socket.select | |
2219 _sleeptime = 0 -- time to wait at the end of every loop | |
2220 | |
2221 _maxsendlen = 51000 * 1024 -- max len of send buffer | |
2222 _maxreadlen = 25000 * 1024 -- max len of read buffer | |
2223 | |
2224 _checkinterval = 1200000 -- interval in secs to check idle clients | |
2225 _sendtimeout = 60000 -- allowed send idle time in secs | |
2226 _readtimeout = 6 * 60 * 60 -- allowed read idle time in secs | |
2227 | |
2228 _cleanqueue = false -- clean bufferqueue after using | |
2229 | |
2230 _maxclientsperserver = 1000 | |
2231 | |
2232 _maxsslhandshake = 30 -- max handshake round-trips | |
2233 | |
2234 ----------------------------------// PRIVATE //-- | |
2235 | |
2236 wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections ) -- this function wraps a server | |
2237 | |
2238 maxconnections = maxconnections or _maxclientsperserver | |
2239 | |
2240 local connections = 0 | |
2241 | |
2242 local dispatch, disconnect = listeners.onconnect or listeners.onincoming, listeners.ondisconnect | |
2243 | |
2244 local accept = socket.accept | |
2245 | |
2246 --// public methods of the object //-- | |
2247 | |
2248 local handler = { } | |
2249 | |
2250 handler.shutdown = function( ) end | |
2251 | |
2252 handler.ssl = function( ) | |
2253 return sslctx ~= nil | |
2254 end | |
2255 handler.sslctx = function( ) | |
2256 return sslctx | |
2257 end | |
2258 handler.remove = function( ) | |
2259 connections = connections - 1 | |
2260 end | |
2261 handler.close = function( ) | |
2262 for _, handler in pairs( _socketlist ) do | |
2263 if handler.serverport == serverport then | |
2264 handler.disconnect( handler, "server closed" ) | |
2265 handler:close( true ) | |
2266 end | |
2267 end | |
2268 socket:close( ) | |
2269 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) | |
2270 _readlistlen = removesocket( _readlist, socket, _readlistlen ) | |
2271 _socketlist[ socket ] = nil | |
2272 handler = nil | |
2273 socket = nil | |
2274 --mem_free( ) | |
2275 out_put "server.lua: closed server handler and removed sockets from list" | |
2276 end | |
2277 handler.ip = function( ) | |
2278 return ip | |
2279 end | |
2280 handler.serverport = function( ) | |
2281 return serverport | |
2282 end | |
2283 handler.socket = function( ) | |
2284 return socket | |
2285 end | |
2286 handler.readbuffer = function( ) | |
2287 if connections > maxconnections then | |
2288 out_put( "server.lua: refused new client connection: server full" ) | |
2289 return false | |
2290 end | |
2291 local client, err = accept( socket ) -- try to accept | |
2292 if client then | |
2293 local ip, clientport = client:getpeername( ) | |
2294 client:settimeout( 0 ) | |
2295 local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx ) -- wrap new client socket | |
2296 if err then -- error while wrapping ssl socket | |
2297 return false | |
2298 end | |
2299 connections = connections + 1 | |
2300 out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) | |
2301 return dispatch( handler ) | |
2302 elseif err then -- maybe timeout or something else | |
2303 out_put( "server.lua: error with new client connection: ", tostring(err) ) | |
2304 return false | |
2305 end | |
2306 end | |
2307 return handler | |
2308 end | |
2309 | |
2310 wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx ) -- this function wraps a client to a handler object | |
2311 | |
2312 socket:settimeout( 0 ) | |
2313 | |
2314 --// local import of socket methods //-- | |
2315 | |
2316 local send | |
2317 local receive | |
2318 local shutdown | |
2319 | |
2320 --// private closures of the object //-- | |
2321 | |
2322 local ssl | |
2323 | |
2324 local dispatch = listeners.onincoming | |
2325 local status = listeners.onstatus | |
2326 local disconnect = listeners.ondisconnect | |
2327 local drain = listeners.ondrain | |
2328 | |
2329 local bufferqueue = { } -- buffer array | |
2330 local bufferqueuelen = 0 -- end of buffer array | |
2331 | |
2332 local toclose | |
2333 local fatalerror | |
2334 local needtls | |
2335 | |
2336 local bufferlen = 0 | |
2337 | |
2338 local noread = false | |
2339 local nosend = false | |
2340 | |
2341 local sendtraffic, readtraffic = 0, 0 | |
2342 | |
2343 local maxsendlen = _maxsendlen | |
2344 local maxreadlen = _maxreadlen | |
2345 | |
2346 --// public methods of the object //-- | |
2347 | |
2348 local handler = bufferqueue -- saves a table ^_^ | |
2349 | |
2350 handler.dispatch = function( ) | |
2351 return dispatch | |
2352 end | |
2353 handler.disconnect = function( ) | |
2354 return disconnect | |
2355 end | |
2356 handler.setlistener = function( self, listeners ) | |
2357 dispatch = listeners.onincoming | |
2358 disconnect = listeners.ondisconnect | |
2359 status = listeners.onstatus | |
2360 drain = listeners.ondrain | |
2361 end | |
2362 handler.getstats = function( ) | |
2363 return readtraffic, sendtraffic | |
2364 end | |
2365 handler.ssl = function( ) | |
2366 return ssl | |
2367 end | |
2368 handler.sslctx = function ( ) | |
2369 return sslctx | |
2370 end | |
2371 handler.send = function( _, data, i, j ) | |
2372 return send( socket, data, i, j ) | |
2373 end | |
2374 handler.receive = function( pattern, prefix ) | |
2375 return receive( socket, pattern, prefix ) | |
2376 end | |
2377 handler.shutdown = function( pattern ) | |
2378 return shutdown( socket, pattern ) | |
2379 end | |
2380 handler.setoption = function (self, option, value) | |
2381 if socket.setoption then | |
2382 return socket:setoption(option, value); | |
2383 end | |
2384 return false, "setoption not implemented"; | |
2385 end | |
2386 handler.close = function( self, forced ) | |
2387 if not handler then return true; end | |
2388 _readlistlen = removesocket( _readlist, socket, _readlistlen ) | |
2389 _readtimes[ handler ] = nil | |
2390 if bufferqueuelen ~= 0 then | |
2391 if not ( forced or fatalerror ) then | |
2392 handler.sendbuffer( ) | |
2393 if bufferqueuelen ~= 0 then -- try again... | |
2394 if handler then | |
2395 handler.write = nil -- ... but no further writing allowed | |
2396 end | |
2397 toclose = true | |
2398 return false | |
2399 end | |
2400 else | |
2401 send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen ) -- forced send | |
2402 end | |
2403 end | |
2404 if socket then | |
2405 _ = shutdown and shutdown( socket ) | |
2406 socket:close( ) | |
2407 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) | |
2408 _socketlist[ socket ] = nil | |
2409 socket = nil | |
2410 else | |
2411 out_put "server.lua: socket already closed" | |
2412 end | |
2413 if handler then | |
2414 _writetimes[ handler ] = nil | |
2415 _closelist[ handler ] = nil | |
2416 handler = nil | |
2417 end | |
2418 if server then | |
2419 server.remove( ) | |
2420 end | |
2421 out_put "server.lua: closed client handler and removed socket from list" | |
2422 return true | |
2423 end | |
2424 handler.ip = function( ) | |
2425 return ip | |
2426 end | |
2427 handler.serverport = function( ) | |
2428 return serverport | |
2429 end | |
2430 handler.clientport = function( ) | |
2431 return clientport | |
2432 end | |
2433 local write = function( self, data ) | |
2434 bufferlen = bufferlen + string_len( data ) | |
2435 if bufferlen > maxsendlen then | |
2436 _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle | |
2437 handler.write = idfalse -- dont write anymore | |
2438 return false | |
2439 elseif socket and not _sendlist[ socket ] then | |
2440 _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) | |
2441 end | |
2442 bufferqueuelen = bufferqueuelen + 1 | |
2443 bufferqueue[ bufferqueuelen ] = data | |
2444 if handler then | |
2445 _writetimes[ handler ] = _writetimes[ handler ] or _currenttime | |
2446 end | |
2447 return true | |
2448 end | |
2449 handler.write = write | |
2450 handler.bufferqueue = function( self ) | |
2451 return bufferqueue | |
2452 end | |
2453 handler.socket = function( self ) | |
2454 return socket | |
2455 end | |
2456 handler.set_mode = function( self, new ) | |
2457 pattern = new or pattern | |
2458 return pattern | |
2459 end | |
2460 handler.set_send = function ( self, newsend ) | |
2461 send = newsend or send | |
2462 return send | |
2463 end | |
2464 handler.bufferlen = function( self, readlen, sendlen ) | |
2465 maxsendlen = sendlen or maxsendlen | |
2466 maxreadlen = readlen or maxreadlen | |
2467 return bufferlen, maxreadlen, maxsendlen | |
2468 end | |
2469 --TODO: Deprecate | |
2470 handler.lock_read = function (self, switch) | |
2471 if switch == true then | |
2472 local tmp = _readlistlen | |
2473 _readlistlen = removesocket( _readlist, socket, _readlistlen ) | |
2474 _readtimes[ handler ] = nil | |
2475 if _readlistlen ~= tmp then | |
2476 noread = true | |
2477 end | |
2478 elseif switch == false then | |
2479 if noread then | |
2480 noread = false | |
2481 _readlistlen = addsocket(_readlist, socket, _readlistlen) | |
2482 _readtimes[ handler ] = _currenttime | |
2483 end | |
2484 end | |
2485 return noread | |
2486 end | |
2487 handler.pause = function (self) | |
2488 return self:lock_read(true); | |
2489 end | |
2490 handler.resume = function (self) | |
2491 return self:lock_read(false); | |
2492 end | |
2493 handler.lock = function( self, switch ) | |
2494 handler.lock_read (switch) | |
2495 if switch == true then | |
2496 handler.write = idfalse | |
2497 local tmp = _sendlistlen | |
2498 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) | |
2499 _writetimes[ handler ] = nil | |
2500 if _sendlistlen ~= tmp then | |
2501 nosend = true | |
2502 end | |
2503 elseif switch == false then | |
2504 handler.write = write | |
2505 if nosend then | |
2506 nosend = false | |
2507 write( "" ) | |
2508 end | |
2509 end | |
2510 return noread, nosend | |
2511 end | |
2512 local _readbuffer = function( ) -- this function reads data | |
2513 local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" | |
2514 if not err or (err == "wantread" or err == "timeout") then -- received something | |
2515 local buffer = buffer or part or "" | |
2516 local len = string_len( buffer ) | |
2517 if len > maxreadlen then | |
2518 disconnect( handler, "receive buffer exceeded" ) | |
2519 handler:close( true ) | |
2520 return false | |
2521 end | |
2522 local count = len * STAT_UNIT | |
2523 readtraffic = readtraffic + count | |
2524 _readtraffic = _readtraffic + count | |
2525 _readtimes[ handler ] = _currenttime | |
2526 --out_put( "server.lua: read data '", buffer:gsub("[^%w%p ]", "."), "', error: ", err ) | |
2527 return dispatch( handler, buffer, err ) | |
2528 else -- connections was closed or fatal error | |
2529 out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) ) | |
2530 fatalerror = true | |
2531 disconnect( handler, err ) | |
2532 _ = handler and handler:close( ) | |
2533 return false | |
2534 end | |
2535 end | |
2536 local _sendbuffer = function( ) -- this function sends data | |
2537 local succ, err, byte, buffer, count; | |
2538 local count; | |
2539 if socket then | |
2540 buffer = table_concat( bufferqueue, "", 1, bufferqueuelen ) | |
2541 succ, err, byte = send( socket, buffer, 1, bufferlen ) | |
2542 count = ( succ or byte or 0 ) * STAT_UNIT | |
2543 sendtraffic = sendtraffic + count | |
2544 _sendtraffic = _sendtraffic + count | |
2545 _ = _cleanqueue and clean( bufferqueue ) | |
2546 --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) ) | |
2547 else | |
2548 succ, err, count = false, "closed", 0; | |
2549 end | |
2550 if succ then -- sending succesful | |
2551 bufferqueuelen = 0 | |
2552 bufferlen = 0 | |
2553 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist | |
2554 _writetimes[ handler ] = nil | |
2555 if drain then | |
2556 drain(handler) | |
2557 end | |
2558 _ = needtls and handler:starttls(nil) | |
2559 _ = toclose and handler:close( ) | |
2560 return true | |
2561 elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write | |
2562 buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer | |
2563 bufferqueue[ 1 ] = buffer -- insert new buffer in queue | |
2564 bufferqueuelen = 1 | |
2565 bufferlen = bufferlen - byte | |
2566 _writetimes[ handler ] = _currenttime | |
2567 return true | |
2568 else -- connection was closed during sending or fatal error | |
2569 out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) ) | |
2570 fatalerror = true | |
2571 disconnect( handler, err ) | |
2572 _ = handler and handler:close( ) | |
2573 return false | |
2574 end | |
2575 end | |
2576 | |
2577 -- Set the sslctx | |
2578 local handshake; | |
2579 function handler.set_sslctx(self, new_sslctx) | |
2580 ssl = true | |
2581 sslctx = new_sslctx; | |
2582 local wrote | |
2583 local read | |
2584 handshake = coroutine_wrap( function( client ) -- create handshake coroutine | |
2585 local err | |
2586 for i = 1, _maxsslhandshake do | |
2587 _sendlistlen = ( wrote and removesocket( _sendlist, client, _sendlistlen ) ) or _sendlistlen | |
2588 _readlistlen = ( read and removesocket( _readlist, client, _readlistlen ) ) or _readlistlen | |
2589 read, wrote = nil, nil | |
2590 _, err = client:dohandshake( ) | |
2591 if not err then | |
2592 out_put( "server.lua: ssl handshake done" ) | |
2593 handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions | |
2594 handler.sendbuffer = _sendbuffer | |
2595 _ = status and status( handler, "ssl-handshake-complete" ) | |
2596 _readlistlen = addsocket(_readlist, client, _readlistlen) | |
2597 return true | |
2598 else | |
2599 if err == "wantwrite" and not wrote then | |
2600 _sendlistlen = addsocket(_sendlist, client, _sendlistlen) | |
2601 wrote = true | |
2602 elseif err == "wantread" and not read then | |
2603 _readlistlen = addsocket(_readlist, client, _readlistlen) | |
2604 read = true | |
2605 else | |
2606 out_put( "server.lua: ssl handshake error: ", tostring(err) ) | |
2607 break; | |
2608 end | |
2609 --coroutine_yield( handler, nil, err ) -- handshake not finished | |
2610 coroutine_yield( ) | |
2611 end | |
2612 end | |
2613 disconnect( handler, "ssl handshake failed" ) | |
2614 _ = handler and handler:close( true ) -- forced disconnect | |
2615 return false -- handshake failed | |
2616 end | |
2617 ) | |
2618 end | |
2619 if luasec then | |
2620 if sslctx then -- ssl? | |
2621 handler:set_sslctx(sslctx); | |
2622 out_put("server.lua: ", "starting ssl handshake") | |
2623 local err | |
2624 socket, err = ssl_wrap( socket, sslctx ) -- wrap socket | |
2625 if err then | |
2626 out_put( "server.lua: ssl error: ", tostring(err) ) | |
2627 --mem_free( ) | |
2628 return nil, nil, err -- fatal error | |
2629 end | |
2630 socket:settimeout( 0 ) | |
2631 handler.readbuffer = handshake | |
2632 handler.sendbuffer = handshake | |
2633 handshake( socket ) -- do handshake | |
2634 if not socket then | |
2635 return nil, nil, "ssl handshake failed"; | |
2636 end | |
2637 else | |
2638 local sslctx; | |
2639 handler.starttls = function( self, _sslctx) | |
2640 if _sslctx then | |
2641 sslctx = _sslctx; | |
2642 handler:set_sslctx(sslctx); | |
2643 end | |
2644 if bufferqueuelen > 0 then | |
2645 out_put "server.lua: we need to do tls, but delaying until send buffer empty" | |
2646 needtls = true | |
2647 return | |
2648 end | |
2649 out_put( "server.lua: attempting to start tls on " .. tostring( socket ) ) | |
2650 local oldsocket, err = socket | |
2651 socket, err = ssl_wrap( socket, sslctx ) -- wrap socket | |
2652 --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) ) | |
2653 if err then | |
2654 out_put( "server.lua: error while starting tls on client: ", tostring(err) ) | |
2655 return nil, err -- fatal error | |
2656 end | |
2657 | |
2658 socket:settimeout( 0 ) | |
2659 | |
2660 -- add the new socket to our system | |
2661 | |
2662 send = socket.send | |
2663 receive = socket.receive | |
2664 shutdown = id | |
2665 | |
2666 _socketlist[ socket ] = handler | |
2667 _readlistlen = addsocket(_readlist, socket, _readlistlen) | |
2668 | |
2669 -- remove traces of the old socket | |
2670 | |
2671 _readlistlen = removesocket( _readlist, oldsocket, _readlistlen ) | |
2672 _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen ) | |
2673 _socketlist[ oldsocket ] = nil | |
2674 | |
2675 handler.starttls = nil | |
2676 needtls = nil | |
2677 | |
2678 -- Secure now | |
2679 ssl = true | |
2680 | |
2681 handler.readbuffer = handshake | |
2682 handler.sendbuffer = handshake | |
2683 handshake( socket ) -- do handshake | |
2684 end | |
2685 handler.readbuffer = _readbuffer | |
2686 handler.sendbuffer = _sendbuffer | |
2687 end | |
2688 else | |
2689 handler.readbuffer = _readbuffer | |
2690 handler.sendbuffer = _sendbuffer | |
2691 end | |
2692 send = socket.send | |
2693 receive = socket.receive | |
2694 shutdown = ( ssl and id ) or socket.shutdown | |
2695 | |
2696 _socketlist[ socket ] = handler | |
2697 _readlistlen = addsocket(_readlist, socket, _readlistlen) | |
2698 return handler, socket | |
2699 end | |
2700 | |
2701 id = function( ) | |
2702 end | |
2703 | |
2704 idfalse = function( ) | |
2705 return false | |
2706 end | |
2707 | |
2708 addsocket = function( list, socket, len ) | |
2709 if not list[ socket ] then | |
2710 len = len + 1 | |
2711 list[ len ] = socket | |
2712 list[ socket ] = len | |
2713 end | |
2714 return len; | |
2715 end | |
2716 | |
2717 removesocket = function( list, socket, len ) -- this function removes sockets from a list ( copied from copas ) | |
2718 local pos = list[ socket ] | |
2719 if pos then | |
2720 list[ socket ] = nil | |
2721 local last = list[ len ] | |
2722 list[ len ] = nil | |
2723 if last ~= socket then | |
2724 list[ last ] = pos | |
2725 list[ pos ] = last | |
2726 end | |
2727 return len - 1 | |
2728 end | |
2729 return len | |
2730 end | |
2731 | |
2732 closesocket = function( socket ) | |
2733 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) | |
2734 _readlistlen = removesocket( _readlist, socket, _readlistlen ) | |
2735 _socketlist[ socket ] = nil | |
2736 socket:close( ) | |
2737 --mem_free( ) | |
2738 end | |
2739 | |
2740 local function link(sender, receiver, buffersize) | |
2741 local sender_locked; | |
2742 local _sendbuffer = receiver.sendbuffer; | |
2743 function receiver.sendbuffer() | |
2744 _sendbuffer(); | |
2745 if sender_locked and receiver.bufferlen() < buffersize then | |
2746 sender:lock_read(false); -- Unlock now | |
2747 sender_locked = nil; | |
2748 end | |
2749 end | |
2750 | |
2751 local _readbuffer = sender.readbuffer; | |
2752 function sender.readbuffer() | |
2753 _readbuffer(); | |
2754 if not sender_locked and receiver.bufferlen() >= buffersize then | |
2755 sender_locked = true; | |
2756 sender:lock_read(true); | |
2757 end | |
2758 end | |
2759 end | |
2760 | |
2761 ----------------------------------// PUBLIC //-- | |
2762 | |
2763 addserver = function( addr, port, listeners, pattern, sslctx ) -- this function provides a way for other scripts to reg a server | |
2764 local err | |
2765 if type( listeners ) ~= "table" then | |
2766 err = "invalid listener table" | |
2767 end | |
2768 if type( port ) ~= "number" or not ( port >= 0 and port <= 65535 ) then | |
2769 err = "invalid port" | |
2770 elseif _server[ addr..":"..port ] then | |
2771 err = "listeners on '[" .. addr .. "]:" .. port .. "' already exist" | |
2772 elseif sslctx and not luasec then | |
2773 err = "luasec not found" | |
2774 end | |
2775 if err then | |
2776 out_error( "server.lua, [", addr, "]:", port, ": ", err ) | |
2777 return nil, err | |
2778 end | |
2779 addr = addr or "*" | |
2780 local server, err = socket_bind( addr, port ) | |
2781 if err then | |
2782 out_error( "server.lua, [", addr, "]:", port, ": ", err ) | |
2783 return nil, err | |
2784 end | |
2785 local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, _maxclientsperserver ) -- wrap new server socket | |
2786 if not handler then | |
2787 server:close( ) | |
2788 return nil, err | |
2789 end | |
2790 server:settimeout( 0 ) | |
2791 _readlistlen = addsocket(_readlist, server, _readlistlen) | |
2792 _server[ addr..":"..port ] = handler | |
2793 _socketlist[ server ] = handler | |
2794 out_put( "server.lua: new "..(sslctx and "ssl " or "").."server listener on '[", addr, "]:", port, "'" ) | |
2795 return handler | |
2796 end | |
2797 | |
2798 getserver = function ( addr, port ) | |
2799 return _server[ addr..":"..port ]; | |
2800 end | |
2801 | |
2802 removeserver = function( addr, port ) | |
2803 local handler = _server[ addr..":"..port ] | |
2804 if not handler then | |
2805 return nil, "no server found on '[" .. addr .. "]:" .. tostring( port ) .. "'" | |
2806 end | |
2807 handler:close( ) | |
2808 _server[ addr..":"..port ] = nil | |
2809 return true | |
2810 end | |
2811 | |
2812 closeall = function( ) | |
2813 for _, handler in pairs( _socketlist ) do | |
2814 handler:close( ) | |
2815 _socketlist[ _ ] = nil | |
2816 end | |
2817 _readlistlen = 0 | |
2818 _sendlistlen = 0 | |
2819 _timerlistlen = 0 | |
2820 _server = { } | |
2821 _readlist = { } | |
2822 _sendlist = { } | |
2823 _timerlist = { } | |
2824 _socketlist = { } | |
2825 --mem_free( ) | |
2826 end | |
2827 | |
2828 getsettings = function( ) | |
2829 return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver, _maxsslhandshake | |
2830 end | |
2831 | |
2832 changesettings = function( new ) | |
2833 if type( new ) ~= "table" then | |
2834 return nil, "invalid settings table" | |
2835 end | |
2836 _selecttimeout = tonumber( new.timeout ) or _selecttimeout | |
2837 _sleeptime = tonumber( new.sleeptime ) or _sleeptime | |
2838 _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen | |
2839 _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen | |
2840 _checkinterval = tonumber( new.checkinterval ) or _checkinterval | |
2841 _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout | |
2842 _readtimeout = tonumber( new.readtimeout ) or _readtimeout | |
2843 _cleanqueue = new.cleanqueue | |
2844 _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver | |
2845 _maxsslhandshake = new._maxsslhandshake or _maxsslhandshake | |
2846 return true | |
2847 end | |
2848 | |
2849 addtimer = function( listener ) | |
2850 if type( listener ) ~= "function" then | |
2851 return nil, "invalid listener function" | |
2852 end | |
2853 _timerlistlen = _timerlistlen + 1 | |
2854 _timerlist[ _timerlistlen ] = listener | |
2855 return true | |
2856 end | |
2857 | |
2858 stats = function( ) | |
2859 return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen | |
2860 end | |
2861 | |
2862 local quitting; | |
2863 | |
2864 setquitting = function (quit) | |
2865 quitting = not not quit; | |
2866 end | |
2867 | |
2868 loop = function(once) -- this is the main loop of the program | |
2869 if quitting then return "quitting"; end | |
2870 if once then quitting = "once"; end | |
2871 local next_timer_time = math_huge; | |
2872 repeat | |
2873 local read, write, err = socket_select( _readlist, _sendlist, math_min(_selecttimeout, next_timer_time) ) | |
2874 for i, socket in ipairs( write ) do -- send data waiting in writequeues | |
2875 local handler = _socketlist[ socket ] | |
2876 if handler then | |
2877 handler.sendbuffer( ) | |
2878 else | |
2879 closesocket( socket ) | |
2880 out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen | |
2881 end | |
2882 end | |
2883 for i, socket in ipairs( read ) do -- receive data | |
2884 local handler = _socketlist[ socket ] | |
2885 if handler then | |
2886 handler.readbuffer( ) | |
2887 else | |
2888 closesocket( socket ) | |
2889 out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen | |
2890 end | |
2891 end | |
2892 for handler, err in pairs( _closelist ) do | |
2893 handler.disconnect( )( handler, err ) | |
2894 handler:close( true ) -- forced disconnect | |
2895 end | |
2896 clean( _closelist ) | |
2897 _currenttime = luasocket_gettime( ) | |
2898 if _currenttime - _timer >= math_min(next_timer_time, 1) then | |
2899 next_timer_time = math_huge; | |
2900 for i = 1, _timerlistlen do | |
2901 local t = _timerlist[ i ]( _currenttime ) -- fire timers | |
2902 if t then next_timer_time = math_min(next_timer_time, t); end | |
2903 end | |
2904 _timer = _currenttime | |
2905 else | |
2906 next_timer_time = next_timer_time - (_currenttime - _timer); | |
2907 end | |
2908 socket_sleep( _sleeptime ) -- wait some time | |
2909 --collectgarbage( ) | |
2910 until quitting; | |
2911 if once and quitting == "once" then quitting = nil; return; end | |
2912 return "quitting" | |
2913 end | |
2914 | |
2915 step = function () | |
2916 return loop(true); | |
2917 end | |
2918 | |
2919 local function get_backend() | |
2920 return "select"; | |
2921 end | |
2922 | |
2923 --// EXPERIMENTAL //-- | |
2924 | |
2925 local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx ) | |
2926 local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx ) | |
2927 _socketlist[ socket ] = handler | |
2928 _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) | |
2929 if listeners.onconnect then | |
2930 -- When socket is writeable, call onconnect | |
2931 local _sendbuffer = handler.sendbuffer; | |
2932 handler.sendbuffer = function () | |
2933 handler.sendbuffer = _sendbuffer; | |
2934 listeners.onconnect(handler); | |
2935 -- If there was data with the incoming packet, handle it now. | |
2936 if #handler:bufferqueue() > 0 then | |
2937 return _sendbuffer(); | |
2938 end | |
2939 end | |
2940 end | |
2941 return handler, socket | |
2942 end | |
2943 | |
2944 local addclient = function( address, port, listeners, pattern, sslctx ) | |
2945 local client, err = luasocket.tcp( ) | |
2946 if err then | |
2947 return nil, err | |
2948 end | |
2949 client:settimeout( 0 ) | |
2950 _, err = client:connect( address, port ) | |
2951 if err then -- try again | |
2952 local handler = wrapclient( client, address, port, listeners ) | |
2953 else | |
2954 wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx ) | |
2955 end | |
2956 end | |
2957 | |
2958 --// EXPERIMENTAL //-- | |
2959 | |
2960 ----------------------------------// BEGIN //-- | |
2961 | |
2962 use "setmetatable" ( _socketlist, { __mode = "k" } ) | |
2963 use "setmetatable" ( _readtimes, { __mode = "k" } ) | |
2964 use "setmetatable" ( _writetimes, { __mode = "k" } ) | |
2965 | |
2966 _timer = luasocket_gettime( ) | |
2967 _starttime = luasocket_gettime( ) | |
2968 | |
2969 addtimer( function( ) | |
2970 local difftime = os_difftime( _currenttime - _starttime ) | |
2971 if difftime > _checkinterval then | |
2972 _starttime = _currenttime | |
2973 for handler, timestamp in pairs( _writetimes ) do | |
2974 if os_difftime( _currenttime - timestamp ) > _sendtimeout then | |
2975 --_writetimes[ handler ] = nil | |
2976 handler.disconnect( )( handler, "send timeout" ) | |
2977 handler:close( true ) -- forced disconnect | |
2978 end | |
2979 end | |
2980 for handler, timestamp in pairs( _readtimes ) do | |
2981 if os_difftime( _currenttime - timestamp ) > _readtimeout then | |
2982 --_readtimes[ handler ] = nil | |
2983 handler.disconnect( )( handler, "read timeout" ) | |
2984 handler:close( ) -- forced disconnect? | |
2985 end | |
2986 end | |
2987 end | |
2988 end | |
2989 ) | |
2990 | |
2991 local function setlogger(new_logger) | |
2992 local old_logger = log; | |
2993 if new_logger then | |
2994 log = new_logger; | |
2995 end | |
2996 return old_logger; | |
2997 end | |
2998 | |
2999 ----------------------------------// PUBLIC INTERFACE //-- | |
3000 | |
3001 return { | |
3002 | |
3003 addclient = addclient, | |
3004 wrapclient = wrapclient, | |
3005 | |
3006 loop = loop, | |
3007 link = link, | |
3008 step = step, | |
3009 stats = stats, | |
3010 closeall = closeall, | |
3011 addtimer = addtimer, | |
3012 addserver = addserver, | |
3013 getserver = getserver, | |
3014 setlogger = setlogger, | |
3015 getsettings = getsettings, | |
3016 setquitting = setquitting, | |
3017 removeserver = removeserver, | |
3018 get_backend = get_backend, | |
3019 changesettings = changesettings, | |
3020 } | |
3021 end) | |
3022 package.preload['util.xmppstream'] = (function (...) | |
3023 -- Prosody IM | |
3024 -- Copyright (C) 2008-2010 Matthew Wild | |
3025 -- Copyright (C) 2008-2010 Waqas Hussain | |
3026 -- | |
3027 -- This project is MIT/X11 licensed. Please see the | |
3028 -- COPYING file in the source package for more information. | |
3029 -- | |
3030 | |
3031 | |
3032 local lxp = require "lxp"; | |
3033 local st = require "util.stanza"; | |
3034 local stanza_mt = st.stanza_mt; | |
3035 | |
3036 local tostring = tostring; | |
3037 local t_insert = table.insert; | |
3038 local t_concat = table.concat; | |
3039 local t_remove = table.remove; | |
3040 local setmetatable = setmetatable; | |
3041 | |
3042 local default_log = require "util.logger".init("xmppstream"); | |
3043 | |
3044 -- COMPAT: w/LuaExpat 1.1.0 | |
3045 local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false }); | |
3046 | |
3047 if not lxp_supports_doctype then | |
3048 default_log("warn", "The version of LuaExpat on your system leaves Prosody " | |
3049 .."vulnerable to denial-of-service attacks. You should upgrade to " | |
3050 .."LuaExpat 1.1.1 or higher as soon as possible. See " | |
3051 .."http://prosody.im/doc/depends#luaexpat for more information."); | |
3052 end | |
3053 | |
3054 local error = error; | |
3055 | |
3056 module "xmppstream" | |
3057 | |
3058 local new_parser = lxp.new; | |
3059 | |
3060 local ns_prefixes = { | |
3061 ["http://www.w3.org/XML/1998/namespace"] = "xml"; | |
3062 }; | |
3063 | |
3064 local xmlns_streams = "http://etherx.jabber.org/streams"; | |
3065 | |
3066 local ns_separator = "\1"; | |
3067 local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$"; | |
3068 | |
3069 _M.ns_separator = ns_separator; | |
3070 _M.ns_pattern = ns_pattern; | |
3071 | |
3072 function new_sax_handlers(session, stream_callbacks) | |
3073 local xml_handlers = {}; | |
3074 | |
3075 local log = session.log or default_log; | |
3076 | |
3077 local cb_streamopened = stream_callbacks.streamopened; | |
3078 local cb_streamclosed = stream_callbacks.streamclosed; | |
3079 local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end; | |
3080 local cb_handlestanza = stream_callbacks.handlestanza; | |
3081 | |
3082 local stream_ns = stream_callbacks.stream_ns or xmlns_streams; | |
3083 local stream_tag = stream_callbacks.stream_tag or "stream"; | |
3084 if stream_ns ~= "" then | |
3085 stream_tag = stream_ns..ns_separator..stream_tag; | |
3086 end | |
3087 local stream_error_tag = stream_ns..ns_separator..(stream_callbacks.error_tag or "error"); | |
3088 | |
3089 local stream_default_ns = stream_callbacks.default_ns; | |
3090 | |
3091 local stack = {}; | |
3092 local chardata, stanza = {}; | |
3093 local non_streamns_depth = 0; | |
3094 function xml_handlers:StartElement(tagname, attr) | |
3095 if stanza and #chardata > 0 then | |
3096 -- We have some character data in the buffer | |
3097 t_insert(stanza, t_concat(chardata)); | |
3098 chardata = {}; | |
3099 end | |
3100 local curr_ns,name = tagname:match(ns_pattern); | |
3101 if name == "" then | |
3102 curr_ns, name = "", curr_ns; | |
3103 end | |
3104 | |
3105 if curr_ns ~= stream_default_ns or non_streamns_depth > 0 then | |
3106 attr.xmlns = curr_ns; | |
3107 non_streamns_depth = non_streamns_depth + 1; | |
3108 end | |
3109 | |
3110 -- FIXME !!!!! | |
3111 for i=1,#attr do | |
3112 local k = attr[i]; | |
3113 attr[i] = nil; | |
3114 local ns, nm = k:match(ns_pattern); | |
3115 if nm ~= "" then | |
3116 ns = ns_prefixes[ns]; | |
3117 if ns then | |
3118 attr[ns..":"..nm] = attr[k]; | |
3119 attr[k] = nil; | |
3120 end | |
3121 end | |
3122 end | |
3123 | |
3124 if not stanza then --if we are not currently inside a stanza | |
3125 if session.notopen then | |
3126 if tagname == stream_tag then | |
3127 non_streamns_depth = 0; | |
3128 if cb_streamopened then | |
3129 cb_streamopened(session, attr); | |
3130 end | |
3131 else | |
3132 -- Garbage before stream? | |
3133 cb_error(session, "no-stream"); | |
3134 end | |
3135 return; | |
3136 end | |
3137 if curr_ns == "jabber:client" and name ~= "iq" and name ~= "presence" and name ~= "message" then | |
3138 cb_error(session, "invalid-top-level-element"); | |
3139 end | |
3140 | |
3141 stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt); | |
3142 else -- we are inside a stanza, so add a tag | |
3143 t_insert(stack, stanza); | |
3144 local oldstanza = stanza; | |
3145 stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt); | |
3146 t_insert(oldstanza, stanza); | |
3147 t_insert(oldstanza.tags, stanza); | |
3148 end | |
3149 end | |
3150 function xml_handlers:CharacterData(data) | |
3151 if stanza then | |
3152 t_insert(chardata, data); | |
3153 end | |
3154 end | |
3155 function xml_handlers:EndElement(tagname) | |
3156 if non_streamns_depth > 0 then | |
3157 non_streamns_depth = non_streamns_depth - 1; | |
3158 end | |
3159 if stanza then | |
3160 if #chardata > 0 then | |
3161 -- We have some character data in the buffer | |
3162 t_insert(stanza, t_concat(chardata)); | |
3163 chardata = {}; | |
3164 end | |
3165 -- Complete stanza | |
3166 if #stack == 0 then | |
3167 if tagname ~= stream_error_tag then | |
3168 cb_handlestanza(session, stanza); | |
3169 else | |
3170 cb_error(session, "stream-error", stanza); | |
3171 end | |
3172 stanza = nil; | |
3173 else | |
3174 stanza = t_remove(stack); | |
3175 end | |
3176 else | |
3177 if tagname == stream_tag then | |
3178 if cb_streamclosed then | |
3179 cb_streamclosed(session); | |
3180 end | |
3181 else | |
3182 local curr_ns,name = tagname:match(ns_pattern); | |
3183 if name == "" then | |
3184 curr_ns, name = "", curr_ns; | |
3185 end | |
3186 cb_error(session, "parse-error", "unexpected-element-close", name); | |
3187 end | |
3188 stanza, chardata = nil, {}; | |
3189 stack = {}; | |
3190 end | |
3191 end | |
3192 | |
3193 local function restricted_handler(parser) | |
3194 cb_error(session, "parse-error", "restricted-xml", "Restricted XML, see RFC 6120 section 11.1."); | |
3195 if not parser.stop or not parser:stop() then | |
3196 error("Failed to abort parsing"); | |
3197 end | |
3198 end | |
3199 | |
3200 if lxp_supports_doctype then | |
3201 xml_handlers.StartDoctypeDecl = restricted_handler; | |
3202 end | |
3203 xml_handlers.Comment = restricted_handler; | |
3204 xml_handlers.ProcessingInstruction = restricted_handler; | |
3205 | |
3206 local function reset() | |
3207 stanza, chardata = nil, {}; | |
3208 stack = {}; | |
3209 end | |
3210 | |
3211 local function set_session(stream, new_session) | |
3212 session = new_session; | |
3213 log = new_session.log or default_log; | |
3214 end | |
3215 | |
3216 return xml_handlers, { reset = reset, set_session = set_session }; | |
3217 end | |
3218 | |
3219 function new(session, stream_callbacks) | |
3220 local handlers, meta = new_sax_handlers(session, stream_callbacks); | |
3221 local parser = new_parser(handlers, ns_separator); | |
3222 local parse = parser.parse; | |
3223 | |
3224 return { | |
3225 reset = function () | |
3226 parser = new_parser(handlers, ns_separator); | |
3227 parse = parser.parse; | |
3228 meta.reset(); | |
3229 end, | |
3230 feed = function (self, data) | |
3231 return parse(parser, data); | |
3232 end, | |
3233 set_session = meta.set_session; | |
3234 }; | |
3235 end | |
3236 | |
3237 return _M; | |
3238 end) | |
3239 package.preload['util.jid'] = (function (...) | |
3240 -- Prosody IM | |
3241 -- Copyright (C) 2008-2010 Matthew Wild | |
3242 -- Copyright (C) 2008-2010 Waqas Hussain | |
3243 -- | |
3244 -- This project is MIT/X11 licensed. Please see the | |
3245 -- COPYING file in the source package for more information. | |
3246 -- | |
3247 | |
3248 | |
3249 | |
3250 local match = string.match; | |
3251 local nodeprep = require "util.encodings".stringprep.nodeprep; | |
3252 local nameprep = require "util.encodings".stringprep.nameprep; | |
3253 local resourceprep = require "util.encodings".stringprep.resourceprep; | |
3254 | |
3255 module "jid" | |
3256 | |
3257 local function _split(jid) | |
3258 if not jid then return; end | |
3259 local node, nodepos = match(jid, "^([^@/]+)@()"); | |
3260 local host, hostpos = match(jid, "^([^@/]+)()", nodepos) | |
3261 if node and not host then return nil, nil, nil; end | |
3262 local resource = match(jid, "^/(.+)$", hostpos); | |
3263 if (not host) or ((not resource) and #jid >= hostpos) then return nil, nil, nil; end | |
3264 return node, host, resource; | |
3265 end | |
3266 split = _split; | |
3267 | |
3268 function bare(jid) | |
3269 local node, host = _split(jid); | |
3270 if node and host then | |
3271 return node.."@"..host; | |
3272 end | |
3273 return host; | |
3274 end | |
3275 | |
3276 local function _prepped_split(jid) | |
3277 local node, host, resource = _split(jid); | |
3278 if host then | |
3279 host = nameprep(host); | |
3280 if not host then return; end | |
3281 if node then | |
3282 node = nodeprep(node); | |
3283 if not node then return; end | |
3284 end | |
3285 if resource then | |
3286 resource = resourceprep(resource); | |
3287 if not resource then return; end | |
3288 end | |
3289 return node, host, resource; | |
3290 end | |
3291 end | |
3292 prepped_split = _prepped_split; | |
3293 | |
3294 function prep(jid) | |
3295 local node, host, resource = _prepped_split(jid); | |
3296 if host then | |
3297 if node then | |
3298 host = node .. "@" .. host; | |
3299 end | |
3300 if resource then | |
3301 host = host .. "/" .. resource; | |
3302 end | |
3303 end | |
3304 return host; | |
3305 end | |
3306 | |
3307 function join(node, host, resource) | |
3308 if node and host and resource then | |
3309 return node.."@"..host.."/"..resource; | |
3310 elseif node and host then | |
3311 return node.."@"..host; | |
3312 elseif host and resource then | |
3313 return host.."/"..resource; | |
3314 elseif host then | |
3315 return host; | |
3316 end | |
3317 return nil; -- Invalid JID | |
3318 end | |
3319 | |
3320 function compare(jid, acl) | |
3321 -- compare jid to single acl rule | |
3322 -- TODO compare to table of rules? | |
3323 local jid_node, jid_host, jid_resource = _split(jid); | |
3324 local acl_node, acl_host, acl_resource = _split(acl); | |
3325 if ((acl_node ~= nil and acl_node == jid_node) or acl_node == nil) and | |
3326 ((acl_host ~= nil and acl_host == jid_host) or acl_host == nil) and | |
3327 ((acl_resource ~= nil and acl_resource == jid_resource) or acl_resource == nil) then | |
3328 return true | |
3329 end | |
3330 return false | |
3331 end | |
3332 | |
3333 return _M; | |
3334 end) | |
3335 package.preload['util.events'] = (function (...) | |
3336 -- Prosody IM | |
3337 -- Copyright (C) 2008-2010 Matthew Wild | |
3338 -- Copyright (C) 2008-2010 Waqas Hussain | |
3339 -- | |
3340 -- This project is MIT/X11 licensed. Please see the | |
3341 -- COPYING file in the source package for more information. | |
3342 -- | |
3343 | |
3344 | |
3345 local pairs = pairs; | |
3346 local t_insert = table.insert; | |
3347 local t_sort = table.sort; | |
3348 local setmetatable = setmetatable; | |
3349 local next = next; | |
3350 | |
3351 module "events" | |
3352 | |
3353 function new() | |
3354 local handlers = {}; | |
3355 local event_map = {}; | |
3356 local function _rebuild_index(handlers, event) | |
3357 local _handlers = event_map[event]; | |
3358 if not _handlers or next(_handlers) == nil then return; end | |
3359 local index = {}; | |
3360 for handler in pairs(_handlers) do | |
3361 t_insert(index, handler); | |
3362 end | |
3363 t_sort(index, function(a, b) return _handlers[a] > _handlers[b]; end); | |
3364 handlers[event] = index; | |
3365 return index; | |
3366 end; | |
3367 setmetatable(handlers, { __index = _rebuild_index }); | |
3368 local function add_handler(event, handler, priority) | |
3369 local map = event_map[event]; | |
3370 if map then | |
3371 map[handler] = priority or 0; | |
3372 else | |
3373 map = {[handler] = priority or 0}; | |
3374 event_map[event] = map; | |
3375 end | |
3376 handlers[event] = nil; | |
3377 end; | |
3378 local function remove_handler(event, handler) | |
3379 local map = event_map[event]; | |
3380 if map then | |
3381 map[handler] = nil; | |
3382 handlers[event] = nil; | |
3383 if next(map) == nil then | |
3384 event_map[event] = nil; | |
3385 end | |
3386 end | |
3387 end; | |
3388 local function add_handlers(handlers) | |
3389 for event, handler in pairs(handlers) do | |
3390 add_handler(event, handler); | |
3391 end | |
3392 end; | |
3393 local function remove_handlers(handlers) | |
3394 for event, handler in pairs(handlers) do | |
3395 remove_handler(event, handler); | |
3396 end | |
3397 end; | |
3398 local function fire_event(event, ...) | |
3399 local h = handlers[event]; | |
3400 if h then | |
3401 for i=1,#h do | |
3402 local ret = h[i](...); | |
3403 if ret ~= nil then return ret; end | |
3404 end | |
3405 end | |
3406 end; | |
3407 return { | |
3408 add_handler = add_handler; | |
3409 remove_handler = remove_handler; | |
3410 add_handlers = add_handlers; | |
3411 remove_handlers = remove_handlers; | |
3412 fire_event = fire_event; | |
3413 _handlers = handlers; | |
3414 _event_map = event_map; | |
3415 }; | |
3416 end | |
3417 | |
3418 return _M; | |
3419 end) | |
3420 package.preload['util.dataforms'] = (function (...) | |
3421 -- Prosody IM | |
3422 -- Copyright (C) 2008-2010 Matthew Wild | |
3423 -- Copyright (C) 2008-2010 Waqas Hussain | |
3424 -- | |
3425 -- This project is MIT/X11 licensed. Please see the | |
3426 -- COPYING file in the source package for more information. | |
3427 -- | |
3428 | |
3429 local setmetatable = setmetatable; | |
3430 local pairs, ipairs = pairs, ipairs; | |
3431 local tostring, type = tostring, type; | |
3432 local t_concat = table.concat; | |
3433 local st = require "util.stanza"; | |
3434 | |
3435 module "dataforms" | |
3436 | |
3437 local xmlns_forms = 'jabber:x:data'; | |
3438 | |
3439 local form_t = {}; | |
3440 local form_mt = { __index = form_t }; | |
3441 | |
3442 function new(layout) | |
3443 return setmetatable(layout, form_mt); | |
3444 end | |
3445 | |
3446 function form_t.form(layout, data, formtype) | |
3447 local form = st.stanza("x", { xmlns = xmlns_forms, type = formtype or "form" }); | |
3448 if layout.title then | |
3449 form:tag("title"):text(layout.title):up(); | |
3450 end | |
3451 if layout.instructions then | |
3452 form:tag("instructions"):text(layout.instructions):up(); | |
3453 end | |
3454 for n, field in ipairs(layout) do | |
3455 local field_type = field.type or "text-single"; | |
3456 -- Add field tag | |
3457 form:tag("field", { type = field_type, var = field.name, label = field.label }); | |
3458 | |
3459 local value = (data and data[field.name]) or field.value; | |
3460 | |
3461 if value then | |
3462 -- Add value, depending on type | |
3463 if field_type == "hidden" then | |
3464 if type(value) == "table" then | |
3465 -- Assume an XML snippet | |
3466 form:tag("value") | |
3467 :add_child(value) | |
3468 :up(); | |
3469 else | |
3470 form:tag("value"):text(tostring(value)):up(); | |
3471 end | |
3472 elseif field_type == "boolean" then | |
3473 form:tag("value"):text((value and "1") or "0"):up(); | |
3474 elseif field_type == "fixed" then | |
3475 | |
3476 elseif field_type == "jid-multi" then | |
3477 for _, jid in ipairs(value) do | |
3478 form:tag("value"):text(jid):up(); | |
3479 end | |
3480 elseif field_type == "jid-single" then | |
3481 form:tag("value"):text(value):up(); | |
3482 elseif field_type == "text-single" or field_type == "text-private" then | |
3483 form:tag("value"):text(value):up(); | |
3484 elseif field_type == "text-multi" then | |
3485 -- Split into multiple <value> tags, one for each line | |
3486 for line in value:gmatch("([^\r\n]+)\r?\n*") do | |
3487 form:tag("value"):text(line):up(); | |
3488 end | |
3489 elseif field_type == "list-single" then | |
3490 local has_default = false; | |
3491 if type(value) == "string" then | |
3492 form:tag("value"):text(value):up(); | |
3493 else | |
3494 for _, val in ipairs(value) do | |
3495 if type(val) == "table" then | |
3496 form:tag("option", { label = val.label }):tag("value"):text(val.value):up():up(); | |
3497 if val.default and (not has_default) then | |
3498 form:tag("value"):text(val.value):up(); | |
3499 has_default = true; | |
3500 end | |
3501 else | |
3502 form:tag("option", { label= val }):tag("value"):text(tostring(val)):up():up(); | |
3503 end | |
3504 end | |
3505 end | |
3506 elseif field_type == "list-multi" then | |
3507 for _, val in ipairs(value) do | |
3508 if type(val) == "table" then | |
3509 form:tag("option", { label = val.label }):tag("value"):text(val.value):up():up(); | |
3510 if val.default then | |
3511 form:tag("value"):text(val.value):up(); | |
3512 end | |
3513 else | |
3514 form:tag("option", { label= val }):tag("value"):text(tostring(val)):up():up(); | |
3515 end | |
3516 end | |
3517 end | |
3518 end | |
3519 | |
3520 if field.required then | |
3521 form:tag("required"):up(); | |
3522 end | |
3523 | |
3524 -- Jump back up to list of fields | |
3525 form:up(); | |
3526 end | |
3527 return form; | |
3528 end | |
3529 | |
3530 local field_readers = {}; | |
3531 | |
3532 function form_t.data(layout, stanza) | |
3533 local data = {}; | |
3534 | |
3535 for field_tag in stanza:childtags() do | |
3536 local field_type; | |
3537 for n, field in ipairs(layout) do | |
3538 if field.name == field_tag.attr.var then | |
3539 field_type = field.type; | |
3540 break; | |
3541 end | |
3542 end | |
3543 | |
3544 local reader = field_readers[field_type]; | |
3545 if reader then | |
3546 data[field_tag.attr.var] = reader(field_tag); | |
3547 end | |
3548 | |
3549 end | |
3550 return data; | |
3551 end | |
3552 | |
3553 field_readers["text-single"] = | |
3554 function (field_tag) | |
3555 local value = field_tag:child_with_name("value"); | |
3556 if value then | |
3557 return value[1]; | |
3558 end | |
3559 end | |
3560 | |
3561 field_readers["text-private"] = | |
3562 field_readers["text-single"]; | |
3563 | |
3564 field_readers["jid-single"] = | |
3565 field_readers["text-single"]; | |
3566 | |
3567 field_readers["jid-multi"] = | |
3568 function (field_tag) | |
3569 local result = {}; | |
3570 for value_tag in field_tag:childtags() do | |
3571 if value_tag.name == "value" then | |
3572 result[#result+1] = value_tag[1]; | |
3573 end | |
3574 end | |
3575 return result; | |
3576 end | |
3577 | |
3578 field_readers["text-multi"] = | |
3579 function (field_tag) | |
3580 local result = {}; | |
3581 for value_tag in field_tag:childtags() do | |
3582 if value_tag.name == "value" then | |
3583 result[#result+1] = value_tag[1]; | |
3584 end | |
3585 end | |
3586 return t_concat(result, "\n"); | |
3587 end | |
3588 | |
3589 field_readers["list-single"] = | |
3590 field_readers["text-single"]; | |
3591 | |
3592 field_readers["list-multi"] = | |
3593 function (field_tag) | |
3594 local result = {}; | |
3595 for value_tag in field_tag:childtags() do | |
3596 if value_tag.name == "value" then | |
3597 result[#result+1] = value_tag[1]; | |
3598 end | |
3599 end | |
3600 return result; | |
3601 end | |
3602 | |
3603 field_readers["boolean"] = | |
3604 function (field_tag) | |
3605 local value = field_tag:child_with_name("value"); | |
3606 if value then | |
3607 if value[1] == "1" or value[1] == "true" then | |
3608 return true; | |
3609 else | |
3610 return false; | |
3611 end | |
3612 end | |
3613 end | |
3614 | |
3615 field_readers["hidden"] = | |
3616 function (field_tag) | |
3617 local value = field_tag:child_with_name("value"); | |
3618 if value then | |
3619 return value[1]; | |
3620 end | |
3621 end | |
3622 | |
3623 return _M; | |
3624 | |
3625 | |
3626 --[=[ | |
3627 | |
3628 Layout: | |
3629 { | |
3630 | |
3631 title = "MUC Configuration", | |
3632 instructions = [[Use this form to configure options for this MUC room.]], | |
3633 | |
3634 { name = "FORM_TYPE", type = "hidden", required = true }; | |
3635 { name = "field-name", type = "field-type", required = false }; | |
3636 } | |
3637 | |
3638 | |
3639 --]=] | |
3640 end) | |
3641 package.preload['util.serialization'] = (function (...) | |
3642 -- Prosody IM | |
3643 -- Copyright (C) 2008-2010 Matthew Wild | |
3644 -- Copyright (C) 2008-2010 Waqas Hussain | |
3645 -- | |
3646 -- This project is MIT/X11 licensed. Please see the | |
3647 -- COPYING file in the source package for more information. | |
3648 -- | |
3649 | |
3650 local string_rep = string.rep; | |
3651 local type = type; | |
3652 local tostring = tostring; | |
3653 local t_insert = table.insert; | |
3654 local t_concat = table.concat; | |
3655 local error = error; | |
3656 local pairs = pairs; | |
3657 local next = next; | |
3658 | |
3659 local loadstring = loadstring; | |
3660 local setfenv = setfenv; | |
3661 local pcall = pcall; | |
3662 | |
3663 local debug_traceback = debug.traceback; | |
3664 local log = require "util.logger".init("serialization"); | |
3665 module "serialization" | |
3666 | |
3667 local indent = function(i) | |
3668 return string_rep("\t", i); | |
3669 end | |
3670 local function basicSerialize (o) | |
3671 if type(o) == "number" or type(o) == "boolean" then | |
3672 -- no need to check for NaN, as that's not a valid table index | |
3673 if o == 1/0 then return "(1/0)"; | |
3674 elseif o == -1/0 then return "(-1/0)"; | |
3675 else return tostring(o); end | |
3676 else -- assume it is a string -- FIXME make sure it's a string. throw an error otherwise. | |
3677 return (("%q"):format(tostring(o)):gsub("\\\n", "\\n")); | |
3678 end | |
3679 end | |
3680 local function _simplesave(o, ind, t, func) | |
3681 if type(o) == "number" then | |
3682 if o ~= o then func(t, "(0/0)"); | |
3683 elseif o == 1/0 then func(t, "(1/0)"); | |
3684 elseif o == -1/0 then func(t, "(-1/0)"); | |
3685 else func(t, tostring(o)); end | |
3686 elseif type(o) == "string" then | |
3687 func(t, (("%q"):format(o):gsub("\\\n", "\\n"))); | |
3688 elseif type(o) == "table" then | |
3689 if next(o) ~= nil then | |
3690 func(t, "{\n"); | |
3691 for k,v in pairs(o) do | |
3692 func(t, indent(ind)); | |
3693 func(t, "["); | |
3694 func(t, basicSerialize(k)); | |
3695 func(t, "] = "); | |
3696 if ind == 0 then | |
3697 _simplesave(v, 0, t, func); | |
3698 else | |
3699 _simplesave(v, ind+1, t, func); | |
3700 end | |
3701 func(t, ";\n"); | |
3702 end | |
3703 func(t, indent(ind-1)); | |
3704 func(t, "}"); | |
3705 else | |
3706 func(t, "{}"); | |
3707 end | |
3708 elseif type(o) == "boolean" then | |
3709 func(t, (o and "true" or "false")); | |
3710 else | |
3711 log("error", "cannot serialize a %s: %s", type(o), debug_traceback()) | |
3712 func(t, "nil"); | |
3713 end | |
3714 end | |
3715 | |
3716 function append(t, o) | |
3717 _simplesave(o, 1, t, t.write or t_insert); | |
3718 return t; | |
3719 end | |
3720 | |
3721 function serialize(o) | |
3722 return t_concat(append({}, o)); | |
3723 end | |
3724 | |
3725 function deserialize(str) | |
3726 if type(str) ~= "string" then return nil; end | |
3727 str = "return "..str; | |
3728 local f, err = loadstring(str, "@data"); | |
3729 if not f then return nil, err; end | |
3730 setfenv(f, {}); | |
3731 local success, ret = pcall(f); | |
3732 if not success then return nil, ret; end | |
3733 return ret; | |
3734 end | |
3735 | |
3736 return _M; | |
3737 end) | |
3738 package.preload['verse.plugins.presence'] = (function (...) | |
3739 function verse.plugins.presence(stream) | |
3740 stream.last_presence = nil; | |
3741 | |
3742 stream:hook("presence-out", function (presence) | |
3743 if not presence.attr.to then | |
3744 stream.last_presence = presence; -- Cache non-directed presence | |
3745 end | |
3746 end, 1); | |
3747 | |
3748 function stream:resend_presence() | |
3749 if last_presence then | |
3750 stream:send(last_presence); | |
3751 end | |
3752 end | |
3753 | |
3754 function stream:set_status(opts) | |
3755 local p = verse.presence(); | |
3756 if type(opts) == "table" then | |
3757 if opts.show then | |
3758 p:tag("show"):text(opts.show):up(); | |
3759 end | |
3760 if opts.prio then | |
3761 p:tag("priority"):text(tostring(opts.prio)):up(); | |
3762 end | |
3763 if opts.msg then | |
3764 p:tag("status"):text(opts.msg):up(); | |
3765 end | |
3766 end | |
3767 -- TODO maybe use opts as prio if it's a int, | |
3768 -- or as show or status if it's a string? | |
3769 | |
3770 stream:send(p); | |
3771 end | |
3772 end | |
3773 end) | |
3774 package.preload['verse.plugins.groupchat'] = (function (...) | |
3775 local events = require "events"; | |
3776 | |
3777 local room_mt = {}; | |
3778 room_mt.__index = room_mt; | |
3779 | |
3780 local xmlns_delay = "urn:xmpp:delay"; | |
3781 local xmlns_muc = "http://jabber.org/protocol/muc"; | |
3782 | |
3783 function verse.plugins.groupchat(stream) | |
3784 stream:add_plugin("presence") | |
3785 stream.rooms = {}; | |
3786 | |
3787 stream:hook("stanza", function (stanza) | |
3788 local room_jid = jid.bare(stanza.attr.from); | |
3789 if not room_jid then return end | |
3790 local room = stream.rooms[room_jid] | |
3791 if not room and stanza.attr.to and room_jid then | |
3792 room = stream.rooms[stanza.attr.to.." "..room_jid] | |
3793 end | |
3794 if room and room.opts.source and stanza.attr.to ~= room.opts.source then return end | |
3795 if room then | |
3796 local nick = select(3, jid.split(stanza.attr.from)); | |
3797 local body = stanza:get_child("body"); | |
3798 local delay = stanza:get_child("delay", xmlns_delay); | |
3799 local event = { | |
3800 room_jid = room_jid; | |
3801 room = room; | |
3802 sender = room.occupants[nick]; | |
3803 nick = nick; | |
3804 body = (body and body:get_text()) or nil; | |
3805 stanza = stanza; | |
3806 delay = (delay and delay.attr.stamp); | |
3807 }; | |
3808 local ret = room:event(stanza.name, event); | |
3809 return ret or (stanza.name == "message") or nil; | |
3810 end | |
3811 end, 500); | |
3812 | |
3813 function stream:join_room(jid, nick, opts) | |
3814 if not nick then | |
3815 return false, "no nickname supplied" | |
3816 end | |
3817 opts = opts or {}; | |
3818 local room = setmetatable({ | |
3819 stream = stream, jid = jid, nick = nick, | |
3820 subject = nil, | |
3821 occupants = {}, | |
3822 opts = opts, | |
3823 events = events.new() | |
3824 }, room_mt); | |
3825 if opts.source then | |
3826 self.rooms[opts.source.." "..jid] = room; | |
3827 else | |
3828 self.rooms[jid] = room; | |
3829 end | |
3830 local occupants = room.occupants; | |
3831 room:hook("presence", function (presence) | |
3832 local nick = presence.nick or nick; | |
3833 if not occupants[nick] and presence.stanza.attr.type ~= "unavailable" then | |
3834 occupants[nick] = { | |
3835 nick = nick; | |
3836 jid = presence.stanza.attr.from; | |
3837 presence = presence.stanza; | |
3838 }; | |
3839 local x = presence.stanza:get_child("x", xmlns_muc .. "#user"); | |
3840 if x then | |
3841 local x_item = x:get_child("item"); | |
3842 if x_item and x_item.attr then | |
3843 occupants[nick].real_jid = x_item.attr.jid; | |
3844 occupants[nick].affiliation = x_item.attr.affiliation; | |
3845 occupants[nick].role = x_item.attr.role; | |
3846 end | |
3847 --TODO Check for status 100? | |
3848 end | |
3849 if nick == room.nick then | |
3850 room.stream:event("groupchat/joined", room); | |
3851 else | |
3852 room:event("occupant-joined", occupants[nick]); | |
3853 end | |
3854 elseif occupants[nick] and presence.stanza.attr.type == "unavailable" then | |
3855 if nick == room.nick then | |
3856 room.stream:event("groupchat/left", room); | |
3857 if room.opts.source then | |
3858 self.rooms[room.opts.source.." "..jid] = nil; | |
3859 else | |
3860 self.rooms[jid] = nil; | |
3861 end | |
3862 else | |
3863 occupants[nick].presence = presence.stanza; | |
3864 room:event("occupant-left", occupants[nick]); | |
3865 occupants[nick] = nil; | |
3866 end | |
3867 end | |
3868 end); | |
3869 room:hook("message", function(msg) | |
3870 local subject = msg.stanza:get_child_text("subject"); | |
3871 if not subject then return end | |
3872 subject = #subject > 0 and subject or nil; | |
3873 if subject ~= room.subject then | |
3874 local old_subject = room.subject; | |
3875 room.subject = subject; | |
3876 return self:event("subject-changed", { from = old_subject, to = subject, by = msg.sender }); | |
3877 end | |
3878 end, 2000); | |
3879 local join_st = verse.presence():tag("x",{xmlns = xmlns_muc}):reset(); | |
3880 self:event("pre-groupchat/joining", join_st); | |
3881 room:send(join_st) | |
3882 self:event("groupchat/joining", room); | |
3883 return room; | |
3884 end | |
3885 | |
3886 stream:hook("presence-out", function(presence) | |
3887 if not presence.attr.to then | |
3888 for _, room in pairs(stream.rooms) do | |
3889 room:send(presence); | |
3890 end | |
3891 presence.attr.to = nil; | |
3892 end | |
3893 end); | |
3894 end | |
3895 | |
3896 function room_mt:send(stanza) | |
3897 if stanza.name == "message" and not stanza.attr.type then | |
3898 stanza.attr.type = "groupchat"; | |
3899 end | |
3900 if stanza.name == "presence" then | |
3901 stanza.attr.to = self.jid .."/"..self.nick; | |
3902 end | |
3903 if stanza.attr.type == "groupchat" or not stanza.attr.to then | |
3904 stanza.attr.to = self.jid; | |
3905 end | |
3906 if self.opts.source then | |
3907 stanza.attr.from = self.opts.source | |
3908 end | |
3909 self.stream:send(stanza); | |
3910 end | |
3911 | |
3912 function room_mt:send_message(text) | |
3913 self:send(verse.message():tag("body"):text(text)); | |
3914 end | |
3915 | |
3916 function room_mt:set_subject(text) | |
3917 self:send(verse.message():tag("subject"):text(text)); | |
3918 end | |
3919 | |
3920 function room_mt:leave(message) | |
3921 self.stream:event("groupchat/leaving", self); | |
3922 self:send(verse.presence({type="unavailable"})); | |
3923 end | |
3924 | |
3925 function room_mt:admin_set(nick, what, value, reason) | |
3926 self:send(verse.iq({type="set"}) | |
3927 :query(xmlns_muc .. "#admin") | |
3928 :tag("item", {nick = nick, [what] = value}) | |
3929 :tag("reason"):text(reason or "")); | |
3930 end | |
3931 | |
3932 function room_mt:set_role(nick, role, reason) | |
3933 self:admin_set(nick, "role", role, reason); | |
3934 end | |
3935 | |
3936 function room_mt:set_affiliation(nick, affiliation, reason) | |
3937 self:admin_set(nick, "affiliation", affiliation, reason); | |
3938 end | |
3939 | |
3940 function room_mt:kick(nick, reason) | |
3941 self:set_role(nick, "none", reason); | |
3942 end | |
3943 | |
3944 function room_mt:ban(nick, reason) | |
3945 self:set_affiliation(nick, "outcast", reason); | |
3946 end | |
3947 | |
3948 function room_mt:event(name, arg) | |
3949 self.stream:debug("Firing room event: %s", name); | |
3950 return self.events.fire_event(name, arg); | |
3951 end | |
3952 | |
3953 function room_mt:hook(name, callback, priority) | |
3954 return self.events.add_handler(name, callback, priority); | |
3955 end | |
3956 end) | |
3957 package.preload['net.httpclient_listener'] = (function (...) | |
3958 -- Prosody IM | |
3959 -- Copyright (C) 2008-2010 Matthew Wild | |
3960 -- Copyright (C) 2008-2010 Waqas Hussain | |
3961 -- | |
3962 -- This project is MIT/X11 licensed. Please see the | |
3963 -- COPYING file in the source package for more information. | |
3964 -- | |
3965 | |
3966 local log = require "util.logger".init("httpclient_listener"); | |
3967 | |
3968 local connlisteners_register = require "net.connlisteners".register; | |
3969 | |
3970 local requests = {}; -- Open requests | |
3971 local buffers = {}; -- Buffers of partial lines | |
3972 | |
3973 local httpclient = { default_port = 80, default_mode = "*a" }; | |
3974 | |
3975 function httpclient.onincoming(conn, data) | |
3976 local request = requests[conn]; | |
3977 | |
3978 if not request then | |
3979 log("warn", "Received response from connection %s with no request attached!", tostring(conn)); | |
3980 return; | |
3981 end | |
3982 | |
3983 if data and request.reader then | |
3984 request:reader(data); | |
3985 end | |
3986 end | |
3987 | |
3988 function httpclient.ondisconnect(conn, err) | |
3989 local request = requests[conn]; | |
3990 if request and err ~= "closed" then | |
3991 request:reader(nil); | |
3992 end | |
3993 requests[conn] = nil; | |
3994 end | |
3995 | |
3996 function httpclient.register_request(conn, req) | |
3997 log("debug", "Attaching request %s to connection %s", tostring(req.id or req), tostring(conn)); | |
3998 requests[conn] = req; | |
3999 end | |
4000 | |
4001 connlisteners_register("httpclient", httpclient); | |
4002 end) | |
4003 package.preload['net.connlisteners'] = (function (...) | |
4004 -- Prosody IM | |
4005 -- Copyright (C) 2008-2010 Matthew Wild | |
4006 -- Copyright (C) 2008-2010 Waqas Hussain | |
4007 -- | |
4008 -- This project is MIT/X11 licensed. Please see the | |
4009 -- COPYING file in the source package for more information. | |
4010 -- | |
4011 | |
4012 | |
4013 | |
4014 local listeners_dir = (CFG_SOURCEDIR or ".").."/net/"; | |
4015 local server = require "net.server"; | |
4016 local log = require "util.logger".init("connlisteners"); | |
4017 local tostring = tostring; | |
4018 local type = type | |
4019 local ipairs = ipairs | |
4020 | |
4021 local dofile, xpcall, error = | |
4022 dofile, xpcall, error | |
4023 | |
4024 local debug_traceback = debug.traceback; | |
4025 | |
4026 module "connlisteners" | |
4027 | |
4028 local listeners = {}; | |
4029 | |
4030 function register(name, listener) | |
4031 if listeners[name] and listeners[name] ~= listener then | |
4032 log("debug", "Listener %s is already registered, not registering any more", name); | |
4033 return false; | |
4034 end | |
4035 listeners[name] = listener; | |
4036 log("debug", "Registered connection listener %s", name); | |
4037 return true; | |
4038 end | |
4039 | |
4040 function deregister(name) | |
4041 listeners[name] = nil; | |
4042 end | |
4043 | |
4044 function get(name) | |
4045 local h = listeners[name]; | |
4046 if not h then | |
4047 local ok, ret = xpcall(function() dofile(listeners_dir..name:gsub("[^%w%-]", "_").."_listener.lua") end, debug_traceback); | |
4048 if not ok then | |
4049 log("error", "Error while loading listener '%s': %s", tostring(name), tostring(ret)); | |
4050 return nil, ret; | |
4051 end | |
4052 h = listeners[name]; | |
4053 end | |
4054 return h; | |
4055 end | |
4056 | |
4057 function start(name, udata) | |
4058 local h, err = get(name); | |
4059 if not h then | |
4060 error("No such connection module: "..name.. (err and (" ("..err..")") or ""), 0); | |
4061 end | |
4062 | |
4063 local interfaces = (udata and udata.interface) or h.default_interface or "*"; | |
4064 if type(interfaces) == "string" then interfaces = {interfaces}; end | |
4065 local port = (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0); | |
4066 local mode = (udata and udata.mode) or h.default_mode or 1; | |
4067 local ssl = (udata and udata.ssl) or nil; | |
4068 local autossl = udata and udata.type == "ssl"; | |
4069 | |
4070 if autossl and not ssl then | |
4071 return nil, "no ssl context"; | |
4072 end | |
4073 | |
4074 ok, err = true, {}; | |
4075 for _, interface in ipairs(interfaces) do | |
4076 local handler | |
4077 handler, err[interface] = server.addserver(interface, port, h, mode, autossl and ssl or nil); | |
4078 ok = ok and handler; | |
4079 end | |
4080 | |
4081 return ok, err; | |
4082 end | |
4083 | |
4084 return _M; | |
4085 end) | |
4086 package.preload['util.httpstream'] = (function (...) | |
4087 | |
4088 local coroutine = coroutine; | |
4089 local tonumber = tonumber; | |
4090 | |
4091 local deadroutine = coroutine.create(function() end); | |
4092 coroutine.resume(deadroutine); | |
4093 | |
4094 module("httpstream") | |
4095 | |
4096 local function parser(success_cb, parser_type, options_cb) | |
4097 local data = coroutine.yield(); | |
4098 local function readline() | |
4099 local pos = data:find("\r\n", nil, true); | |
4100 while not pos do | |
4101 data = data..coroutine.yield(); | |
4102 pos = data:find("\r\n", nil, true); | |
4103 end | |
4104 local r = data:sub(1, pos-1); | |
4105 data = data:sub(pos+2); | |
4106 return r; | |
4107 end | |
4108 local function readlength(n) | |
4109 while #data < n do | |
4110 data = data..coroutine.yield(); | |
4111 end | |
4112 local r = data:sub(1, n); | |
4113 data = data:sub(n + 1); | |
4114 return r; | |
4115 end | |
4116 local function readheaders() | |
4117 local headers = {}; -- read headers | |
4118 while true do | |
4119 local line = readline(); | |
4120 if line == "" then break; end -- headers done | |
4121 local key, val = line:match("^([^%s:]+): *(.*)$"); | |
4122 if not key then coroutine.yield("invalid-header-line"); end -- TODO handle multi-line and invalid headers | |
4123 key = key:lower(); | |
4124 headers[key] = headers[key] and headers[key]..","..val or val; | |
4125 end | |
4126 return headers; | |
4127 end | |
4128 | |
4129 if not parser_type or parser_type == "server" then | |
4130 while true do | |
4131 -- read status line | |
4132 local status_line = readline(); | |
4133 local method, path, httpversion = status_line:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$"); | |
4134 if not method then coroutine.yield("invalid-status-line"); end | |
4135 path = path:gsub("^//+", "/"); -- TODO parse url more | |
4136 local headers = readheaders(); | |
4137 | |
4138 -- read body | |
4139 local len = tonumber(headers["content-length"]); | |
4140 len = len or 0; -- TODO check for invalid len | |
4141 local body = readlength(len); | |
4142 | |
4143 success_cb({ | |
4144 method = method; | |
4145 path = path; | |
4146 httpversion = httpversion; | |
4147 headers = headers; | |
4148 body = body; | |
4149 }); | |
4150 end | |
4151 elseif parser_type == "client" then | |
4152 while true do | |
4153 -- read status line | |
4154 local status_line = readline(); | |
4155 local httpversion, status_code, reason_phrase = status_line:match("^HTTP/(%S+)%s+(%d%d%d)%s+(.*)$"); | |
4156 status_code = tonumber(status_code); | |
4157 if not status_code then coroutine.yield("invalid-status-line"); end | |
4158 local headers = readheaders(); | |
4159 | |
4160 -- read body | |
4161 local have_body = not | |
4162 ( (options_cb and options_cb().method == "HEAD") | |
4163 or (status_code == 204 or status_code == 304 or status_code == 301) | |
4164 or (status_code >= 100 and status_code < 200) ); | |
4165 | |
4166 local body; | |
4167 if have_body then | |
4168 local len = tonumber(headers["content-length"]); | |
4169 if headers["transfer-encoding"] == "chunked" then | |
4170 body = ""; | |
4171 while true do | |
4172 local chunk_size = readline():match("^%x+"); | |
4173 if not chunk_size then coroutine.yield("invalid-chunk-size"); end | |
4174 chunk_size = tonumber(chunk_size, 16) | |
4175 if chunk_size == 0 then break; end | |
4176 body = body..readlength(chunk_size); | |
4177 if readline() ~= "" then coroutine.yield("invalid-chunk-ending"); end | |
4178 end | |
4179 local trailers = readheaders(); | |
4180 elseif len then -- TODO check for invalid len | |
4181 body = readlength(len); | |
4182 else -- read to end | |
4183 repeat | |
4184 local newdata = coroutine.yield(); | |
4185 data = data..newdata; | |
4186 until newdata == ""; | |
4187 body, data = data, ""; | |
4188 end | |
4189 end | |
4190 | |
4191 success_cb({ | |
4192 code = status_code; | |
4193 httpversion = httpversion; | |
4194 headers = headers; | |
4195 body = body; | |
4196 -- COMPAT the properties below are deprecated | |
4197 responseversion = httpversion; | |
4198 responseheaders = headers; | |
4199 }); | |
4200 end | |
4201 else coroutine.yield("unknown-parser-type"); end | |
4202 end | |
4203 | |
4204 function new(success_cb, error_cb, parser_type, options_cb) | |
4205 local co = coroutine.create(parser); | |
4206 coroutine.resume(co, success_cb, parser_type, options_cb) | |
4207 return { | |
4208 feed = function(self, data) | |
4209 if not data then | |
4210 if parser_type == "client" then coroutine.resume(co, ""); end | |
4211 co = deadroutine; | |
4212 return error_cb(); | |
4213 end | |
4214 local success, result = coroutine.resume(co, data); | |
4215 if result then | |
4216 co = deadroutine; | |
4217 return error_cb(result); | |
4218 end | |
4219 end; | |
4220 }; | |
4221 end | |
4222 | |
4223 return _M; | |
4224 end) | |
4225 package.preload['net.http'] = (function (...) | |
4226 -- Prosody IM | |
4227 -- Copyright (C) 2008-2010 Matthew Wild | |
4228 -- Copyright (C) 2008-2010 Waqas Hussain | |
4229 -- | |
4230 -- This project is MIT/X11 licensed. Please see the | |
4231 -- COPYING file in the source package for more information. | |
4232 -- | |
4233 | |
4234 | |
4235 local socket = require "socket" | |
4236 local mime = require "mime" | |
4237 local url = require "socket.url" | |
4238 local httpstream_new = require "util.httpstream".new; | |
4239 | |
4240 local server = require "net.server" | |
4241 | |
4242 local connlisteners_get = require "net.connlisteners".get; | |
4243 local listener = connlisteners_get("httpclient") or error("No httpclient listener!"); | |
4244 | |
4245 local t_insert, t_concat = table.insert, table.concat; | |
4246 local pairs, ipairs = pairs, ipairs; | |
4247 local tonumber, tostring, xpcall, select, debug_traceback, char, format = | |
4248 tonumber, tostring, xpcall, select, debug.traceback, string.char, string.format; | |
4249 | |
4250 local log = require "util.logger".init("http"); | |
4251 | |
4252 module "http" | |
4253 | |
4254 function urlencode(s) return s and (s:gsub("%W", function (c) return format("%%%02x", c:byte()); end)); end | |
4255 function urldecode(s) return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); end | |
4256 | |
4257 local function _formencodepart(s) | |
4258 return s and (s:gsub("%W", function (c) | |
4259 if c ~= " " then | |
4260 return format("%%%02x", c:byte()); | |
4261 else | |
4262 return "+"; | |
4263 end | |
4264 end)); | |
4265 end | |
4266 function formencode(form) | |
4267 local result = {}; | |
4268 for _, field in ipairs(form) do | |
4269 t_insert(result, _formencodepart(field.name).."=".._formencodepart(field.value)); | |
4270 end | |
4271 return t_concat(result, "&"); | |
4272 end | |
4273 | |
4274 local function request_reader(request, data, startpos) | |
4275 if not request.parser then | |
4276 local function success_cb(r) | |
4277 if request.callback then | |
4278 for k,v in pairs(r) do request[k] = v; end | |
4279 request.callback(r.body, r.code, request); | |
4280 request.callback = nil; | |
4281 end | |
4282 destroy_request(request); | |
4283 end | |
4284 local function error_cb(r) | |
4285 if request.callback then | |
4286 request.callback(r or "connection-closed", 0, request); | |
4287 request.callback = nil; | |
4288 end | |
4289 destroy_request(request); | |
4290 end | |
4291 local function options_cb() | |
4292 return request; | |
4293 end | |
4294 request.parser = httpstream_new(success_cb, error_cb, "client", options_cb); | |
4295 end | |
4296 request.parser:feed(data); | |
4297 end | |
4298 | |
4299 local function handleerr(err) log("error", "Traceback[http]: %s: %s", tostring(err), debug_traceback()); end | |
4300 function request(u, ex, callback) | |
4301 local req = url.parse(u); | |
4302 | |
4303 if not (req and req.host) then | |
4304 callback(nil, 0, req); | |
4305 return nil, "invalid-url"; | |
4306 end | |
4307 | |
4308 if not req.path then | |
4309 req.path = "/"; | |
4310 end | |
4311 | |
4312 local custom_headers, body; | |
4313 local default_headers = { ["Host"] = req.host, ["User-Agent"] = "Prosody XMPP Server" } | |
4314 | |
4315 | |
4316 if req.userinfo then | |
4317 default_headers["Authorization"] = "Basic "..mime.b64(req.userinfo); | |
4318 end | |
4319 | |
4320 if ex then | |
4321 custom_headers = ex.headers; | |
4322 req.onlystatus = ex.onlystatus; | |
4323 body = ex.body; | |
4324 if body then | |
4325 req.method = "POST "; | |
4326 default_headers["Content-Length"] = tostring(#body); | |
4327 default_headers["Content-Type"] = "application/x-www-form-urlencoded"; | |
4328 end | |
4329 if ex.method then req.method = ex.method; end | |
4330 end | |
4331 | |
4332 req.handler, req.conn = server.wrapclient(socket.tcp(), req.host, req.port or 80, listener, "*a"); | |
4333 req.write = function (...) return req.handler:write(...); end | |
4334 req.conn:settimeout(0); | |
4335 local ok, err = req.conn:connect(req.host, req.port or 80); | |
4336 if not ok and err ~= "timeout" then | |
4337 callback(nil, 0, req); | |
4338 return nil, err; | |
4339 end | |
4340 | |
4341 local request_line = { req.method or "GET", " ", req.path, " HTTP/1.1\r\n" }; | |
4342 | |
4343 if req.query then | |
4344 t_insert(request_line, 4, "?"); | |
4345 t_insert(request_line, 5, req.query); | |
4346 end | |
4347 | |
4348 req.write(t_concat(request_line)); | |
4349 local t = { [2] = ": ", [4] = "\r\n" }; | |
4350 if custom_headers then | |
4351 for k, v in pairs(custom_headers) do | |
4352 t[1], t[3] = k, v; | |
4353 req.write(t_concat(t)); | |
4354 default_headers[k] = nil; | |
4355 end | |
4356 end | |
4357 | |
4358 for k, v in pairs(default_headers) do | |
4359 t[1], t[3] = k, v; | |
4360 req.write(t_concat(t)); | |
4361 default_headers[k] = nil; | |
4362 end | |
4363 req.write("\r\n"); | |
4364 | |
4365 if body then | |
4366 req.write(body); | |
4367 end | |
4368 | |
4369 req.callback = function (content, code, request) log("debug", "Calling callback, status %s", code or "---"); return select(2, xpcall(function () return callback(content, code, request) end, handleerr)); end | |
4370 req.reader = request_reader; | |
4371 req.state = "status"; | |
4372 | |
4373 listener.register_request(req.handler, req); | |
4374 | |
4375 return req; | |
4376 end | |
4377 | |
4378 function destroy_request(request) | |
4379 if request.conn then | |
4380 request.conn = nil; | |
4381 request.handler:close() | |
4382 listener.ondisconnect(request.handler, "closed"); | |
4383 end | |
4384 end | |
4385 | |
4386 _M.urlencode = urlencode; | |
4387 | |
4388 return _M; | |
4389 end) | |
4390 package.preload['verse.bosh'] = (function (...) | |
4391 | |
4392 local new_xmpp_stream = require "util.xmppstream".new; | |
4393 local st = require "util.stanza"; | |
4394 require "net.httpclient_listener"; -- Required for net.http to work | |
4395 local http = require "net.http"; | |
4396 | |
4397 local stream_mt = setmetatable({}, { __index = verse.stream_mt }); | |
4398 stream_mt.__index = stream_mt; | |
4399 | |
4400 local xmlns_stream = "http://etherx.jabber.org/streams"; | |
4401 local xmlns_bosh = "http://jabber.org/protocol/httpbind"; | |
4402 | |
4403 local reconnect_timeout = 5; | |
4404 | |
4405 function verse.new_bosh(logger, url) | |
4406 local stream = { | |
4407 bosh_conn_pool = {}; | |
4408 bosh_waiting_requests = {}; | |
4409 bosh_rid = math.random(1,999999); | |
4410 bosh_outgoing_buffer = {}; | |
4411 bosh_url = url; | |
4412 conn = {}; | |
4413 }; | |
4414 function stream:reopen() | |
4415 self.bosh_need_restart = true; | |
4416 self:flush(); | |
4417 end | |
4418 local conn = verse.new(logger, stream); | |
4419 return setmetatable(conn, stream_mt); | |
4420 end | |
4421 | |
4422 function stream_mt:connect() | |
4423 self:_send_session_request(); | |
4424 end | |
4425 | |
4426 function stream_mt:send(data) | |
4427 self:debug("Putting into BOSH send buffer: %s", tostring(data)); | |
4428 self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1] = st.clone(data); | |
4429 self:flush(); --TODO: Optimize by doing this on next tick (give a chance for data to buffer) | |
4430 end | |
4431 | |
4432 function stream_mt:flush() | |
4433 if self.connected | |
4434 and #self.bosh_waiting_requests < self.bosh_max_requests | |
4435 and (#self.bosh_waiting_requests == 0 | |
4436 or #self.bosh_outgoing_buffer > 0 | |
4437 or self.bosh_need_restart) then | |
4438 self:debug("Flushing..."); | |
4439 local payload = self:_make_body(); | |
4440 local buffer = self.bosh_outgoing_buffer; | |
4441 for i, stanza in ipairs(buffer) do | |
4442 payload:add_child(stanza); | |
4443 buffer[i] = nil; | |
4444 end | |
4445 self:_make_request(payload); | |
4446 else | |
4447 self:debug("Decided not to flush."); | |
4448 end | |
4449 end | |
4450 | |
4451 function stream_mt:_make_request(payload) | |
4452 local request, err = http.request(self.bosh_url, { body = tostring(payload) }, function (response, code, request) | |
4453 if code ~= 0 then | |
4454 self.inactive_since = nil; | |
4455 return self:_handle_response(response, code, request); | |
4456 end | |
4457 | |
4458 -- Connection issues, we need to retry this request | |
4459 local time = os.time(); | |
4460 if not self.inactive_since then | |
4461 self.inactive_since = time; -- So we know when it is time to give up | |
4462 elseif time - self.inactive_since > self.bosh_max_inactivity then | |
4463 return self:_disconnected(); | |
4464 else | |
4465 self:debug("%d seconds left to reconnect, retrying in %d seconds...", | |
4466 self.bosh_max_inactivity - (time - self.inactive_since), reconnect_timeout); | |
4467 end | |
4468 | |
4469 -- Set up reconnect timer | |
4470 timer.add_task(reconnect_timeout, function () | |
4471 self:debug("Retrying request..."); | |
4472 -- Remove old request | |
4473 for i, waiting_request in ipairs(self.bosh_waiting_requests) do | |
4474 if waiting_request == request then | |
4475 table.remove(self.bosh_waiting_requests, i); | |
4476 break; | |
4477 end | |
4478 end | |
4479 self:_make_request(payload); | |
4480 end); | |
4481 end); | |
4482 if request then | |
4483 table.insert(self.bosh_waiting_requests, request); | |
4484 else | |
4485 self:warn("Request failed instantly: %s", err); | |
4486 end | |
4487 end | |
4488 | |
4489 function stream_mt:_disconnected() | |
4490 self.connected = nil; | |
4491 self:event("disconnected"); | |
4492 end | |
4493 | |
4494 function stream_mt:_send_session_request() | |
4495 local body = self:_make_body(); | |
4496 | |
4497 -- XEP-0124 | |
4498 body.attr.hold = "1"; | |
4499 body.attr.wait = "60"; | |
4500 body.attr["xml:lang"] = "en"; | |
4501 body.attr.ver = "1.6"; | |
4502 | |
4503 -- XEP-0206 | |
4504 body.attr.from = self.jid; | |
4505 body.attr.to = self.host; | |
4506 body.attr.secure = 'true'; | |
4507 | |
4508 http.request(self.bosh_url, { body = tostring(body) }, function (response, code) | |
4509 if code == 0 then | |
4510 -- Failed to connect | |
4511 return self:_disconnected(); | |
4512 end | |
4513 -- Handle session creation response | |
4514 local payload = self:_parse_response(response) | |
4515 if not payload then | |
4516 self:warn("Invalid session creation response"); | |
4517 self:_disconnected(); | |
4518 return; | |
4519 end | |
4520 self.bosh_sid = payload.attr.sid; -- Session id | |
4521 self.bosh_wait = tonumber(payload.attr.wait); -- How long the server may hold connections for | |
4522 self.bosh_hold = tonumber(payload.attr.hold); -- How many connections the server may hold | |
4523 self.bosh_max_inactivity = tonumber(payload.attr.inactivity); -- Max amount of time with no connections | |
4524 self.bosh_max_requests = tonumber(payload.attr.requests) or self.bosh_hold; -- Max simultaneous requests we can make | |
4525 self.connected = true; | |
4526 self:event("connected"); | |
4527 self:_handle_response_payload(payload); | |
4528 end); | |
4529 end | |
4530 | |
4531 function stream_mt:_handle_response(response, code, request) | |
4532 if self.bosh_waiting_requests[1] ~= request then | |
4533 self:warn("Server replied to request that wasn't the oldest"); | |
4534 for i, waiting_request in ipairs(self.bosh_waiting_requests) do | |
4535 if waiting_request == request then | |
4536 self.bosh_waiting_requests[i] = nil; | |
4537 break; | |
4538 end | |
4539 end | |
4540 else | |
4541 table.remove(self.bosh_waiting_requests, 1); | |
4542 end | |
4543 local payload = self:_parse_response(response); | |
4544 if payload then | |
4545 self:_handle_response_payload(payload); | |
4546 end | |
4547 self:flush(); | |
4548 end | |
4549 | |
4550 function stream_mt:_handle_response_payload(payload) | |
4551 for stanza in payload:childtags() do | |
4552 if stanza.attr.xmlns == xmlns_stream then | |
4553 self:event("stream-"..stanza.name, stanza); | |
4554 elseif stanza.attr.xmlns then | |
4555 self:event("stream/"..stanza.attr.xmlns, stanza); | |
4556 else | |
4557 self:event("stanza", stanza); | |
4558 end | |
4559 end | |
4560 if payload.attr.type == "terminate" then | |
4561 self:_disconnected({reason = payload.attr.condition}); | |
4562 end | |
4563 end | |
4564 | |
4565 local stream_callbacks = { | |
4566 stream_ns = "http://jabber.org/protocol/httpbind", stream_tag = "body", | |
4567 default_ns = "jabber:client", | |
4568 streamopened = function (session, attr) session.notopen = nil; session.payload = verse.stanza("body", attr); return true; end; | |
4569 handlestanza = function (session, stanza) session.payload:add_child(stanza); end; | |
4570 }; | |
4571 function stream_mt:_parse_response(response) | |
4572 self:debug("Parsing response: %s", response); | |
4573 if response == nil then | |
4574 self:debug("%s", debug.traceback()); | |
4575 self:_disconnected(); | |
4576 return; | |
4577 end | |
4578 local session = { notopen = true, log = self.log }; | |
4579 local stream = new_xmpp_stream(session, stream_callbacks); | |
4580 stream:feed(response); | |
4581 return session.payload; | |
4582 end | |
4583 | |
4584 function stream_mt:_make_body() | |
4585 self.bosh_rid = self.bosh_rid + 1; | |
4586 local body = verse.stanza("body", { | |
4587 xmlns = xmlns_bosh; | |
4588 content = "text/xml; charset=utf-8"; | |
4589 sid = self.bosh_sid; | |
4590 rid = self.bosh_rid; | |
4591 }); | |
4592 if self.bosh_need_restart then | |
4593 self.bosh_need_restart = nil; | |
4594 body.attr.restart = 'true'; | |
4595 end | |
4596 return body; | |
4597 end | |
4598 end) | |
4599 package.preload['bit'] = (function (...) | |
4600 -- Prosody IM | |
4601 -- Copyright (C) 2008-2010 Matthew Wild | |
4602 -- Copyright (C) 2008-2010 Waqas Hussain | |
4603 -- | |
4604 -- This project is MIT/X11 licensed. Please see the | |
4605 -- COPYING file in the source package for more information. | |
4606 -- | |
4607 | |
4608 | |
4609 local type = type; | |
4610 local tonumber = tonumber; | |
4611 local setmetatable = setmetatable; | |
4612 local error = error; | |
4613 local tostring = tostring; | |
4614 local print = print; | |
4615 | |
4616 local xor_map = {[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=0;[18]=3;[19]=2;[20]=5;[21]=4;[22]=7;[23]=6;[24]=9;[25]=8;[26]=11;[27]=10;[28]=13;[29]=12;[30]=15;[31]=14;[32]=2;[33]=3;[34]=0;[35]=1;[36]=6;[37]=7;[38]=4;[39]=5;[40]=10;[41]=11;[42]=8;[43]=9;[44]=14;[45]=15;[46]=12;[47]=13;[48]=3;[49]=2;[50]=1;[51]=0;[52]=7;[53]=6;[54]=5;[55]=4;[56]=11;[57]=10;[58]=9;[59]=8;[60]=15;[61]=14;[62]=13;[63]=12;[64]=4;[65]=5;[66]=6;[67]=7;[68]=0;[69]=1;[70]=2;[71]=3;[72]=12;[73]=13;[74]=14;[75]=15;[76]=8;[77]=9;[78]=10;[79]=11;[80]=5;[81]=4;[82]=7;[83]=6;[84]=1;[85]=0;[86]=3;[87]=2;[88]=13;[89]=12;[90]=15;[91]=14;[92]=9;[93]=8;[94]=11;[95]=10;[96]=6;[97]=7;[98]=4;[99]=5;[100]=2;[101]=3;[102]=0;[103]=1;[104]=14;[105]=15;[106]=12;[107]=13;[108]=10;[109]=11;[110]=8;[111]=9;[112]=7;[113]=6;[114]=5;[115]=4;[116]=3;[117]=2;[118]=1;[119]=0;[120]=15;[121]=14;[122]=13;[123]=12;[124]=11;[125]=10;[126]=9;[127]=8;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=0;[137]=1;[138]=2;[139]=3;[140]=4;[141]=5;[142]=6;[143]=7;[144]=9;[145]=8;[146]=11;[147]=10;[148]=13;[149]=12;[150]=15;[151]=14;[152]=1;[153]=0;[154]=3;[155]=2;[156]=5;[157]=4;[158]=7;[159]=6;[160]=10;[161]=11;[162]=8;[163]=9;[164]=14;[165]=15;[166]=12;[167]=13;[168]=2;[169]=3;[170]=0;[171]=1;[172]=6;[173]=7;[174]=4;[175]=5;[176]=11;[177]=10;[178]=9;[179]=8;[180]=15;[181]=14;[182]=13;[183]=12;[184]=3;[185]=2;[186]=1;[187]=0;[188]=7;[189]=6;[190]=5;[191]=4;[192]=12;[193]=13;[194]=14;[195]=15;[196]=8;[197]=9;[198]=10;[199]=11;[200]=4;[201]=5;[202]=6;[203]=7;[204]=0;[205]=1;[206]=2;[207]=3;[208]=13;[209]=12;[210]=15;[211]=14;[212]=9;[213]=8;[214]=11;[215]=10;[216]=5;[217]=4;[218]=7;[219]=6;[220]=1;[221]=0;[222]=3;[223]=2;[224]=14;[225]=15;[226]=12;[227]=13;[228]=10;[229]=11;[230]=8;[231]=9;[232]=6;[233]=7;[234]=4;[235]=5;[236]=2;[237]=3;[238]=0;[239]=1;[240]=15;[241]=14;[242]=13;[243]=12;[244]=11;[245]=10;[246]=9;[247]=8;[248]=7;[249]=6;[250]=5;[251]=4;[252]=3;[253]=2;[254]=1;[255]=0;}; | |
4617 local or_map = {[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=1;[18]=3;[19]=3;[20]=5;[21]=5;[22]=7;[23]=7;[24]=9;[25]=9;[26]=11;[27]=11;[28]=13;[29]=13;[30]=15;[31]=15;[32]=2;[33]=3;[34]=2;[35]=3;[36]=6;[37]=7;[38]=6;[39]=7;[40]=10;[41]=11;[42]=10;[43]=11;[44]=14;[45]=15;[46]=14;[47]=15;[48]=3;[49]=3;[50]=3;[51]=3;[52]=7;[53]=7;[54]=7;[55]=7;[56]=11;[57]=11;[58]=11;[59]=11;[60]=15;[61]=15;[62]=15;[63]=15;[64]=4;[65]=5;[66]=6;[67]=7;[68]=4;[69]=5;[70]=6;[71]=7;[72]=12;[73]=13;[74]=14;[75]=15;[76]=12;[77]=13;[78]=14;[79]=15;[80]=5;[81]=5;[82]=7;[83]=7;[84]=5;[85]=5;[86]=7;[87]=7;[88]=13;[89]=13;[90]=15;[91]=15;[92]=13;[93]=13;[94]=15;[95]=15;[96]=6;[97]=7;[98]=6;[99]=7;[100]=6;[101]=7;[102]=6;[103]=7;[104]=14;[105]=15;[106]=14;[107]=15;[108]=14;[109]=15;[110]=14;[111]=15;[112]=7;[113]=7;[114]=7;[115]=7;[116]=7;[117]=7;[118]=7;[119]=7;[120]=15;[121]=15;[122]=15;[123]=15;[124]=15;[125]=15;[126]=15;[127]=15;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=8;[137]=9;[138]=10;[139]=11;[140]=12;[141]=13;[142]=14;[143]=15;[144]=9;[145]=9;[146]=11;[147]=11;[148]=13;[149]=13;[150]=15;[151]=15;[152]=9;[153]=9;[154]=11;[155]=11;[156]=13;[157]=13;[158]=15;[159]=15;[160]=10;[161]=11;[162]=10;[163]=11;[164]=14;[165]=15;[166]=14;[167]=15;[168]=10;[169]=11;[170]=10;[171]=11;[172]=14;[173]=15;[174]=14;[175]=15;[176]=11;[177]=11;[178]=11;[179]=11;[180]=15;[181]=15;[182]=15;[183]=15;[184]=11;[185]=11;[186]=11;[187]=11;[188]=15;[189]=15;[190]=15;[191]=15;[192]=12;[193]=13;[194]=14;[195]=15;[196]=12;[197]=13;[198]=14;[199]=15;[200]=12;[201]=13;[202]=14;[203]=15;[204]=12;[205]=13;[206]=14;[207]=15;[208]=13;[209]=13;[210]=15;[211]=15;[212]=13;[213]=13;[214]=15;[215]=15;[216]=13;[217]=13;[218]=15;[219]=15;[220]=13;[221]=13;[222]=15;[223]=15;[224]=14;[225]=15;[226]=14;[227]=15;[228]=14;[229]=15;[230]=14;[231]=15;[232]=14;[233]=15;[234]=14;[235]=15;[236]=14;[237]=15;[238]=14;[239]=15;[240]=15;[241]=15;[242]=15;[243]=15;[244]=15;[245]=15;[246]=15;[247]=15;[248]=15;[249]=15;[250]=15;[251]=15;[252]=15;[253]=15;[254]=15;[255]=15;}; | |
4618 local and_map = {[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=0;[9]=0;[10]=0;[11]=0;[12]=0;[13]=0;[14]=0;[15]=0;[16]=0;[17]=1;[18]=0;[19]=1;[20]=0;[21]=1;[22]=0;[23]=1;[24]=0;[25]=1;[26]=0;[27]=1;[28]=0;[29]=1;[30]=0;[31]=1;[32]=0;[33]=0;[34]=2;[35]=2;[36]=0;[37]=0;[38]=2;[39]=2;[40]=0;[41]=0;[42]=2;[43]=2;[44]=0;[45]=0;[46]=2;[47]=2;[48]=0;[49]=1;[50]=2;[51]=3;[52]=0;[53]=1;[54]=2;[55]=3;[56]=0;[57]=1;[58]=2;[59]=3;[60]=0;[61]=1;[62]=2;[63]=3;[64]=0;[65]=0;[66]=0;[67]=0;[68]=4;[69]=4;[70]=4;[71]=4;[72]=0;[73]=0;[74]=0;[75]=0;[76]=4;[77]=4;[78]=4;[79]=4;[80]=0;[81]=1;[82]=0;[83]=1;[84]=4;[85]=5;[86]=4;[87]=5;[88]=0;[89]=1;[90]=0;[91]=1;[92]=4;[93]=5;[94]=4;[95]=5;[96]=0;[97]=0;[98]=2;[99]=2;[100]=4;[101]=4;[102]=6;[103]=6;[104]=0;[105]=0;[106]=2;[107]=2;[108]=4;[109]=4;[110]=6;[111]=6;[112]=0;[113]=1;[114]=2;[115]=3;[116]=4;[117]=5;[118]=6;[119]=7;[120]=0;[121]=1;[122]=2;[123]=3;[124]=4;[125]=5;[126]=6;[127]=7;[128]=0;[129]=0;[130]=0;[131]=0;[132]=0;[133]=0;[134]=0;[135]=0;[136]=8;[137]=8;[138]=8;[139]=8;[140]=8;[141]=8;[142]=8;[143]=8;[144]=0;[145]=1;[146]=0;[147]=1;[148]=0;[149]=1;[150]=0;[151]=1;[152]=8;[153]=9;[154]=8;[155]=9;[156]=8;[157]=9;[158]=8;[159]=9;[160]=0;[161]=0;[162]=2;[163]=2;[164]=0;[165]=0;[166]=2;[167]=2;[168]=8;[169]=8;[170]=10;[171]=10;[172]=8;[173]=8;[174]=10;[175]=10;[176]=0;[177]=1;[178]=2;[179]=3;[180]=0;[181]=1;[182]=2;[183]=3;[184]=8;[185]=9;[186]=10;[187]=11;[188]=8;[189]=9;[190]=10;[191]=11;[192]=0;[193]=0;[194]=0;[195]=0;[196]=4;[197]=4;[198]=4;[199]=4;[200]=8;[201]=8;[202]=8;[203]=8;[204]=12;[205]=12;[206]=12;[207]=12;[208]=0;[209]=1;[210]=0;[211]=1;[212]=4;[213]=5;[214]=4;[215]=5;[216]=8;[217]=9;[218]=8;[219]=9;[220]=12;[221]=13;[222]=12;[223]=13;[224]=0;[225]=0;[226]=2;[227]=2;[228]=4;[229]=4;[230]=6;[231]=6;[232]=8;[233]=8;[234]=10;[235]=10;[236]=12;[237]=12;[238]=14;[239]=14;[240]=0;[241]=1;[242]=2;[243]=3;[244]=4;[245]=5;[246]=6;[247]=7;[248]=8;[249]=9;[250]=10;[251]=11;[252]=12;[253]=13;[254]=14;[255]=15;} | |
4619 | |
4620 local not_map = {[0]=15;[1]=14;[2]=13;[3]=12;[4]=11;[5]=10;[6]=9;[7]=8;[8]=7;[9]=6;[10]=5;[11]=4;[12]=3;[13]=2;[14]=1;[15]=0;}; | |
4621 local rshift1_map = {[0]=0;[1]=0;[2]=1;[3]=1;[4]=2;[5]=2;[6]=3;[7]=3;[8]=4;[9]=4;[10]=5;[11]=5;[12]=6;[13]=6;[14]=7;[15]=7;}; | |
4622 local rshift1carry_map = {[0]=0;[1]=8;[2]=0;[3]=8;[4]=0;[5]=8;[6]=0;[7]=8;[8]=0;[9]=8;[10]=0;[11]=8;[12]=0;[13]=8;[14]=0;[15]=8;}; | |
4623 local lshift1_map = {[0]=0;[1]=2;[2]=4;[3]=6;[4]=8;[5]=10;[6]=12;[7]=14;[8]=0;[9]=2;[10]=4;[11]=6;[12]=8;[13]=10;[14]=12;[15]=14;}; | |
4624 local lshift1carry_map = {[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=1;[9]=1;[10]=1;[11]=1;[12]=1;[13]=1;[14]=1;[15]=1;}; | |
4625 local arshift1carry_map = {[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=8;[9]=8;[10]=8;[11]=8;[12]=8;[13]=8;[14]=8;[15]=8;}; | |
4626 | |
4627 module "bit" | |
4628 | |
4629 local bit_mt = {__tostring = function(t) return ("%x%x%x%x%x%x%x%x"):format(t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8]); end}; | |
4630 local function do_bop(a, b, op) | |
4631 return setmetatable({ | |
4632 op[a[1]*16+b[1]]; | |
4633 op[a[2]*16+b[2]]; | |
4634 op[a[3]*16+b[3]]; | |
4635 op[a[4]*16+b[4]]; | |
4636 op[a[5]*16+b[5]]; | |
4637 op[a[6]*16+b[6]]; | |
4638 op[a[7]*16+b[7]]; | |
4639 op[a[8]*16+b[8]]; | |
4640 }, bit_mt); | |
4641 end | |
4642 local function do_uop(a, op) | |
4643 return setmetatable({ | |
4644 op[a[1]]; | |
4645 op[a[2]]; | |
4646 op[a[3]]; | |
4647 op[a[4]]; | |
4648 op[a[5]]; | |
4649 op[a[6]]; | |
4650 op[a[7]]; | |
4651 op[a[8]]; | |
4652 }, bit_mt); | |
4653 end | |
4654 | |
4655 function bxor(a, b) return do_bop(a, b, xor_map); end | |
4656 function bor(a, b) return do_bop(a, b, or_map); end | |
4657 function band(a, b) return do_bop(a, b, and_map); end | |
4658 | |
4659 function bnot(a) return do_uop(a, not_map); end | |
4660 local function _rshift1(t) | |
4661 local carry = 0; | |
4662 for i=1,8 do | |
4663 local t_i = rshift1_map[t[i]] + carry; | |
4664 carry = rshift1carry_map[t[i]]; | |
4665 t[i] = t_i; | |
4666 end | |
4667 end | |
4668 function rshift(a, i) | |
4669 local t = {a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]}; | |
4670 for n = 1,i do _rshift1(t); end | |
4671 return setmetatable(t, bit_mt); | |
4672 end | |
4673 local function _arshift1(t) | |
4674 local carry = arshift1carry_map[t[1]]; | |
4675 for i=1,8 do | |
4676 local t_i = rshift1_map[t[i]] + carry; | |
4677 carry = rshift1carry_map[t[i]]; | |
4678 t[i] = t_i; | |
4679 end | |
4680 end | |
4681 function arshift(a, i) | |
4682 local t = {a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]}; | |
4683 for n = 1,i do _arshift1(t); end | |
4684 return setmetatable(t, bit_mt); | |
4685 end | |
4686 local function _lshift1(t) | |
4687 local carry = 0; | |
4688 for i=8,1,-1 do | |
4689 local t_i = lshift1_map[t[i]] + carry; | |
4690 carry = lshift1carry_map[t[i]]; | |
4691 t[i] = t_i; | |
4692 end | |
4693 end | |
4694 function lshift(a, i) | |
4695 local t = {a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]}; | |
4696 for n = 1,i do _lshift1(t); end | |
4697 return setmetatable(t, bit_mt); | |
4698 end | |
4699 | |
4700 local function _cast(a) | |
4701 if type(a) == "number" then a = ("%x"):format(a); | |
4702 elseif type(a) == "table" then return a; | |
4703 elseif type(a) ~= "string" then error("string expected, got "..type(a), 2); end | |
4704 local t = {0,0,0,0,0,0,0,0}; | |
4705 a = "00000000"..a; | |
4706 a = a:sub(-8); | |
4707 for i = 1,8 do | |
4708 t[i] = tonumber(a:sub(i,i), 16) or error("Number format error", 2); | |
4709 end | |
4710 return setmetatable(t, bit_mt); | |
4711 end | |
4712 | |
4713 local function wrap1(f) | |
4714 return function(a, ...) | |
4715 if type(a) ~= "table" then a = _cast(a); end | |
4716 a = f(a, ...); | |
4717 a = tonumber(tostring(a), 16); | |
4718 if a > 0x7fffffff then a = a - 1 - 0xffffffff; end | |
4719 return a; | |
4720 end; | |
4721 end | |
4722 local function wrap2(f) | |
4723 return function(a, b, ...) | |
4724 if type(a) ~= "table" then a = _cast(a); end | |
4725 if type(b) ~= "table" then b = _cast(b); end | |
4726 a = f(a, b, ...); | |
4727 a = tonumber(tostring(a), 16); | |
4728 if a > 0x7fffffff then a = a - 1 - 0xffffffff; end | |
4729 return a; | |
4730 end; | |
4731 end | |
4732 | |
4733 bxor = wrap2(bxor); | |
4734 bor = wrap2(bor); | |
4735 band = wrap2(band); | |
4736 bnot = wrap1(bnot); | |
4737 lshift = wrap1(lshift); | |
4738 rshift = wrap1(rshift); | |
4739 arshift = wrap1(arshift); | |
4740 cast = wrap1(_cast); | |
4741 | |
4742 bits = 32; | |
4743 | |
4744 return _M; | |
4745 end) | |
4746 package.preload['verse.client'] = (function (...) | |
4747 local verse = require "verse"; | |
4748 local stream = verse.stream_mt; | |
4749 | |
4750 local jid_split = require "util.jid".split; | |
4751 local adns = require "net.adns"; | |
4752 local lxp = require "lxp"; | |
4753 local st = require "util.stanza"; | |
4754 | |
4755 -- Shortcuts to save having to load util.stanza | |
4756 verse.message, verse.presence, verse.iq, verse.stanza, verse.reply, verse.error_reply = | |
4757 st.message, st.presence, st.iq, st.stanza, st.reply, st.error_reply; | |
4758 | |
4759 local new_xmpp_stream = require "util.xmppstream".new; | |
4760 | |
4761 local xmlns_stream = "http://etherx.jabber.org/streams"; | |
4762 | |
4763 local function compare_srv_priorities(a,b) | |
4764 return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight); | |
4765 end | |
4766 | |
4767 local stream_callbacks = { | |
4768 stream_ns = xmlns_stream, | |
4769 stream_tag = "stream", | |
4770 default_ns = "jabber:client" }; | |
4771 | |
4772 function stream_callbacks.streamopened(stream, attr) | |
4773 stream.stream_id = attr.id; | |
4774 if not stream:event("opened", attr) then | |
4775 stream.notopen = nil; | |
4776 end | |
4777 return true; | |
4778 end | |
4779 | |
4780 function stream_callbacks.streamclosed(stream) | |
4781 return stream:event("closed"); | |
4782 end | |
4783 | |
4784 function stream_callbacks.handlestanza(stream, stanza) | |
4785 if stanza.attr.xmlns == xmlns_stream then | |
4786 return stream:event("stream-"..stanza.name, stanza); | |
4787 elseif stanza.attr.xmlns then | |
4788 return stream:event("stream/"..stanza.attr.xmlns, stanza); | |
4789 end | |
4790 | |
4791 return stream:event("stanza", stanza); | |
4792 end | |
4793 | |
4794 function stream:reset() | |
4795 if self.stream then | |
4796 self.stream:reset(); | |
4797 else | |
4798 self.stream = new_xmpp_stream(self, stream_callbacks); | |
4799 end | |
4800 self.notopen = true; | |
4801 return true; | |
4802 end | |
4803 | |
4804 function stream:connect_client(jid, pass) | |
4805 self.jid, self.password = jid, pass; | |
4806 self.username, self.host, self.resource = jid_split(jid); | |
4807 | |
4808 -- Required XMPP features | |
4809 self:add_plugin("tls"); | |
4810 self:add_plugin("sasl"); | |
4811 self:add_plugin("bind"); | |
4812 self:add_plugin("session"); | |
4813 | |
4814 function self.data(conn, data) | |
4815 local ok, err = self.stream:feed(data); | |
4816 if ok then return; end | |
4817 self:debug("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " ")); | |
4818 self:close("xml-not-well-formed"); | |
4819 end | |
4820 | |
4821 self:hook("connected", function () self:reopen(); end); | |
4822 self:hook("incoming-raw", function (data) return self.data(self.conn, data); end); | |
4823 | |
4824 self.curr_id = 0; | |
4825 | |
4826 self.tracked_iqs = {}; | |
4827 self:hook("stanza", function (stanza) | |
4828 local id, type = stanza.attr.id, stanza.attr.type; | |
4829 if id and stanza.name == "iq" and (type == "result" or type == "error") and self.tracked_iqs[id] then | |
4830 self.tracked_iqs[id](stanza); | |
4831 self.tracked_iqs[id] = nil; | |
4832 return true; | |
4833 end | |
4834 end); | |
4835 | |
4836 self:hook("stanza", function (stanza) | |
4837 if stanza.attr.xmlns == nil or stanza.attr.xmlns == "jabber:client" then | |
4838 if stanza.name == "iq" and (stanza.attr.type == "get" or stanza.attr.type == "set") then | |
4839 local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns; | |
4840 if xmlns then | |
4841 ret = self:event("iq/"..xmlns, stanza); | |
4842 if not ret then | |
4843 ret = self:event("iq", stanza); | |
4844 end | |
4845 end | |
4846 if ret == nil then | |
4847 self:send(verse.error_reply(stanza, "cancel", "service-unavailable")); | |
4848 return true; | |
4849 end | |
4850 else | |
4851 ret = self:event(stanza.name, stanza); | |
4852 end | |
4853 end | |
4854 return ret; | |
4855 end, -1); | |
4856 | |
4857 self:hook("outgoing", function (data) | |
4858 if data.name then | |
4859 self:event("stanza-out", data); | |
4860 end | |
4861 end); | |
4862 | |
4863 self:hook("stanza-out", function (stanza) | |
4864 if not stanza.attr.xmlns then | |
4865 self:event(stanza.name.."-out", stanza); | |
4866 end | |
4867 end); | |
4868 | |
4869 local function stream_ready() | |
4870 self:event("ready"); | |
4871 end | |
4872 self:hook("session-success", stream_ready, -1) | |
4873 self:hook("bind-success", stream_ready, -1); | |
4874 | |
4875 local _base_close = self.close; | |
4876 function self:close(reason) | |
4877 if not self.notopen then | |
4878 self:send("</stream:stream>"); | |
4879 end | |
4880 return _base_close(self); | |
4881 end | |
4882 | |
4883 local function start_connect() | |
4884 -- Initialise connection | |
4885 self:connect(self.connect_host or self.host, self.connect_port or 5222); | |
4886 end | |
4887 | |
4888 if not (self.connect_host or self.connect_port) then | |
4889 -- Look up SRV records | |
4890 adns.lookup(function (answer) | |
4891 if answer then | |
4892 local srv_hosts = {}; | |
4893 self.srv_hosts = srv_hosts; | |
4894 for _, record in ipairs(answer) do | |
4895 table.insert(srv_hosts, record.srv); | |
4896 end | |
4897 table.sort(srv_hosts, compare_srv_priorities); | |
4898 | |
4899 local srv_choice = srv_hosts[1]; | |
4900 self.srv_choice = 1; | |
4901 if srv_choice then | |
4902 self.connect_host, self.connect_port = srv_choice.target, srv_choice.port; | |
4903 self:debug("Best record found, will connect to %s:%d", self.connect_host or self.host, self.connect_port or 5222); | |
4904 end | |
4905 | |
4906 self:hook("disconnected", function () | |
4907 if self.srv_hosts and self.srv_choice < #self.srv_hosts then | |
4908 self.srv_choice = self.srv_choice + 1; | |
4909 local srv_choice = srv_hosts[self.srv_choice]; | |
4910 self.connect_host, self.connect_port = srv_choice.target, srv_choice.port; | |
4911 start_connect(); | |
4912 return true; | |
4913 end | |
4914 end, 1000); | |
4915 | |
4916 self:hook("connected", function () | |
4917 self.srv_hosts = nil; | |
4918 end, 1000); | |
4919 end | |
4920 start_connect(); | |
4921 end, "_xmpp-client._tcp."..(self.host)..".", "SRV"); | |
4922 else | |
4923 start_connect(); | |
4924 end | |
4925 end | |
4926 | |
4927 function stream:reopen() | |
4928 self:reset(); | |
4929 self:send(st.stanza("stream:stream", { to = self.host, ["xmlns:stream"]='http://etherx.jabber.org/streams', | |
4930 xmlns = "jabber:client", version = "1.0" }):top_tag()); | |
4931 end | |
4932 | |
4933 function stream:send_iq(iq, callback) | |
4934 local id = self:new_id(); | |
4935 self.tracked_iqs[id] = callback; | |
4936 iq.attr.id = id; | |
4937 self:send(iq); | |
4938 end | |
4939 | |
4940 function stream:new_id() | |
4941 self.curr_id = self.curr_id + 1; | |
4942 return tostring(self.curr_id); | |
4943 end | |
4944 end) | |
4945 package.preload['verse.component'] = (function (...) | |
4946 local verse = require "verse"; | |
4947 local stream = verse.stream_mt; | |
4948 | |
4949 local jid_split = require "util.jid".split; | |
4950 local lxp = require "lxp"; | |
4951 local st = require "util.stanza"; | |
4952 local sha1 = require "util.sha1".sha1; | |
4953 | |
4954 -- Shortcuts to save having to load util.stanza | |
4955 verse.message, verse.presence, verse.iq, verse.stanza, verse.reply, verse.error_reply = | |
4956 st.message, st.presence, st.iq, st.stanza, st.reply, st.error_reply; | |
4957 | |
4958 local new_xmpp_stream = require "util.xmppstream".new; | |
4959 | |
4960 local xmlns_stream = "http://etherx.jabber.org/streams"; | |
4961 local xmlns_component = "jabber:component:accept"; | |
4962 | |
4963 local stream_callbacks = { | |
4964 stream_ns = xmlns_stream, | |
4965 stream_tag = "stream", | |
4966 default_ns = xmlns_component }; | |
4967 | |
4968 function stream_callbacks.streamopened(stream, attr) | |
4969 stream.stream_id = attr.id; | |
4970 if not stream:event("opened", attr) then | |
4971 stream.notopen = nil; | |
4972 end | |
4973 return true; | |
4974 end | |
4975 | |
4976 function stream_callbacks.streamclosed(stream) | |
4977 return stream:event("closed"); | |
4978 end | |
4979 | |
4980 function stream_callbacks.handlestanza(stream, stanza) | |
4981 if stanza.attr.xmlns == xmlns_stream then | |
4982 return stream:event("stream-"..stanza.name, stanza); | |
4983 elseif stanza.attr.xmlns or stanza.name == "handshake" then | |
4984 return stream:event("stream/"..(stanza.attr.xmlns or xmlns_component), stanza); | |
4985 end | |
4986 | |
4987 return stream:event("stanza", stanza); | |
4988 end | |
4989 | |
4990 function stream:reset() | |
4991 if self.stream then | |
4992 self.stream:reset(); | |
4993 else | |
4994 self.stream = new_xmpp_stream(self, stream_callbacks); | |
4995 end | |
4996 self.notopen = true; | |
4997 return true; | |
4998 end | |
4999 | |
5000 function stream:connect_component(jid, pass) | |
5001 self.jid, self.password = jid, pass; | |
5002 self.username, self.host, self.resource = jid_split(jid); | |
5003 | |
5004 function self.data(conn, data) | |
5005 local ok, err = self.stream:feed(data); | |
5006 if ok then return; end | |
5007 stream:debug("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " ")); | |
5008 stream:close("xml-not-well-formed"); | |
5009 end | |
5010 | |
5011 self:hook("incoming-raw", function (data) return self.data(self.conn, data); end); | |
5012 | |
5013 self.curr_id = 0; | |
5014 | |
5015 self.tracked_iqs = {}; | |
5016 self:hook("stanza", function (stanza) | |
5017 local id, type = stanza.attr.id, stanza.attr.type; | |
5018 if id and stanza.name == "iq" and (type == "result" or type == "error") and self.tracked_iqs[id] then | |
5019 self.tracked_iqs[id](stanza); | |
5020 self.tracked_iqs[id] = nil; | |
5021 return true; | |
5022 end | |
5023 end); | |
5024 | |
5025 self:hook("stanza", function (stanza) | |
5026 if stanza.attr.xmlns == nil or stanza.attr.xmlns == "jabber:client" then | |
5027 if stanza.name == "iq" and (stanza.attr.type == "get" or stanza.attr.type == "set") then | |
5028 local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns; | |
5029 if xmlns then | |
5030 ret = self:event("iq/"..xmlns, stanza); | |
5031 if not ret then | |
5032 ret = self:event("iq", stanza); | |
5033 end | |
5034 end | |
5035 if ret == nil then | |
5036 self:send(verse.error_reply(stanza, "cancel", "service-unavailable")); | |
5037 return true; | |
5038 end | |
5039 else | |
5040 ret = self:event(stanza.name, stanza); | |
5041 end | |
5042 end | |
5043 return ret; | |
5044 end, -1); | |
5045 | |
5046 self:hook("opened", function (attr) | |
5047 print(self.jid, self.stream_id, attr.id); | |
5048 local token = sha1(self.stream_id..pass, true); | |
5049 | |
5050 self:send(st.stanza("handshake", { xmlns = xmlns_component }):text(token)); | |
5051 self:hook("stream/"..xmlns_component, function (stanza) | |
5052 if stanza.name == "handshake" then | |
5053 self:event("authentication-success"); | |
5054 end | |
5055 end); | |
5056 end); | |
5057 | |
5058 local function stream_ready() | |
5059 self:event("ready"); | |
5060 end | |
5061 self:hook("authentication-success", stream_ready, -1); | |
5062 | |
5063 -- Initialise connection | |
5064 self:connect(self.connect_host or self.host, self.connect_port or 5347); | |
5065 self:reopen(); | |
5066 end | |
5067 | |
5068 function stream:reopen() | |
5069 self:reset(); | |
5070 self:send(st.stanza("stream:stream", { to = self.host, ["xmlns:stream"]='http://etherx.jabber.org/streams', | |
5071 xmlns = xmlns_component, version = "1.0" }):top_tag()); | |
5072 end | |
5073 | |
5074 function stream:close(reason) | |
5075 if not self.notopen then | |
5076 self:send("</stream:stream>"); | |
5077 end | |
5078 local on_disconnect = self.conn.disconnect(); | |
5079 self.conn:close(); | |
5080 on_disconnect(conn, reason); | |
5081 end | |
5082 | |
5083 function stream:send_iq(iq, callback) | |
5084 local id = self:new_id(); | |
5085 self.tracked_iqs[id] = callback; | |
5086 iq.attr.id = id; | |
5087 self:send(iq); | |
5088 end | |
5089 | |
5090 function stream:new_id() | |
5091 self.curr_id = self.curr_id + 1; | |
5092 return tostring(self.curr_id); | |
5093 end | |
5094 end) | |
5095 | |
5096 -- Use LuaRocks if available | |
5097 pcall(require, "luarocks.require"); | |
5098 | |
5099 -- Load LuaSec if available | |
5100 pcall(require, "ssl"); | |
5101 | |
5102 local server = require "net.server"; | |
5103 local events = require "util.events"; | |
5104 | |
5105 module("verse", package.seeall); | |
5106 local verse = _M; | |
5107 _M.server = server; | |
5108 | |
5109 local stream = {}; | |
5110 stream.__index = stream; | |
5111 stream_mt = stream; | |
5112 | |
5113 verse.plugins = {}; | |
5114 | |
5115 function verse.new(logger, base) | |
5116 local t = setmetatable(base or {}, stream); | |
5117 t.id = tostring(t):match("%x*$"); | |
5118 t:set_logger(logger, true); | |
5119 t.events = events.new(); | |
5120 t.plugins = {}; | |
5121 return t; | |
5122 end | |
5123 | |
5124 verse.add_task = require "util.timer".add_task; | |
5125 | |
5126 verse.logger = logger.init; | |
5127 verse.log = verse.logger("verse"); | |
5128 | |
5129 function verse.set_logger(logger) | |
5130 verse.log = logger; | |
5131 server.setlogger(logger); | |
5132 end | |
5133 | |
5134 function verse.filter_log(levels, logger) | |
5135 local level_set = {}; | |
5136 for _, level in ipairs(levels) do | |
5137 level_set[level] = true; | |
5138 end | |
5139 return function (level, name, ...) | |
5140 if level_set[level] then | |
5141 return logger(level, name, ...); | |
5142 end | |
5143 end; | |
5144 end | |
5145 | |
5146 local function error_handler(err) | |
5147 verse.log("error", "Error: %s", err); | |
5148 verse.log("error", "Traceback: %s", debug.traceback()); | |
5149 end | |
5150 | |
5151 function verse.set_error_handler(new_error_handler) | |
5152 error_handler = new_error_handler; | |
5153 end | |
5154 | |
5155 function verse.loop() | |
5156 return xpcall(server.loop, error_handler); | |
5157 end | |
5158 | |
5159 function verse.step() | |
5160 return xpcall(server.step, error_handler); | |
5161 end | |
5162 | |
5163 function verse.quit() | |
5164 return server.setquitting(true); | |
5165 end | |
5166 | |
5167 function stream:connect(connect_host, connect_port) | |
5168 connect_host = connect_host or "localhost"; | |
5169 connect_port = tonumber(connect_port) or 5222; | |
5170 | |
5171 -- Create and initiate connection | |
5172 local conn = socket.tcp() | |
5173 conn:settimeout(0); | |
5174 local success, err = conn:connect(connect_host, connect_port); | |
5175 | |
5176 if not success and err ~= "timeout" then | |
5177 self:warn("connect() to %s:%d failed: %s", connect_host, connect_port, err); | |
5178 return self:event("disconnected", { reason = err }) or false, err; | |
5179 end | |
5180 | |
5181 local conn = server.wrapclient(conn, connect_host, connect_port, new_listener(self), "*a"); | |
5182 if not conn then | |
5183 self:warn("connection initialisation failed: %s", err); | |
5184 return self:event("disconnected", { reason = err }) or false, err; | |
5185 end | |
5186 | |
5187 self.conn = conn; | |
5188 self.send = function (stream, data) | |
5189 self:event("outgoing", data); | |
5190 data = tostring(data); | |
5191 self:event("outgoing-raw", data); | |
5192 return conn:write(data); | |
5193 end; | |
5194 return true; | |
5195 end | |
5196 | |
5197 function stream:close() | |
5198 if not self.conn then | |
5199 verse.log("error", "Attempt to close disconnected connection - possibly a bug"); | |
5200 return; | |
5201 end | |
5202 local on_disconnect = self.conn.disconnect(); | |
5203 self.conn:close(); | |
5204 on_disconnect(conn, reason); | |
5205 end | |
5206 | |
5207 -- Logging functions | |
5208 function stream:debug(...) | |
5209 if self.logger and self.log.debug then | |
5210 return self.logger("debug", ...); | |
5211 end | |
5212 end | |
5213 | |
5214 function stream:warn(...) | |
5215 if self.logger and self.log.warn then | |
5216 return self.logger("warn", ...); | |
5217 end | |
5218 end | |
5219 | |
5220 function stream:error(...) | |
5221 if self.logger and self.log.error then | |
5222 return self.logger("error", ...); | |
5223 end | |
5224 end | |
5225 | |
5226 function stream:set_logger(logger, levels) | |
5227 local old_logger = self.logger; | |
5228 if logger then | |
5229 self.logger = logger; | |
5230 end | |
5231 if levels then | |
5232 if levels == true then | |
5233 levels = { "debug", "info", "warn", "error" }; | |
5234 end | |
5235 self.log = {}; | |
5236 for _, level in ipairs(levels) do | |
5237 self.log[level] = true; | |
5238 end | |
5239 end | |
5240 return old_logger; | |
5241 end | |
5242 | |
5243 function stream_mt:set_log_levels(levels) | |
5244 self:set_logger(nil, levels); | |
5245 end | |
5246 | |
5247 -- Event handling | |
5248 function stream:event(name, ...) | |
5249 self:debug("Firing event: "..tostring(name)); | |
5250 return self.events.fire_event(name, ...); | |
5251 end | |
5252 | |
5253 function stream:hook(name, ...) | |
5254 return self.events.add_handler(name, ...); | |
5255 end | |
5256 | |
5257 function stream:unhook(name, handler) | |
5258 return self.events.remove_handler(name, handler); | |
5259 end | |
5260 | |
5261 function verse.eventable(object) | |
5262 object.events = events.new(); | |
5263 object.hook, object.unhook = stream.hook, stream.unhook; | |
5264 local fire_event = object.events.fire_event; | |
5265 function object:event(name, ...) | |
5266 return fire_event(name, ...); | |
5267 end | |
5268 return object; | |
5269 end | |
5270 | |
5271 function stream:add_plugin(name) | |
5272 if self.plugins[name] then return true; end | |
5273 if require("verse.plugins."..name) then | |
5274 local ok, err = verse.plugins[name](self); | |
5275 if ok ~= false then | |
5276 self:debug("Loaded %s plugin", name); | |
5277 self.plugins[name] = true; | |
5278 else | |
5279 self:warn("Failed to load %s plugin: %s", name, err); | |
5280 end | |
5281 end | |
5282 return self; | |
5283 end | |
5284 | |
5285 -- Listener factory | |
5286 function new_listener(stream) | |
5287 local conn_listener = {}; | |
5288 | |
5289 function conn_listener.onconnect(conn) | |
5290 stream.connected = true; | |
5291 stream:event("connected"); | |
5292 end | |
5293 | |
5294 function conn_listener.onincoming(conn, data) | |
5295 stream:event("incoming-raw", data); | |
5296 end | |
5297 | |
5298 function conn_listener.ondisconnect(conn, err) | |
5299 stream.connected = false; | |
5300 stream:event("disconnected", { reason = err }); | |
5301 end | |
5302 | |
5303 function conn_listener.ondrain(conn) | |
5304 stream:event("drained"); | |
5305 end | |
5306 | |
5307 function conn_listener.onstatus(conn, new_status) | |
5308 stream:event("status", new_status); | |
5309 end | |
5310 | |
5311 return conn_listener; | |
5312 end | |
5313 | |
5314 | |
5315 local log = require "util.logger".init("verse"); | |
5316 | |
5317 return verse; | |
5318 end) | |
1 -- README | 5319 -- README |
2 -- Squish verse into this dir, then squish them into one, which you move | 5320 -- Squish verse into this dir, then squish them into one, which you move |
3 -- and rename to mod_ircd.lua in your prosody modules/plugins dir. | 5321 -- and rename to mod_ircd.lua in your prosody modules/plugins dir. |
4 -- | 5322 -- |
5 -- IRC spec: | 5323 -- IRC spec: |
179 end | 5497 end |
180 session.send(":"..nick.." PRIVMSG "..channel.." :"..body); | 5498 session.send(":"..nick.." PRIVMSG "..channel.." :"..body); |
181 --FIXME PM's probably won't work | 5499 --FIXME PM's probably won't work |
182 end | 5500 end |
183 end); | 5501 end); |
5502 room:hook("subject-changed", function(changed) | |
5503 session.send((":%s TOPIC %s :%s"):format(changed.by, channel, changed.to or "")); | |
5504 end); | |
184 end | 5505 end |
185 | 5506 |
186 c:hook("groupchat/joined", function(room) | 5507 c:hook("groupchat/joined", function(room) |
187 local session = room.session or jids[room.opts.source]; | 5508 local session = room.session or jids[room.opts.source]; |
188 local channel = room.channel; | 5509 local channel = room.channel; |
238 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); | 5559 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); |
239 session.rooms[channel]:send_message(message); | 5560 session.rooms[channel]:send_message(message); |
240 end | 5561 end |
241 end | 5562 end |
242 | 5563 |
5564 function commands.TOPIC(session, message) | |
5565 if not message then return end | |
5566 local channel, topic = message:match("^(%S+) :(.*)$"); | |
5567 if not channel then | |
5568 channel = message:match("^(%S+)"); | |
5569 end | |
5570 if not channel then return end | |
5571 local room = session.rooms[channel]; | |
5572 if topic then | |
5573 room:set_subject(topic) | |
5574 else | |
5575 session.send((":%s TOPIC %s :%s"):format(session.host, channel, room.subject or "")); | |
5576 -- first should be who set it, but verse doesn't provide that yet, so we'll | |
5577 -- just say it was the server | |
5578 end | |
5579 end | |
5580 | |
243 function commands.PING(session, server) | 5581 function commands.PING(session, server) |
244 session.send(":"..session.host..": PONG "..server); | 5582 session.send(":"..session.host..": PONG "..server); |
245 end | 5583 end |
246 | 5584 |
247 function commands.WHO(session, channel) | 5585 function commands.WHO(session, channel) |
272 | 5610 |
273 function commands.RAW(session, data) | 5611 function commands.RAW(session, data) |
274 --c:send(data) | 5612 --c:send(data) |
275 end | 5613 end |
276 | 5614 |
5615 local function desetup() | |
5616 require "net.connlisteners".deregister("irc"); | |
5617 end | |
5618 | |
277 --c:hook("ready", function () | 5619 --c:hook("ready", function () |
278 require "net.connlisteners".register("irc", irc_listener); | 5620 require "net.connlisteners".register("irc", irc_listener); |
279 require "net.connlisteners".start("irc"); | 5621 require "net.connlisteners".start("irc"); |
280 --end); | 5622 --end); |
281 | 5623 |
5624 module:hook("module-unloaded", desetup) | |
282 --print("Starting loop...") | 5625 --print("Starting loop...") |
283 --verse.loop() | 5626 --verse.loop() |
284 | 5627 |
285 --[[ TODO | 5628 --[[ TODO |
286 | 5629 |