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 |
