# HG changeset patch # User Matthew Wild # Date 1441492143 -3600 # Node ID fc435d5678962413560c72a5aff2a9d41242742f # Parent 48c77c05b9ba9d18135b71295a6106f84b869252 Add verse for convenience diff -r 48c77c05b9ba -r fc435d567896 verse.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/verse.lua Sat Sep 05 23:29:03 2015 +0100 @@ -0,0 +1,7350 @@ +package.preload['util.encodings']=(function(...) +local function e() +error("Function not implemented"); +end +local t=require"mime"; +module"encodings" +stringprep={}; +base64={encode=t.b64,decode=e}; +return _M; +end) +package.preload['util.hashes']=(function(...) +local e=require"util.sha1"; +return{sha1=e.sha1}; +end) +package.preload['util.sha1']=(function(...) +local l=string.len +local a=string.char +local g=string.byte +local q=string.sub +local u=math.floor +local t=require"bit" +local j=t.bnot +local e=t.band +local y=t.bor +local n=t.bxor +local o=t.lshift +local i=t.rshift +local s,h,d,r,f +local function p(e,t) +return o(e,t)+i(e,32-t) +end +local function m(i) +local t,o +local t="" +for n=1,8 do +o=e(i,15) +if(o<10)then +t=a(o+48)..t +else +t=a(o+87)..t +end +i=u(i/16) +end +return t +end +local function b(t) +local i,o +local n="" +i=l(t)*8 +t=t..a(128) +o=56-e(l(t),63) +if(o<0)then +o=o+64 +end +for e=1,o do +t=t..a(0) +end +for t=1,8 do +n=a(e(i,255))..n +i=u(i/256) +end +return t..n +end +local function k(w) +local u,t,a,o,m,l,c,v +local i,i +local i={} +while(w~="")do +for e=0,15 do +i[e]=0 +for t=1,4 do +i[e]=i[e]*256+g(w,e*4+t) +end +end +for e=16,79 do +i[e]=p(n(n(i[e-3],i[e-8]),n(i[e-14],i[e-16])),1) +end +u=s +t=h +a=d +o=r +m=f +for h=0,79 do +if(h<20)then +l=y(e(t,a),e(j(t),o)) +c=1518500249 +elseif(h<40)then +l=n(n(t,a),o) +c=1859775393 +elseif(h<60)then +l=y(y(e(t,a),e(t,o)),e(a,o)) +c=2400959708 +else +l=n(n(t,a),o) +c=3395469782 +end +v=p(u,5)+l+m+c+i[h] +m=o +o=a +a=p(t,30) +t=u +u=v +end +s=e(s+u,4294967295) +h=e(h+t,4294967295) +d=e(d+a,4294967295) +r=e(r+o,4294967295) +f=e(f+m,4294967295) +w=q(w,65) +end +end +local function a(e,t) +e=b(e) +s=1732584193 +h=4023233417 +d=2562383102 +r=271733878 +f=3285377520 +k(e) +local e=m(s)..m(h)..m(d) +..m(r)..m(f); +if t then +return e; +else +return(e:gsub("..",function(e) +return string.char(tonumber(e,16)); +end)); +end +end +_G.sha1={sha1=a}; +return _G.sha1; +end) +package.preload['lib.adhoc']=(function(...) +local n,h=require"util.stanza",require"util.uuid"; +local e="http://jabber.org/protocol/commands"; +local i={} +local s={}; +function _cmdtag(i,o,t,a) +local e=n.stanza("command",{xmlns=e,node=i.node,status=o}); +if t then e.attr.sessionid=t;end +if a then e.attr.action=a;end +return e; +end +function s.new(t,e,a,o) +return{name=t,node=e,handler=a,cmdtag=_cmdtag,permission=(o or"user")}; +end +function s.handle_cmd(a,s,o) +local e=o.tags[1].attr.sessionid or h.generate(); +local t={}; +t.to=o.attr.to; +t.from=o.attr.from; +t.action=o.tags[1].attr.action or"execute"; +t.form=o.tags[1]:child_with_ns("jabber:x:data"); +local t,h=a:handler(t,i[e]); +i[e]=h; +local o=n.reply(o); +if t.status=="completed"then +i[e]=nil; +cmdtag=a:cmdtag("completed",e); +elseif t.status=="canceled"then +i[e]=nil; +cmdtag=a:cmdtag("canceled",e); +elseif t.status=="error"then +i[e]=nil; +o=n.error_reply(o,t.error.type,t.error.condition,t.error.message); +s.send(o); +return true; +else +cmdtag=a:cmdtag("executing",e); +end +for t,e in pairs(t)do +if t=="info"then +cmdtag:tag("note",{type="info"}):text(e):up(); +elseif t=="warn"then +cmdtag:tag("note",{type="warn"}):text(e):up(); +elseif t=="error"then +cmdtag:tag("note",{type="error"}):text(e.message):up(); +elseif t=="actions"then +local t=n.stanza("actions"); +for o,e in ipairs(e)do +if(e=="prev")or(e=="next")or(e=="complete")then +t:tag(e):up(); +else +module:log("error",'Command "'..a.name.. +'" at node "'..a.node..'" provided an invalid action "'..e..'"'); +end +end +cmdtag:add_child(t); +elseif t=="form"then +cmdtag:add_child((e.layout or e):form(e.values)); +elseif t=="result"then +cmdtag:add_child((e.layout or e):form(e.values,"result")); +elseif t=="other"then +cmdtag:add_child(e); +end +end +o:add_child(cmdtag); +s.send(o); +return true; +end +return s; +end) +package.preload['util.stanza']=(function(...) +local t=table.insert; +local e=table.concat; +local r=table.remove; +local w=table.concat; +local s=string.format; +local y=string.match; +local c=tostring; +local m=setmetatable; +local e=getmetatable; +local i=pairs; +local n=ipairs; +local o=type; +local e=next; +local e=print; +local e=unpack; +local f=string.gsub; +local e=string.char; +local u=string.find; +local e=os; +local l=not e.getenv("WINDIR"); +local d,a; +if l then +local t,e=pcall(require,"util.termcolours"); +if t then +d,a=e.getstyle,e.getstring; +else +l=nil; +end +end +local p="urn:ietf:params:xml:ns:xmpp-stanzas"; +module"stanza" +stanza_mt={__type="stanza"}; +stanza_mt.__index=stanza_mt; +local e=stanza_mt; +function stanza(t,a) +local t={name=t,attr=a or{},tags={}}; +return m(t,e); +end +local h=stanza; +function e:query(e) +return self:tag("query",{xmlns=e}); +end +function e:body(t,e) +return self:tag("body",e):text(t); +end +function e:tag(a,e) +local a=h(a,e); +local e=self.last_add; +if not e then e={};self.last_add=e;end +(e[#e]or self):add_direct_child(a); +t(e,a); +return self; +end +function e:text(t) +local e=self.last_add; +(e and e[#e]or self):add_direct_child(t); +return self; +end +function e:up() +local e=self.last_add; +if e then r(e);end +return self; +end +function e:reset() +self.last_add=nil; +return self; +end +function e:add_direct_child(e) +if o(e)=="table"then +t(self.tags,e); +end +t(self,e); +end +function e:add_child(t) +local e=self.last_add; +(e and e[#e]or self):add_direct_child(t); +return self; +end +function e:get_child(a,t) +for o,e in n(self.tags)do +if(not a or e.name==a) +and((not t and self.attr.xmlns==e.attr.xmlns) +or e.attr.xmlns==t)then +return e; +end +end +end +function e:get_child_text(t,e) +local e=self:get_child(t,e); +if e then +return e:get_text(); +end +return nil; +end +function e:child_with_name(t) +for a,e in n(self.tags)do +if e.name==t then return e;end +end +end +function e:child_with_ns(t) +for a,e in n(self.tags)do +if e.attr.xmlns==t then return e;end +end +end +function e:children() +local e=0; +return function(t) +e=e+1 +return t[e]; +end,self,e; +end +function e:childtags(i,e) +e=e or self.attr.xmlns; +local t=self.tags; +local a,o=1,#t; +return function() +for o=a,o do +local t=t[o]; +if(not i or t.name==i) +and(not e or e==t.attr.xmlns)then +a=o+1; +return t; +end +end +end; +end +function e:maptags(i) +local a,t=self.tags,1; +local n,o=#self,#a; +local e=1; +while t<=o do +if self[e]==a[t]then +local i=i(self[e]); +if i==nil then +r(self,e); +r(a,t); +n=n-1; +o=o-1; +else +self[e]=i; +a[e]=i; +end +e=e+1; +t=t+1; +end +end +return self; +end +local r +do +local e={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; +function r(t)return(f(t,"['&<>\"]",e));end +_M.xml_escape=r; +end +local function f(o,e,h,a,r) +local n=0; +local s=o.name +t(e,"<"..s); +for o,i in i(o.attr)do +if u(o,"\1",1,true)then +local o,s=y(o,"^([^\1]*)\1?(.*)$"); +n=n+1; +t(e," xmlns:ns"..n.."='"..a(o).."' ".."ns"..n..":"..s.."='"..a(i).."'"); +elseif not(o=="xmlns"and i==r)then +t(e," "..o.."='"..a(i).."'"); +end +end +local i=#o; +if i==0 then +t(e,"/>"); +else +t(e,">"); +for i=1,i do +local i=o[i]; +if i.name then +h(i,e,h,a,o.attr.xmlns); +else +t(e,a(i)); +end +end +t(e,""); +end +end +function e.__tostring(t) +local e={}; +f(t,e,f,r,nil); +return w(e); +end +function e.top_tag(t) +local e=""; +if t.attr then +for t,a in i(t.attr)do if o(t)=="string"then e=e..s(" %s='%s'",t,r(c(a)));end end +end +return s("<%s%s>",t.name,e); +end +function e.get_text(e) +if#e.tags==0 then +return w(e); +end +end +function e.get_error(a) +local o,e,t; +local a=a:get_child("error"); +if not a then +return nil,nil,nil; +end +o=a.attr.type; +for a in a:childtags()do +if a.attr.xmlns==p then +if not t and a.name=="text"then +t=a:get_text(); +elseif not e then +e=a.name; +end +if e and t then +break; +end +end +end +return o,e or"undefined-condition",t; +end +function e.__add(t,e) +return t:add_direct_child(e); +end +do +local e=0; +function new_id() +e=e+1; +return"lx"..e; +end +end +function preserialize(a) +local e={name=a.name,attr=a.attr}; +for i,a in n(a)do +if o(a)=="table"then +t(e,preserialize(a)); +else +t(e,a); +end +end +return e; +end +function deserialize(a) +if a then +local s=a.attr; +for e=1,#s do s[e]=nil;end +local h={}; +for e in i(s)do +if u(e,"|",1,true)and not u(e,"\1",1,true)then +local t,a=y(e,"^([^|]+)|(.+)$"); +h[t.."\1"..a]=s[e]; +s[e]=nil; +end +end +for t,e in i(h)do +s[t]=e; +end +m(a,e); +for t,e in n(a)do +if o(e)=="table"then +deserialize(e); +end +end +if not a.tags then +local e={}; +for n,i in n(a)do +if o(i)=="table"then +t(e,i); +end +end +a.tags=e; +end +end +return a; +end +local function u(a) +local o,n={},{}; +for t,e in i(a.attr)do o[t]=e;end +local o={name=a.name,attr=o,tags=n}; +for e=1,#a do +local e=a[e]; +if e.name then +e=u(e); +t(n,e); +end +t(o,e); +end +return m(o,e); +end +clone=u; +function message(t,e) +if not e then +return h("message",t); +else +return h("message",t):tag("body"):text(e):up(); +end +end +function iq(e) +if e and not e.id then e.id=new_id();end +return h("iq",e or{id=new_id()}); +end +function reply(e) +return h(e.name,e.attr and{to=e.attr.from,from=e.attr.to,id=e.attr.id,type=((e.name=="iq"and"result")or e.attr.type)}); +end +do +local t={xmlns=p}; +function error_reply(e,i,o,a) +local e=reply(e); +e.attr.type="error"; +e:tag("error",{type=i}) +:tag(o,t):up(); +if(a)then e:tag("text",t):text(a):up();end +return e; +end +end +function presence(e) +return h("presence",e); +end +if l then +local u=d("yellow"); +local l=d("red"); +local h=d("red"); +local t=d("magenta"); +local d=" "..a(u,"%s")..a(t,"=")..a(l,"'%s'"); +local l=a(t,"<")..a(h,"%s").."%s"..a(t,">"); +local h=l.."%s"..a(t,""); +function e.pretty_print(e) +local t=""; +for a,e in n(e)do +if o(e)=="string"then +t=t..r(e); +else +t=t..e:pretty_print(); +end +end +local a=""; +if e.attr then +for e,t in i(e.attr)do if o(e)=="string"then a=a..s(d,e,c(t));end end +end +return s(h,e.name,a,t,e.name); +end +function e.pretty_top_tag(e) +local t=""; +if e.attr then +for e,a in i(e.attr)do if o(e)=="string"then t=t..s(d,e,c(a));end end +end +return s(l,e.name,t); +end +else +e.pretty_print=e.__tostring; +e.pretty_top_tag=e.top_tag; +end +return _M; +end) +package.preload['util.timer']=(function(...) +local c=require"net.server".addtimer; +local o=require"net.server".event; +local l=require"net.server".event_base; +local r=math.min +local u=math.huge +local d=require"socket".gettime; +local s=table.insert; +local e=table.remove; +local e,n=ipairs,pairs; +local h=type; +local i={}; +local e={}; +module"timer" +local t; +if not o then +function t(a,o) +local i=d(); +a=a+i; +if a>=i then +s(e,{a,o}); +else +local e=o(); +if e and h(e)=="number"then +return t(e,o); +end +end +end +c(function() +local o=d(); +if#e>0 then +for a,t in n(e)do +s(i,t); +end +e={}; +end +local e=u; +for d,a in n(i)do +local n,s=a[1],a[2]; +if n<=o then +i[d]=nil; +local a=s(o); +if h(a)=="number"then +t(a,s); +e=r(e,a); +end +else +e=r(e,n-o); +end +end +return e; +end); +else +local o=(o.core and o.core.LEAVE)or-1; +function t(a,t) +local e; +e=l:addevent(nil,0,function() +local t=t(); +if t then +return 0,t; +elseif e then +return o; +end +end +,a); +end +end +add_task=t; +return _M; +end) +package.preload['util.termcolours']=(function(...) +local a,n=table.concat,table.insert; +local t,i=string.char,string.format; +local h=ipairs; +local s=io.write; +local e; +if os.getenv("WINDIR")then +e=require"util.windows"; +end +local o=e and e.get_consolecolor and e.get_consolecolor(); +module"termcolours" +local r={ +reset=0;bright=1,dim=2,underscore=4,blink=5,reverse=7,hidden=8; +black=30;red=31;green=32;yellow=33;blue=34;magenta=35;cyan=36;white=37; +["black background"]=40;["red background"]=41;["green background"]=42;["yellow background"]=43;["blue background"]=44;["magenta background"]=45;["cyan background"]=46;["white background"]=47; +bold=1,dark=2,underline=4,underlined=4,normal=0; +} +local d={ +["0"]=o, +["1"]=7+8, +["1;33"]=2+4+8, +["1;31"]=4+8 +} +local l=t(27).."[%sm%s"..t(27).."[0m"; +function getstring(e,t) +if e then +return i(l,e,t); +else +return t; +end +end +function getstyle(...) +local e,t={...},{}; +for a,e in h(e)do +e=r[e]; +if e then +n(t,e); +end +end +return a(t,";"); +end +local a="0"; +function setstyle(e) +e=e or"0"; +if e~=a then +s("\27["..e.."m"); +a=e; +end +end +if e then +function setstyle(t) +t=t or"0"; +if t~=a then +e.set_consolecolor(d[t]or o); +a=t; +end +end +if not o then +function setstyle(e)end +end +end +return _M; +end) +package.preload['util.uuid']=(function(...) +local e=math.random; +local i=tostring; +local e=os.time; +local a=os.clock; +local n=require"util.hashes".sha1; +module"uuid" +local t=0; +local function o() +local e=e(); +if t>=e then e=t+1;end +t=e; +return e; +end +local function e(e) +return n(e..a()..i({}),true); +end +local t=e(o()); +local function a(a) +t=e(t..a); +end +local function e(e) +if#t"; +end +return e; +end +local f={ +LOC=e.LOC_tostring; +MX=function(e) +return a.format('%2i %s',e.pref,e.mx); +end; +SRV=function(e) +local e=e.srv; +return a.format('%5d %5d %5d %s',e.priority,e.weight,e.port,e.target); +end; +}; +local j={}; +function j.__tostring(e) +local t=(f[e.type]or x)(e); +return a.format('%2s %-5s %6i %-28s %s',e.class,e.type,e.ttl,e.name,t); +end +local x={}; +function x.__tostring(t) +local e={}; +for a,t in o(t)do +i(e,v(t)..'\n'); +end +return n.concat(e); +end +local f={}; +function f.__tostring(t) +local a=s.gettime(); +local e={}; +for n,t in o(t)do +for n,t in o(t)do +for o,t in o(t)do +w(t,a); +i(e,v(t)); +end +end +end +return n.concat(e); +end +function e:new() +local t={active={},cache={},unsorted={}}; +r(t,e); +r(t.cache,f); +r(t.unsorted,{__mode='kv'}); +return t; +end +function t.random(...) +y.randomseed(y.floor(1e4*s.gettime())); +t.random=y.random; +return t.random(...); +end +local function y(e) +e=e or{}; +e.id=e.id or t.random(0,65535); +e.rd=e.rd or 1; +e.tc=e.tc or 0; +e.aa=e.aa or 0; +e.opcode=e.opcode or 0; +e.qr=e.qr or 0; +e.rcode=e.rcode or 0; +e.z=e.z or 0; +e.ra=e.ra or 0; +e.qdcount=e.qdcount or 1; +e.ancount=e.ancount or 0; +e.nscount=e.nscount or 0; +e.arcount=e.arcount or 0; +local t=a.char( +u(e.id),e.id%256, +e.rd+2*e.tc+4*e.aa+8*e.opcode+128*e.qr, +e.rcode+16*e.z+128*e.ra, +u(e.qdcount),e.qdcount%256, +u(e.ancount),e.ancount%256, +u(e.nscount),e.nscount%256, +u(e.arcount),e.arcount%256 +); +return t,e.id; +end +local function u(t) +local e={}; +for t in a.gmatch(t,'[^.]+')do +i(e,a.char(a.len(t))); +i(e,t); +end +i(e,a.char(0)); +return n.concat(e); +end +local function _(a,e,o) +a=u(a); +e=t.typecode[e or'a']; +o=t.classcode[o or'in']; +return a..e..o; +end +function e:byte(e) +e=e or 1; +local t=self.offset; +local o=t+e-1; +if o>#self.packet then +k(a.format('out of bounds: %i>%i',o,#self.packet)); +end +self.offset=t+e; +return a.byte(self.packet,t,o); +end +function e:word() +local e,t=self:byte(2); +return 256*e+t; +end +function e:dword() +local a,o,t,e=self:byte(4); +return 16777216*a+65536*o+256*t+e; +end +function e:sub(e) +e=e or 1; +local t=a.sub(self.packet,self.offset,self.offset+e-1); +self.offset=self.offset+e; +return t; +end +function e:header(t) +local e=self:word(); +if not self.active[e]and not t then return nil;end +local e={id=e}; +local t,a=self:byte(2); +e.rd=t%2; +e.tc=t/2%2; +e.aa=t/4%2; +e.opcode=t/8%16; +e.qr=t/128; +e.rcode=a%16; +e.z=a/16%8; +e.ra=a/128; +e.qdcount=self:word(); +e.ancount=self:word(); +e.nscount=self:word(); +e.arcount=self:word(); +for a,t in o(e)do e[a]=t-t%1;end +return e; +end +function e:name() +local t,a=nil,0; +local e=self:byte(); +local o={}; +while e>0 do +if e>=192 then +a=a+1; +if a>=20 then k('dns error: 20 pointers');end; +local e=((e-192)*256)+self:byte(); +t=t or self.offset; +self.offset=e+1; +else +i(o,self:sub(e)..'.'); +end +e=self:byte(); +end +self.offset=t or self.offset; +return n.concat(o); +end +function e:question() +local e={}; +e.name=self:name(); +e.type=t.type[self:word()]; +e.class=t.class[self:word()]; +return e; +end +function e:A(i) +local n,t,o,e=self:byte(4); +i.a=a.format('%i.%i.%i.%i',n,t,o,e); +end +function e:AAAA(a) +local e={}; +for t=1,a.rdlength,2 do +local a,t=self:byte(2); +n.insert(e,("%02x%02x"):format(a,t)); +end +e=n.concat(e,":"):gsub("%f[%x]0+(%x)","%1"); +local t={}; +for e in e:gmatch(":[0:]+:")do +n.insert(t,e) +end +if#t==0 then +a.aaaa=e; +return +elseif#t>1 then +n.sort(t,function(e,t)return#e>#t end); +end +a.aaaa=e:gsub(t[1],"::",1):gsub("^0::","::"):gsub("::0$","::"); +end +function e:CNAME(e) +e.cname=self:name(); +end +function e:MX(e) +e.pref=self:word(); +e.mx=self:name(); +end +function e:LOC_nibble_power() +local e=self:byte(); +return((e-(e%16))/16)*(10^(e%16)); +end +function e:LOC(e) +e.version=self:byte(); +if e.version==0 then +e.loc=e.loc or{}; +e.loc.size=self:LOC_nibble_power(); +e.loc.horiz_pre=self:LOC_nibble_power(); +e.loc.vert_pre=self:LOC_nibble_power(); +e.loc.latitude=self:dword(); +e.loc.longitude=self:dword(); +e.loc.altitude=self:dword(); +end +end +local function u(e,i,t) +e=e-2147483648; +if e<0 then i=t;e=-e;end +local n,t,o; +o=e%6e4; +e=(e-o)/6e4; +t=e%60; +n=(e-t)/60; +return a.format('%3d %2d %2.3f %s',n,t,o/1e3,i); +end +function e.LOC_tostring(e) +local t={}; +i(t,a.format( +'%s %s %.2fm %.2fm %.2fm %.2fm', +u(e.loc.latitude,'N','S'), +u(e.loc.longitude,'E','W'), +(e.loc.altitude-1e7)/100, +e.loc.size/100, +e.loc.horiz_pre/100, +e.loc.vert_pre/100 +)); +return n.concat(t); +end +function e:NS(e) +e.ns=self:name(); +end +function e:SOA(e) +end +function e:SRV(e) +e.srv={}; +e.srv.priority=self:word(); +e.srv.weight=self:word(); +e.srv.port=self:word(); +e.srv.target=self:name(); +end +function e:PTR(e) +e.ptr=self:name(); +end +function e:TXT(e) +e.txt=self:sub(self:byte()); +end +function e:rr() +local e={}; +r(e,j); +e.name=self:name(self); +e.type=t.type[self:word()]or e.type; +e.class=t.class[self:word()]or e.class; +e.ttl=65536*self:word()+self:word(); +e.rdlength=self:word(); +if e.ttl<=0 then +e.tod=self.time+30; +else +e.tod=self.time+e.ttl; +end +local a=self.offset; +local t=self[t.type[e.type]]; +if t then t(self,e);end +self.offset=a; +e.rdata=self:sub(e.rdlength); +return e; +end +function e:rrs(t) +local e={}; +for t=1,t do i(e,self:rr());end +return e; +end +function e:decode(t,o) +self.packet,self.offset=t,1; +local t=self:header(o); +if not t then return nil;end +local t={header=t}; +t.question={}; +local n=self.offset; +for e=1,t.header.qdcount do +i(t.question,self:question()); +end +t.question.raw=a.sub(self.packet,n,self.offset-1); +if not o then +if not self.active[t.header.id]or not self.active[t.header.id][t.question.raw]then +return nil; +end +end +t.answer=self:rrs(t.header.ancount); +t.authority=self:rrs(t.header.nscount); +t.additional=self:rrs(t.header.arcount); +return t; +end +e.delays={1,3}; +function e:addnameserver(e) +self.server=self.server or{}; +i(self.server,e); +end +function e:setnameserver(e) +self.server={}; +self:addnameserver(e); +end +function e:adddefaultnameservers() +if E then +if b and b.get_nameservers then +for t,e in p(b.get_nameservers())do +self:addnameserver(e); +end +end +if not self.server or#self.server==0 then +self:addnameserver("208.67.222.222"); +self:addnameserver("208.67.220.220"); +end +else +local e=z.open("/etc/resolv.conf"); +if e then +for e in e:lines()do +e=e:gsub("#.*$","") +:match('^%s*nameserver%s+(.*)%s*$'); +if e then +e:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]",function(e) +self:addnameserver(e) +end); +end +end +end +if not self.server or#self.server==0 then +self:addnameserver("127.0.0.1"); +end +end +end +function e:getsocket(t) +self.socket=self.socket or{}; +self.socketset=self.socketset or{}; +local e=self.socket[t]; +if e then return e;end +local a; +e,a=s.udp(); +if not e then +return nil,a; +end +if self.socket_wrapper then e=self.socket_wrapper(e,self);end +e:settimeout(0); +e:setsockname('*',0); +e:setpeername(self.server[t],53); +self.socket[t]=e; +self.socketset[e]=t; +return e; +end +function e:voidsocket(e) +if self.socket[e]then +self.socketset[self.socket[e]]=nil; +self.socket[e]=nil; +elseif self.socketset[e]then +self.socket[self.socketset[e]]=nil; +self.socketset[e]=nil; +end +end +function e:socket_wrapper_set(e) +self.socket_wrapper=e; +end +function e:closeall() +for t,e in p(self.socket)do +self.socket[t]=nil; +self.socketset[e]=nil; +e:close(); +end +end +function e:remember(e,t) +local o,n,a=g(e.name,e.type,e.class); +if t~='*'then +t=n; +local t=d(self.cache,a,'*',o); +if t then i(t,e);end +end +self.cache=self.cache or r({},f); +local a=d(self.cache,a,t,o)or +l(self.cache,a,t,o,r({},x)); +i(a,e); +if t=='MX'then self.unsorted[a]=true;end +end +local function i(e,t) +return(e.pref==t.pref)and(e.mx#self.server then +e.server=1; +end +e.retries=(e.retries or 0)+1; +if e.retries>=#self.server then +a[o]=nil; +else +local t=self:getsocket(e.server); +if t then t:send(e.packet);end +end +end +end +end +if t==self.best_server then +self.best_server=self.best_server+1; +if self.best_server>#self.server then +self.best_server=1; +end +end +end +function e:settimeout(e) +self.timeout=e; +end +function e:receive(t) +self.time=s.gettime(); +t=t or self.socket; +local e; +for a,t in o(t)do +if self.socketset[t]then +local t=t:receive(); +if t then +e=self:decode(t); +if e and self.active[e.header.id] +and self.active[e.header.id][e.question.raw]then +for a,t in o(e.answer)do +if t.name:sub(-#e.question[1].name,-1)==e.question[1].name then +self:remember(t,e.question[1].type) +end +end +local t=self.active[e.header.id]; +t[e.question.raw]=nil; +if not h(t)then self.active[e.header.id]=nil;end +if not h(self.active)then self:closeall();end +local e=e.question[1]; +local t=d(self.wanted,e.class,e.type,e.name); +if t then +for t in o(t)do +l(self.yielded,t,e.class,e.type,e.name,nil); +if c.status(t)=="suspended"then c.resume(t);end +end +l(self.wanted,e.class,e.type,e.name,nil); +end +end +end +end +end +return e; +end +function e:feed(a,e,t) +self.time=s.gettime(); +local e=self:decode(e,t); +if e and self.active[e.header.id] +and self.active[e.header.id][e.question.raw]then +for a,t in o(e.answer)do +self:remember(t,e.question[1].type); +end +local t=self.active[e.header.id]; +t[e.question.raw]=nil; +if not h(t)then self.active[e.header.id]=nil;end +if not h(self.active)then self:closeall();end +local e=e.question[1]; +if e then +local t=d(self.wanted,e.class,e.type,e.name); +if t then +for t in o(t)do +l(self.yielded,t,e.class,e.type,e.name,nil); +if c.status(t)=="suspended"then c.resume(t);end +end +l(self.wanted,e.class,e.type,e.name,nil); +end +end +end +return e; +end +function e:cancel(i,t,o,e,a) +local t=d(self.wanted,i,t,o); +if t then +if a then +c.resume(e); +end +t[e]=nil; +end +end +function e:pulse() +while self:receive()do end +if not h(self.active)then return nil;end +self.time=s.gettime(); +for i,t in o(self.active)do +for a,e in o(t)do +if self.time>=e.retry then +e.server=e.server+1; +if e.server>#self.server then +e.server=1; +e.delay=e.delay+1; +end +if e.delay>#self.delays then +t[a]=nil; +if not h(t)then self.active[i]=nil;end +if not h(self.active)then return nil;end +else +local t=self.socket[e.server]; +if t then t:send(e.packet);end +e.retry=self.time+self.delays[e.delay]; +end +end +end +end +if h(self.active)then return true;end +return nil; +end +function e:lookup(o,a,t) +self:query(o,a,t) +while self:pulse()do +local e={} +for a,t in p(self.socket)do +e[a]=t +end +s.select(e,nil,4) +end +return self:peek(o,a,t); +end +function e:lookupex(o,a,t,e) +return self:peek(a,t,e)or self:query(a,t,e); +end +function e:tohostname(e) +return t.lookup(e:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)","%4.%3.%2.%1.in-addr.arpa."),"PTR"); +end +local i={ +qr={[0]='query','response'}, +opcode={[0]='query','inverse query','server status request'}, +aa={[0]='non-authoritative','authoritative'}, +tc={[0]='complete','truncated'}, +rd={[0]='recursion not desired','recursion desired'}, +ra={[0]='recursion not available','recursion available'}, +z={[0]='(reserved)'}, +rcode={[0]='no error','format error','server failure','name error','not implemented'}, +type=t.type, +class=t.class +}; +local function n(t,e) +return(i[e]and i[e][t[e]])or''; +end +function e.print(t) +for o,e in o{'id','qr','opcode','aa','tc','rd','ra','z', +'rcode','qdcount','ancount','nscount','arcount'}do +m(a.format('%-30s','header.'..e),t.header[e],n(t.header,e)); +end +for t,e in p(t.question)do +m(a.format('question[%i].name ',t),e.name); +m(a.format('question[%i].type ',t),e.type); +m(a.format('question[%i].class ',t),e.class); +end +local h={name=1,type=1,class=1,ttl=1,rdlength=1,rdata=1}; +local e; +for s,i in o({'answer','authority','additional'})do +for s,t in o(t[i])do +for h,o in o({'name','type','class','ttl','rdlength'})do +e=a.format('%s[%i].%s',i,s,o); +m(a.format('%-30s',e),t[o],n(t,o)); +end +for t,o in o(t)do +if not h[t]then +e=a.format('%s[%i].%s',i,s,t); +m(a.format('%-30s %s',v(e),v(o))); +end +end +end +end +end +function t.resolver() +local t={active={},cache={},unsorted={},wanted={},yielded={},best_server=1}; +r(t,e); +r(t.cache,f); +r(t.unsorted,{__mode='kv'}); +return t; +end +local e=t.resolver(); +t._resolver=e; +function t.lookup(...) +return e:lookup(...); +end +function t.tohostname(...) +return e:tohostname(...); +end +function t.purge(...) +return e:purge(...); +end +function t.peek(...) +return e:peek(...); +end +function t.query(...) +return e:query(...); +end +function t.feed(...) +return e:feed(...); +end +function t.cancel(...) +return e:cancel(...); +end +function t.settimeout(...) +return e:settimeout(...); +end +function t.socket_wrapper_set(...) +return e:socket_wrapper_set(...); +end +return t; +end) +package.preload['net.adns']=(function(...) +local u=require"net.server"; +local o=require"net.dns"; +local e=require"util.logger".init("adns"); +local t,t=table.insert,table.remove; +local a,s,l=coroutine,tostring,pcall; +local function c(a,a,e,t)return(t-e)+1;end +module"adns" +function lookup(d,t,h,r) +return a.wrap(function(i) +if i then +e("debug","Records for %s already cached, using those...",t); +d(i); +return; +end +e("debug","Records for %s not in cache, sending query (%s)...",t,s(a.running())); +local n,i=o.query(t,h,r); +if n then +a.yield({r or"IN",h or"A",t,a.running()}); +e("debug","Reply for %s (%s)",t,s(a.running())); +end +if n then +n,i=l(d,o.peek(t,h,r)); +else +e("error","Error sending DNS query: %s",i); +n,i=l(d,nil,i); +end +if not n then +e("error","Error in DNS response handler: %s",s(i)); +end +end)(o.peek(t,h,r)); +end +function cancel(t,a,i) +e("warn","Cancelling DNS lookup for %s",s(t[3])); +o.cancel(t[1],t[2],t[3],t[4],a); +end +function new_async_socket(a,i) +local s=""; +local n={}; +local t={}; +function n.onincoming(a,e) +if e then +o.feed(t,e); +end +end +function n.ondisconnect(o,a) +if a then +e("warn","DNS socket for %s disconnected: %s",s,a); +local t=i.server; +if i.socketset[o]==i.best_server and i.best_server==#t then +e("error","Exhausted all %d configured DNS servers, next lookup will try %s again",#t,t[1]); +end +i:servfail(o); +end +end +t=u.wrapclient(a,"dns",53,n); +if not t then +e("warn","handler is nil"); +end +t.settimeout=function()end +t.setsockname=function(e,...)return a:setsockname(...);end +t.setpeername=function(o,...)s=(...);local e=a:setpeername(...);o:set_send(c);return e;end +t.connect=function(e,...)return a:connect(...)end +t.send=function(t,o) +local t=a.getpeername; +e("debug","Sending DNS query to %s",(t and t(a))or""); +return a:send(o); +end +return t; +end +o.socket_wrapper_set(new_async_socket); +return _M; +end) +package.preload['net.server']=(function(...) +local s=function(e) +return _G[e] +end +local ce=function(e) +for t,a in pairs(e)do +e[t]=nil +end +end +local F,e=require("util.logger").init("socket"),table.concat; +local i=function(...)return F("debug",e{...});end +local de=function(...)return F("warn",e{...});end +local e=collectgarbage +local le=1 +local M=s"type" +local N=s"pairs" +local ue=s"ipairs" +local v=s"tonumber" +local l=s"tostring" +local e=s"collectgarbage" +local o=s"os" +local a=s"table" +local t=s"string" +local e=s"coroutine" +local W=o.difftime +local Y=math.min +local ae=math.huge +local X=a.concat +local a=a.remove +local ne=t.len +local be=t.sub +local pe=e.wrap +local ve=e.yield +local k=s"ssl" +local S=s"socket"or require"socket" +local K=S.gettime +local ye=(k and k.wrap) +local we=S.bind +local fe=S.sleep +local me=S.select +local e=(k and k.newcontext) +local J +local G +local re +local Q +local B +local he +local f +local se +local ee +local te +local Z +local P +local h +local oe +local e +local C +local ie +local p +local d +local U +local r +local n +local I +local b +local w +local m +local a +local o +local g +local H +local R +local _ +local T +local V +local u +local O +local A +local E +local z +local x +local L +local D +local q +local j +p={} +d={} +r={} +U={} +n={} +b={} +w={} +I={} +a=0 +o=0 +g=0 +H=0 +R=0 +_=1 +T=0 +O=51e3*1024 +A=25e3*1024 +E=12e5 +z=6e4 +x=6*60*60 +L=false +q=1e3 +j=30 +te=function(m,t,y,u,p,f,c) +c=c or q +local s=0 +local w,e=m.onconnect,m.ondisconnect +local v=t.accept +local e={} +e.shutdown=function()end +e.ssl=function() +return f~=nil +end +e.sslctx=function() +return f +end +e.remove=function() +s=s-1 +end +e.close=function() +for a,e in N(n)do +if e.serverport==u then +e.disconnect(e,"server closed") +e:close(true) +end +end +t:close() +o=h(r,t,o) +a=h(d,t,a) +n[t]=nil +e=nil +t=nil +i"server.lua: closed server handler and removed sockets from list" +end +e.ip=function() +return y +end +e.serverport=function() +return u +end +e.socket=function() +return t +end +e.readbuffer=function() +if s>c then +i("server.lua: refused new client connection: server full") +return false +end +local t,n=v(t) +if t then +local o,a=t:getpeername() +t:settimeout(0) +local e,n,t=C(e,m,t,o,u,a,p,f) +if t then +return false +end +s=s+1 +i("server.lua: accepted new client connection from ",l(o),":",l(a)," to ",l(u)) +if w then +return w(e); +end +return; +elseif n then +i("server.lua: error with new client connection: ",l(n)) +return false +end +end +return e +end +C=function(B,v,t,P,G,C,_,x) +t:settimeout(0) +local y +local E +local z +local N +local S=v.onincoming +local D=v.onstatus +local g=v.ondisconnect +local W=v.ondrain +local p={} +local s=0 +local V +local F +local M +local c=0 +local q=false +local T=false +local Y,U=0,0 +local O=O +local A=A +local e=p +e.dispatch=function() +return S +end +e.disconnect=function() +return g +end +e.setlistener=function(a,t) +S=t.onincoming +g=t.ondisconnect +D=t.onstatus +W=t.ondrain +end +e.getstats=function() +return U,Y +end +e.ssl=function() +return N +end +e.sslctx=function() +return x +end +e.send=function(n,o,i,a) +return y(t,o,i,a) +end +e.receive=function(o,a) +return E(t,o,a) +end +e.shutdown=function(a) +return z(t,a) +end +e.setoption=function(i,a,o) +if t.setoption then +return t:setoption(a,o); +end +return false,"setoption not implemented"; +end +e.close=function(u,l) +if not e then return true;end +a=h(d,t,a) +b[e]=nil +if s~=0 then +if not(l or F)then +e.sendbuffer() +if s~=0 then +if e then +e.write=nil +end +V=true +return false +end +else +y(t,X(p,"",1,s),1,c) +end +end +if t then +m=z and z(t) +t:close() +o=h(r,t,o) +n[t]=nil +t=nil +else +i"server.lua: socket already closed" +end +if e then +w[e]=nil +I[e]=nil +e=nil +end +if B then +B.remove() +end +i"server.lua: closed client handler and removed socket from list" +return true +end +e.ip=function() +return P +end +e.serverport=function() +return G +end +e.clientport=function() +return C +end +local I=function(i,a) +c=c+ne(a) +if c>O then +I[e]="send buffer exceeded" +e.write=Q +return false +elseif t and not r[t]then +o=f(r,t,o) +end +s=s+1 +p[s]=a +if e then +w[e]=w[e]or u +end +return true +end +e.write=I +e.bufferqueue=function(t) +return p +end +e.socket=function(a) +return t +end +e.set_mode=function(a,t) +_=t or _ +return _ +end +e.set_send=function(a,t) +y=t or y +return y +end +e.bufferlen=function(o,a,t) +O=t or O +A=a or A +return c,A,O +end +e.lock_read=function(i,o) +if o==true then +local o=a +a=h(d,t,a) +b[e]=nil +if a~=o then +q=true +end +elseif o==false then +if q then +q=false +a=f(d,t,a) +b[e]=u +end +end +return q +end +e.pause=function(t) +return t:lock_read(true); +end +e.resume=function(t) +return t:lock_read(false); +end +e.lock=function(i,a) +e.lock_read(a) +if a==true then +e.write=Q +local a=o +o=h(r,t,o) +w[e]=nil +if o~=a then +T=true +end +elseif a==false then +e.write=I +if T then +T=false +I("") +end +end +return q,T +end +local b=function() +local o,t,a=E(t,_) +if not t or(t=="wantread"or t=="timeout")then +local a=o or a or"" +local o=ne(a) +if o>A then +g(e,"receive buffer exceeded") +e:close(true) +return false +end +local o=o*le +U=U+o +R=R+o +b[e]=u +return S(e,a,t) +else +i("server.lua: client ",l(P),":",l(C)," read error: ",l(t)) +F=true +g(e,t) +m=e and e:close() +return false +end +end +local w=function() +local f,a,d,n,v; +local v; +if t then +n=X(p,"",1,s) +f,a,d=y(t,n,1,c) +v=(f or d or 0)*le +Y=Y+v +H=H+v +m=L and ce(p) +else +f,a,v=false,"closed",0; +end +if f then +s=0 +c=0 +o=h(r,t,o) +w[e]=nil +if W then +W(e) +end +m=M and e:starttls(nil) +m=V and e:close() +return true +elseif d and(a=="timeout"or a=="wantwrite")then +n=be(n,d+1,c) +p[1]=n +s=1 +c=c-d +w[e]=u +return true +else +i("server.lua: client ",l(P),":",l(C)," write error: ",l(a)) +F=true +g(e,a) +m=e and e:close() +return false +end +end +local u; +function e.set_sslctx(y,t) +x=t; +local c,s +u=pe(function(n) +local t +for l=1,j do +o=(s and h(r,n,o))or o +a=(c and h(d,n,a))or a +c,s=nil,nil +m,t=n:dohandshake() +if not t then +i("server.lua: ssl handshake done") +e.readbuffer=b +e.sendbuffer=w +m=D and D(e,"ssl-handshake-complete") +if y.autostart_ssl and v.onconnect then +v.onconnect(y); +end +a=f(d,n,a) +return true +else +if t=="wantwrite"then +o=f(r,n,o) +s=true +elseif t=="wantread"then +a=f(d,n,a) +c=true +else +break; +end +t=nil; +ve() +end +end +i("server.lua: ssl handshake error: ",l(t or"handshake too long")) +g(e,"ssl handshake failed") +m=e and e:close(true) +return false +end +) +end +if k then +e.starttls=function(m,c) +if c then +e:set_sslctx(c); +end +if s>0 then +i"server.lua: we need to do tls, but delaying until send buffer empty" +M=true +return +end +i("server.lua: attempting to start tls on "..l(t)) +local s,c=t +t,c=ye(t,x) +if not t then +i("server.lua: error while starting tls on client: ",l(c or"unknown error")) +return nil,c +end +t:settimeout(0) +y=t.send +E=t.receive +z=J +n[t]=e +a=f(d,t,a) +a=h(d,s,a) +o=h(r,s,o) +n[s]=nil +e.starttls=nil +M=nil +N=true +e.readbuffer=u +e.sendbuffer=u +u(t) +end +e.readbuffer=b +e.sendbuffer=w +if x then +i"server.lua: auto-starting ssl negotiation..." +e.autostart_ssl=true; +e:starttls(x); +end +else +e.readbuffer=b +e.sendbuffer=w +end +y=t.send +E=t.receive +z=(N and J)or t.shutdown +n[t]=e +a=f(d,t,a) +return e,t +end +J=function() +end +Q=function() +return false +end +f=function(t,a,e) +if not t[a]then +e=e+1 +t[e]=a +t[a]=e +end +return e; +end +h=function(e,o,t) +local i=e[o] +if i then +e[o]=nil +local a=e[t] +e[t]=nil +if a~=o then +e[a]=i +e[i]=a +end +return t-1 +end +return t +end +P=function(e) +o=h(r,e,o) +a=h(d,e,a) +n[e]=nil +e:close() +end +local function c(a,t,o) +local e; +local i=t.sendbuffer; +function t.sendbuffer() +i(); +if e and t.bufferlen()=o then +e=true; +a:lock_read(true); +end +end +end +se=function(t,e,r,l,h) +local o +if M(r)~="table"then +o="invalid listener table" +end +if M(e)~="number"or not(e>=0 and e<=65535)then +o="invalid port" +elseif p[t..":"..e]then +o="listeners on '["..t.."]:"..e.."' already exist" +elseif h and not k then +o="luasec not found" +end +if o then +de("server.lua, [",t,"]:",e,": ",o) +return nil,o +end +t=t or"*" +local o,s=we(t,e) +if s then +de("server.lua, [",t,"]:",e,": ",s) +return nil,s +end +local s,r=te(r,o,t,e,l,h,q) +if not s then +o:close() +return nil,r +end +o:settimeout(0) +a=f(d,o,a) +p[t..":"..e]=s +n[o]=s +i("server.lua: new "..(h and"ssl "or"").."server listener on '[",t,"]:",e,"'") +return s +end +ee=function(e,t) +return p[e..":"..t]; +end +oe=function(e,t) +local a=p[e..":"..t] +if not a then +return nil,"no server found on '["..e.."]:"..l(t).."'" +end +a:close() +p[e..":"..t]=nil +return true +end +he=function() +for e,t in N(n)do +t:close() +n[e]=nil +end +a=0 +o=0 +g=0 +p={} +d={} +r={} +U={} +n={} +end +Z=function() +return _,T,O,A,E,z,x,L,q,j +end +ie=function(e) +if M(e)~="table"then +return nil,"invalid settings table" +end +_=v(e.timeout)or _ +T=v(e.sleeptime)or T +O=v(e.maxsendlen)or O +A=v(e.maxreadlen)or A +E=v(e.checkinterval)or E +z=v(e.sendtimeout)or z +x=v(e.readtimeout)or x +L=e.cleanqueue +q=e._maxclientsperserver or q +j=e._maxsslhandshake or j +return true +end +B=function(e) +if M(e)~="function"then +return nil,"invalid listener function" +end +g=g+1 +U[g]=e +return true +end +re=function() +return R,H,a,o,g +end +local t; +local function y(e) +t=not not e; +end +G=function(a) +if t then return"quitting";end +if a then t="once";end +local e=ae; +repeat +local a,o,s=me(d,r,Y(_,e)) +for t,e in ue(o)do +local t=n[e] +if t then +t.sendbuffer() +else +P(e) +i"server.lua: found no handler and closed socket (writelist)" +end +end +for e,t in ue(a)do +local e=n[t] +if e then +e.readbuffer() +else +P(t) +i"server.lua: found no handler and closed socket (readlist)" +end +end +for e,t in N(I)do +e.disconnect()(e,t) +e:close(true) +end +ce(I) +u=K() +if u-D>=Y(e,1)then +e=ae; +for t=1,g do +local t=U[t](u) +if t then e=Y(e,t);end +end +D=u +else +e=e-(u-D); +end +fe(T) +until t; +if a and t=="once"then t=nil;return;end +return"quitting" +end +local function l() +return G(true); +end +local function p() +return"select"; +end +local i=function(t,d,s,a,e,i) +local e=C(nil,a,t,d,s,"clientport",e,i) +n[t]=e +if not i then +o=f(r,t,o) +if a.onconnect then +local i=e.sendbuffer; +e.sendbuffer=function() +o=h(r,t,o); +e.sendbuffer=i; +a.onconnect(e); +if#e:bufferqueue()>0 then +return i(); +end +end +end +end +return e,t +end +local t=function(o,a,n,h,r) +local e,t=S.tcp() +if t then +return nil,t +end +e:settimeout(0) +m,t=e:connect(o,a) +if t then +local e=i(e,o,a,n) +else +C(nil,n,e,o,a,"clientport",h,r) +end +end +s"setmetatable"(n,{__mode="k"}) +s"setmetatable"(b,{__mode="k"}) +s"setmetatable"(w,{__mode="k"}) +D=K() +V=K() +B(function() +local e=W(u-V) +if e>E then +V=u +for e,t in N(w)do +if W(u-t)>z then +e.disconnect()(e,"send timeout") +e:close(true) +end +end +for e,t in N(b)do +if W(u-t)>x then +e.disconnect()(e,"read timeout") +e:close() +end +end +end +end +) +local function a(e) +local t=F; +if e then +F=e; +end +return t; +end +return{ +addclient=t, +wrapclient=i, +loop=G, +link=c, +step=l, +stats=re, +closeall=he, +addtimer=B, +addserver=se, +getserver=ee, +setlogger=a, +getsettings=Z, +setquitting=y, +removeserver=oe, +get_backend=p, +changesettings=ie, +} +end) +package.preload['util.xmppstream']=(function(...) +local e=require"lxp"; +local t=require"util.stanza"; +local w=t.stanza_mt; +local a=tostring; +local h=table.insert; +local f=table.concat; +local g=table.remove; +local y=setmetatable; +local u=require"util.logger".init("xmppstream"); +local b=pcall(e.new,{StartDoctypeDecl=false}); +if not b then +u("warn","The version of LuaExpat on your system leaves Prosody " +.."vulnerable to denial-of-service attacks. You should upgrade to " +.."LuaExpat 1.1.1 or higher as soon as possible. See " +.."http://prosody.im/doc/depends#luaexpat for more information."); +end +local v=error; +module"xmppstream" +local p=e.new; +local k={ +["http://www.w3.org/XML/1998/namespace"]="xml"; +}; +local o="http://etherx.jabber.org/streams"; +local s="\1"; +local l="^([^"..s.."]*)"..s.."?(.*)$"; +_M.ns_separator=s; +_M.ns_pattern=l; +function new_sax_handlers(t,e) +local i={}; +local p=t.log or u; +local m=e.streamopened; +local c=e.streamclosed; +local r=e.error or function(t,e)v("XML stream error: "..a(e));end; +local j=e.handlestanza; +local a=e.stream_ns or o; +local d=e.stream_tag or"stream"; +if a~=""then +d=a..s..d; +end +local q=a..s..(e.error_tag or"error"); +local x=e.default_ns; +local s={}; +local o,e={}; +local n=0; +function i:StartElement(u,a) +if e and#o>0 then +h(e,f(o)); +o={}; +end +local i,o=u:match(l); +if o==""then +i,o="",i; +end +if i~=x or n>0 then +a.xmlns=i; +n=n+1; +end +for e=1,#a do +local t=a[e]; +a[e]=nil; +local e,o=t:match(l); +if o~=""then +e=k[e]; +if e then +a[e..":"..o]=a[t]; +a[t]=nil; +end +end +end +if not e then +if t.notopen then +if u==d then +n=0; +if m then +m(t,a); +end +else +r(t,"no-stream"); +end +return; +end +if i=="jabber:client"and o~="iq"and o~="presence"and o~="message"then +r(t,"invalid-top-level-element"); +end +e=y({name=o,attr=a,tags={}},w); +else +h(s,e); +local t=e; +e=y({name=o,attr=a,tags={}},w); +h(t,e); +h(t.tags,e); +end +end +function i:CharacterData(t) +if e then +h(o,t); +end +end +function i:EndElement(a) +if n>0 then +n=n-1; +end +if e then +if#o>0 then +h(e,f(o)); +o={}; +end +if#s==0 then +if a~=q then +j(t,e); +else +r(t,"stream-error",e); +end +e=nil; +else +e=g(s); +end +else +if a==d then +if c then +c(t); +end +else +local a,e=a:match(l); +if e==""then +a,e="",a; +end +r(t,"parse-error","unexpected-element-close",e); +end +e,o=nil,{}; +s={}; +end +end +local function a(e) +r(t,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1."); +if not e.stop or not e:stop()then +v("Failed to abort parsing"); +end +end +if b then +i.StartDoctypeDecl=a; +end +i.Comment=a; +i.ProcessingInstruction=a; +local function n() +e,o=nil,{}; +s={}; +end +local function a(a,e) +t=e; +p=e.log or u; +end +return i,{reset=n,set_session=a}; +end +function new(t,e) +local t,o=new_sax_handlers(t,e); +local e=p(t,s); +local a=e.parse; +return{ +reset=function() +e=p(t,s); +a=e.parse; +o.reset(); +end, +feed=function(o,t) +return a(e,t); +end, +set_session=o.set_session; +}; +end +return _M; +end) +package.preload['util.jid']=(function(...) +local a=string.match; +local h=require"util.encodings".stringprep.nodeprep; +local r=require"util.encodings".stringprep.nameprep; +local d=require"util.encodings".stringprep.resourceprep; +local n={ +[" "]="\\20";['"']="\\22"; +["&"]="\\26";["'"]="\\27"; +["/"]="\\2f";[":"]="\\3a"; +["<"]="\\3c";[">"]="\\3e"; +["@"]="\\40";["\\"]="\\5c"; +}; +local s={}; +for e,t in pairs(n)do s[t]=e;end +module"jid" +local function t(e) +if not e then return;end +local i,t=a(e,"^([^@/]+)@()"); +local t,o=a(e,"^([^@/]+)()",t) +if i and not t then return nil,nil,nil;end +local a=a(e,"^/(.+)$",o); +if(not t)or((not a)and#e>=o)then return nil,nil,nil;end +return i,t,a; +end +split=t; +function bare(e) +local t,e=t(e); +if t and e then +return t.."@"..e; +end +return e; +end +local function o(e) +local t,a,e=t(e); +if a then +a=r(a); +if not a then return;end +if t then +t=h(t); +if not t then return;end +end +if e then +e=d(e); +if not e then return;end +end +return t,a,e; +end +end +prepped_split=o; +function prep(e) +local a,e,t=o(e); +if e then +if a then +e=a.."@"..e; +end +if t then +e=e.."/"..t; +end +end +return e; +end +function join(a,e,t) +if a and e and t then +return a.."@"..e.."/"..t; +elseif a and e then +return a.."@"..e; +elseif e and t then +return e.."/"..t; +elseif e then +return e; +end +return nil; +end +function compare(e,a) +local i,o,n=t(e); +local a,e,t=t(a); +if((a~=nil and a==i)or a==nil)and +((e~=nil and e==o)or e==nil)and +((t~=nil and t==n)or t==nil)then +return true +end +return false +end +function escape(e)return e and(e:gsub(".",n));end +function unescape(e)return e and(e:gsub("\\%x%x",s));end +return _M; +end) +package.preload['util.events']=(function(...) +local i=pairs; +local h=table.insert; +local s=table.sort; +local r=setmetatable; +local n=next; +module"events" +function new() +local t={}; +local e={}; +local function o(o,a) +local e=e[a]; +if not e or n(e)==nil then return;end +local t={}; +for e in i(e)do +h(t,e); +end +s(t,function(a,t)return e[a]>e[t];end); +o[a]=t; +return t; +end; +r(t,{__index=o}); +local function h(o,n,i) +local a=e[o]; +if a then +a[n]=i or 0; +else +a={[n]=i or 0}; +e[o]=a; +end +t[o]=nil; +end; +local function s(a,i) +local o=e[a]; +if o then +o[i]=nil; +t[a]=nil; +if n(o)==nil then +e[a]=nil; +end +end +end; +local function o(e) +for t,e in i(e)do +h(t,e); +end +end; +local function n(e) +for e,t in i(e)do +s(e,t); +end +end; +local function a(e,...) +local e=t[e]; +if e then +for t=1,#e do +local e=e[t](...); +if e~=nil then return e;end +end +end +end; +return{ +add_handler=h; +remove_handler=s; +add_handlers=o; +remove_handlers=n; +fire_event=a; +_handlers=t; +_event_map=e; +}; +end +return _M; +end) +package.preload['util.dataforms']=(function(...) +local e=setmetatable; +local t,i=pairs,ipairs; +local n,s,l=tostring,type,next; +local h=table.concat; +local u=require"util.stanza"; +local d=require"util.jid".prep; +module"dataforms" +local c='jabber:x:data'; +local r={}; +local t={__index=r}; +function new(a) +return e(a,t); +end +function from_stanza(e) +local o={ +title=e:get_child_text("title"); +instructions=e:get_child_text("instructions"); +}; +for e in e:childtags("field")do +local a={ +name=e.attr.var; +label=e.attr.label; +type=e.attr.type; +required=e:get_child("required")and true or nil; +value=e:get_child_text("value"); +}; +o[#o+1]=a; +if a.type then +local t={}; +if a.type:match"list%-"then +for e in e:childtags("option")do +t[#t+1]={label=e.attr.label,value=e:get_child_text("value")}; +end +for e in e:childtags("value")do +t[#t+1]={label=e.attr.label,value=e:get_text(),default=true}; +end +elseif a.type:match"%-multi"then +for e in e:childtags("value")do +t[#t+1]=e.attr.label and{label=e.attr.label,value=e:get_text()}or e:get_text(); +end +if a.type=="text-multi"then +a.value=h(t,"\n"); +else +a.value=t; +end +end +end +end +return new(o); +end +function r.form(t,a,e) +local e=u.stanza("x",{xmlns=c,type=e or"form"}); +if t.title then +e:tag("title"):text(t.title):up(); +end +if t.instructions then +e:tag("instructions"):text(t.instructions):up(); +end +for t,o in i(t)do +local t=o.type or"text-single"; +e:tag("field",{type=t,var=o.name,label=o.label}); +local a=(a and a[o.name])or o.value; +if a then +if t=="hidden"then +if s(a)=="table"then +e:tag("value") +:add_child(a) +:up(); +else +e:tag("value"):text(n(a)):up(); +end +elseif t=="boolean"then +e:tag("value"):text((a and"1")or"0"):up(); +elseif t=="fixed"then +elseif t=="jid-multi"then +for a,t in i(a)do +e:tag("value"):text(t):up(); +end +elseif t=="jid-single"then +e:tag("value"):text(a):up(); +elseif t=="text-single"or t=="text-private"then +e:tag("value"):text(a):up(); +elseif t=="text-multi"then +for t in a:gmatch("([^\r\n]+)\r?\n*")do +e:tag("value"):text(t):up(); +end +elseif t=="list-single"then +local o=false; +for a,t in i(a)do +if s(t)=="table"then +e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up(); +if t.default and(not o)then +e:tag("value"):text(t.value):up(); +o=true; +end +else +e:tag("option",{label=t}):tag("value"):text(n(t)):up():up(); +end +end +elseif t=="list-multi"then +for a,t in i(a)do +if s(t)=="table"then +e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up(); +if t.default then +e:tag("value"):text(t.value):up(); +end +else +e:tag("option",{label=t}):tag("value"):text(n(t)):up():up(); +end +end +end +end +if o.required then +e:tag("required"):up(); +end +e:up(); +end +return e; +end +local e={}; +function r.data(t,s) +local n={}; +local a={}; +for o,t in i(t)do +local o; +for e in s:childtags()do +if t.name==e.attr.var then +o=e; +break; +end +end +if not o then +if t.required then +a[t.name]="Required value missing"; +end +else +local e=e[t.type]; +if e then +n[t.name],a[t.name]=e(o,t.required); +end +end +end +if l(a)then +return n,a; +end +return n; +end +e["text-single"]= +function(t,a) +local t=t:get_child_text("value"); +if t and#t>0 then +return t +elseif a then +return nil,"Required value missing"; +end +end +e["text-private"]= +e["text-single"]; +e["jid-single"]= +function(t,o) +local a=t:get_child_text("value") +local t=d(a); +if t and#t>0 then +return t +elseif a then +return nil,"Invalid JID: "..a; +elseif o then +return nil,"Required value missing"; +end +end +e["jid-multi"]= +function(o,i) +local a={}; +local t={}; +for e in o:childtags("value")do +local e=e:get_text(); +local o=d(e); +a[#a+1]=o; +if e and not o then +t[#t+1]=("Invalid JID: "..e); +end +end +if#a>0 then +return a,(#t>0 and h(t,"\n")or nil); +elseif i then +return nil,"Required value missing"; +end +end +e["list-multi"]= +function(a,o) +local t={}; +for e in a:childtags("value")do +t[#t+1]=e:get_text(); +end +return t,(o and#t==0 and"Required value missing"or nil); +end +e["text-multi"]= +function(t,a) +local t,a=e["list-multi"](t,a); +if t then +t=h(t,"\n"); +end +return t,a; +end +e["list-single"]= +e["text-single"]; +local a={ +["1"]=true,["true"]=true, +["0"]=false,["false"]=false, +}; +e["boolean"]= +function(t,o) +local t=t:get_child_text("value"); +local a=a[t~=nil and t]; +if a~=nil then +return a; +elseif t then +return nil,"Invalid boolean representation"; +elseif o then +return nil,"Required value missing"; +end +end +e["hidden"]= +function(e) +return e:get_child_text("value"); +end +return _M; +end) +package.preload['util.caps']=(function(...) +local l=require"util.encodings".base64.encode; +local d=require"util.hashes".sha1; +local n,h,s=table.insert,table.sort,table.concat; +local r=ipairs; +module"caps" +function calculate_hash(e) +local a,o,i={},{},{}; +for t,e in r(e)do +if e.name=="identity"then +n(a,(e.attr.category or"").."\0"..(e.attr.type or"").."\0"..(e.attr["xml:lang"]or"").."\0"..(e.attr.name or"")); +elseif e.name=="feature"then +n(o,e.attr.var or""); +elseif e.name=="x"and e.attr.xmlns=="jabber:x:data"then +local t={}; +local o; +for a,e in r(e.tags)do +if e.name=="field"and e.attr.var then +local a={}; +for t,e in r(e.tags)do +e=#e.tags==0 and e:get_text(); +if e then n(a,e);end +end +h(a); +if e.attr.var=="FORM_TYPE"then +o=a[1]; +elseif#a>0 then +n(t,e.attr.var.."\0"..s(a,"<")); +else +n(t,e.attr.var); +end +end +end +h(t); +t=s(t,"<"); +if o then t=o.."\0"..t;end +n(i,t); +end +end +h(a); +h(o); +h(i); +if#a>0 then a=s(a,"<"):gsub("%z","/").."<";else a="";end +if#o>0 then o=s(o,"<").."<";else o="";end +if#i>0 then i=s(i,"<"):gsub("%z","<").."<";else i="";end +local e=a..o..i; +local t=l(d(e)); +return t,e; +end +return _M; +end) +package.preload['util.vcard']=(function(...) +local o=require"util.stanza"; +local a,u=table.insert,table.concat; +local h=type; +local e,r,f=next,pairs,ipairs; +local m,c,d,l; +local w="\n"; +local i; +local function e() +error"Not implemented" +end +local function e() +error"Not implemented" +end +local function y(e) +return e:gsub("[,:;\\]","\\%1"):gsub("\n","\\n"); +end +local function s(e) +return e:gsub("\\?[\\nt:;,]",{ +["\\\\"]="\\", +["\\n"]="\n", +["\\r"]="\r", +["\\t"]="\t", +["\\:"]=":", +["\\;"]=";", +["\\,"]=",", +[":"]="\29", +[";"]="\30", +[","]="\31", +}); +end +local function p(e) +local a=o.stanza(e.name,{xmlns="vcard-temp"}); +local t=i[e.name]; +if t=="text"then +a:text(e[1]); +elseif h(t)=="table"then +if t.types and e.TYPE then +if h(e.TYPE)=="table"then +for o,t in r(t.types)do +for o,e in r(e.TYPE)do +if e:upper()==t then +a:tag(t):up(); +break; +end +end +end +else +a:tag(e.TYPE:upper()):up(); +end +end +if t.props then +for o,t in r(t.props)do +if e[t]then +a:tag(t):up(); +end +end +end +if t.value then +a:tag(t.value):text(e[1]):up(); +elseif t.values then +local o=t.values; +local i=o.behaviour=="repeat-last"and o[#o]; +for o=1,#e do +a:tag(t.values[o]or i):text(e[o]):up(); +end +end +end +return a; +end +local function n(t) +local e=o.stanza("vCard",{xmlns="vcard-temp"}); +for a=1,#t do +e:add_child(p(t[a])); +end +return e; +end +function l(e) +if not e[1]or e[1].name then +return n(e) +else +local t=o.stanza("xCard",{xmlns="vcard-temp"}); +for a=1,#e do +t:add_child(n(e[a])); +end +return t; +end +end +function m(t) +t=t +:gsub("\r\n","\n") +:gsub("\n ","") +:gsub("\n\n+","\n"); +local h={}; +local e; +for t in t:gmatch("[^\n]+")do +local t=s(t); +local s,t,n=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); +n=n:gsub("\29",":"); +if#t>0 then +local a={}; +for e,i,o in t:gmatch("\30([^=]+)(=?)([^\30]*)")do +e=e:upper(); +local t={}; +for e in o:gmatch("[^\31]+")do +t[#t+1]=e +t[e]=true; +end +if i=="="then +a[e]=t; +else +a[e]=true; +end +end +t=a; +end +if s=="BEGIN"and n=="VCARD"then +e={}; +h[#h+1]=e; +elseif s=="END"and n=="VCARD"then +e=nil; +elseif e and i[s]then +local o=i[s]; +local i={name=s}; +e[#e+1]=i; +local s=e; +e=i; +if o.types then +for o,a in f(o.types)do +local a=a:lower(); +if(t.TYPE and t.TYPE[a]==true) +or t[a]==true then +e.TYPE=a; +end +end +end +if o.props then +for o,a in f(o.props)do +if t[a]then +if t[a]==true then +e[a]=true; +else +for o,t in f(t[a])do +e[a]=t; +end +end +end +end +end +if o=="text"or o.value then +a(e,n); +elseif o.values then +local t="\30"..n; +for t in t:gmatch("\30([^\30]*)")do +a(e,t); +end +end +e=s; +end +end +return h; +end +local function o(e) +local t={}; +for a=1,#e do +t[a]=y(e[a]); +end +t=u(t,";"); +local a=""; +for e,t in r(e)do +if h(e)=="string"and e~="name"then +a=a..(";%s=%s"):format(e,h(t)=="table"and u(t,",")or t); +end +end +return("%s%s:%s"):format(e.name,a,t) +end +local function t(t) +local e={}; +a(e,"BEGIN:VCARD") +for i=1,#t do +a(e,o(t[i])); +end +a(e,"END:VCARD") +return u(e,w); +end +function c(e) +if e[1]and e[1].name then +return t(e) +else +local a={}; +for o=1,#e do +a[o]=t(e[o]); +end +return u(a,w); +end +end +local function n(o) +local e=o.name; +local t=i[e]; +local e={name=e}; +if t=="text"then +e[1]=o:get_text(); +elseif h(t)=="table"then +if t.value then +e[1]=o:get_child_text(t.value)or""; +elseif t.values then +local t=t.values; +if t.behaviour=="repeat-last"then +for t=1,#o.tags do +a(e,o.tags[t]:get_text()or""); +end +else +for i=1,#t do +a(e,o:get_child_text(t[i])or""); +end +end +elseif t.names then +local t=t.names; +for a=1,#t do +if o:get_child(t[a])then +e[1]=t[a]; +break; +end +end +end +if t.props_verbatim then +for t,a in r(t.props_verbatim)do +e[t]=a; +end +end +if t.types then +local t=t.types; +e.TYPE={}; +for i=1,#t do +if o:get_child(t[i])then +a(e.TYPE,t[i]:lower()); +end +end +if#e.TYPE==0 then +e.TYPE=nil; +end +end +if t.props then +local t=t.props; +for i=1,#t do +local t=t[i] +local o=o:get_child_text(t); +if o then +e[t]=e[t]or{}; +a(e[t],o); +end +end +end +else +return nil +end +return e; +end +local function o(e) +local t=e.tags; +local e={}; +for o=1,#t do +a(e,n(t[o])); +end +return e +end +function d(e) +if e.attr.xmlns~="vcard-temp"then +return nil,"wrong-xmlns"; +end +if e.name=="xCard"then +local t={}; +local e=e.tags; +for a=1,#e do +t[a]=o(e[a]); +end +return t +elseif e.name=="vCard"then +return o(e) +end +end +i={ +VERSION="text", +FN="text", +N={ +values={ +"FAMILY", +"GIVEN", +"MIDDLE", +"PREFIX", +"SUFFIX", +}, +}, +NICKNAME="text", +PHOTO={ +props_verbatim={ENCODING={"b"}}, +props={"TYPE"}, +value="BINVAL", +}, +BDAY="text", +ADR={ +types={ +"HOME", +"WORK", +"POSTAL", +"PARCEL", +"DOM", +"INTL", +"PREF", +}, +values={ +"POBOX", +"EXTADD", +"STREET", +"LOCALITY", +"REGION", +"PCODE", +"CTRY", +} +}, +LABEL={ +types={ +"HOME", +"WORK", +"POSTAL", +"PARCEL", +"DOM", +"INTL", +"PREF", +}, +value="LINE", +}, +TEL={ +types={ +"HOME", +"WORK", +"VOICE", +"FAX", +"PAGER", +"MSG", +"CELL", +"VIDEO", +"BBS", +"MODEM", +"ISDN", +"PCS", +"PREF", +}, +value="NUMBER", +}, +EMAIL={ +types={ +"HOME", +"WORK", +"INTERNET", +"PREF", +"X400", +}, +value="USERID", +}, +JABBERID="text", +MAILER="text", +TZ="text", +GEO={ +values={ +"LAT", +"LON", +}, +}, +TITLE="text", +ROLE="text", +LOGO="copy of PHOTO", +AGENT="text", +ORG={ +values={ +behaviour="repeat-last", +"ORGNAME", +"ORGUNIT", +} +}, +CATEGORIES={ +values="KEYWORD", +}, +NOTE="text", +PRODID="text", +REV="text", +SORTSTRING="text", +SOUND="copy of PHOTO", +UID="text", +URL="text", +CLASS={ +names={ +"PUBLIC", +"PRIVATE", +"CONFIDENTIAL", +}, +}, +KEY={ +props={"TYPE"}, +value="CRED", +}, +DESC="text", +}; +i.LOGO=i.PHOTO; +i.SOUND=i.PHOTO; +return{ +from_text=m; +to_text=c; +from_xep54=d; +to_xep54=l; +lua_to_text=c; +lua_to_xep54=l; +text_to_lua=m; +text_to_xep54=function(...)return l(m(...));end; +xep54_to_lua=d; +xep54_to_text=function(...)return c(d(...))end; +}; +end) +package.preload['util.logger']=(function(...) +local e=pcall; +local e=string.find; +local e,n,e=ipairs,pairs,setmetatable; +module"logger" +local e,t={},{}; +local a={}; +local o; +function init(e) +local i=o(e,"debug"); +local a=o(e,"info"); +local n=o(e,"warn"); +local o=o(e,"error"); +local e=#e; +return function(t,e,...) +if t=="debug"then +return i(e,...); +elseif t=="info"then +return a(e,...); +elseif t=="warn"then +return n(e,...); +elseif t=="error"then +return o(e,...); +end +end +end +function o(i,o) +local a=t[o]; +if not a then +a={}; +t[o]=a; +end +local e=e[i]; +local e=function(t,...) +if e then +for a=1,#e do +if e[a](i,o,t,...)==false then +return; +end +end +end +for e=1,#a do +a[e](i,o,t,...); +end +end +return e; +end +function reset() +for t in n(e)do e[t]=nil;end +for t,e in n(t)do +for t=1,#e do +e[t]=nil; +end +end +for e in n(a)do a[e]=nil;end +end +function add_level_sink(e,a) +if not t[e]then +t[e]={a}; +else +t[e][#t[e]+1]=a; +end +end +function add_name_sink(t,a,o) +if not e[t]then +e[t]={a}; +else +e[t][#e[t]+1]=a; +end +end +function add_name_pattern_sink(e,t,o) +if not a[e]then +a[e]={t}; +else +a[e][#a[e]+1]=t; +end +end +_M.new=o; +return _M; +end) +package.preload['util.datetime']=(function(...) +local e=os.date; +local i=os.time; +local u=os.difftime; +local t=error; +local h=tonumber; +module"datetime" +function date(t) +return e("!%Y-%m-%d",t); +end +function datetime(t) +return e("!%Y-%m-%dT%H:%M:%SZ",t); +end +function time(t) +return e("!%H:%M:%S",t); +end +function legacy(t) +return e("!%Y%m%dT%H:%M:%S",t); +end +function parse(o) +if o then +local n,s,r,d,l,t,a; +n,s,r,d,l,t,a=o:match("^(%d%d%d%d)-?(%d%d)-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-].*)$"); +if n then +local u=u(i(e("*t")),i(e("!*t"))); +local o=0; +if a~=""and a~="Z"then +local a,t,e=a:match("([+%-])(%d%d):?(%d*)"); +if not a then return;end +if#e~=2 then e="0";end +t,e=h(t),h(e); +o=t*60*60+e*60; +if a=="-"then o=-o;end +end +t=(t+u)-o; +return i({year=n,month=s,day=r,hour=d,min=l,sec=t,isdst=false}); +end +end +end +return _M; +end) +package.preload['util.sasl.plain']=(function(...) +return function(e,t) +if t=="PLAIN"and e.username and e.password then +return function(e) +return"success"==coroutine.yield("\0"..e.username.."\0"..e.password); +end,5; +end +end +end) +package.preload['verse.plugins.tls']=(function(...) +local a=require"verse"; +local t="urn:ietf:params:xml:ns:xmpp-tls"; +function a.plugins.tls(e) +local function o(o) +if e.authenticated then return;end +if o:get_child("starttls",t)and e.conn.starttls then +e:debug("Negotiating TLS..."); +e:send(a.stanza("starttls",{xmlns=t})); +return true; +elseif not e.conn.starttls and not e.secure then +e:warn("SSL libary (LuaSec) not loaded, so TLS not available"); +elseif not e.secure then +e:debug("Server doesn't offer TLS :("); +end +end +local function i(t) +if t.name=="proceed"then +e:debug("Server says proceed, handshake starting..."); +e.conn:starttls({mode="client",protocol="sslv23",options="no_sslv2"},true); +end +end +local function a(t) +if t=="ssl-handshake-complete"then +e.secure=true; +e:debug("Re-opening stream..."); +e:reopen(); +end +end +e:hook("stream-features",o,400); +e:hook("stream/"..t,i); +e:hook("status",a,400); +return true; +end +end) +package.preload['verse.plugins.sasl']=(function(...) +local s,r=require"mime".b64,require"mime".unb64; +local o="urn:ietf:params:xml:ns:xmpp-sasl"; +function verse.plugins.sasl(e) +local function h(t) +if e.authenticated then return;end +e:debug("Authenticating with SASL..."); +local t=t:get_child("mechanisms",o); +if not t then return end +local a={}; +local i={}; +for t in t:childtags("mechanism")do +t=t:get_text(); +e:debug("Server offers %s",t); +if not a[t]then +local n=t:match("[^-]+"); +local s,o=pcall(require,"util.sasl."..n:lower()); +if s then +e:debug("Loaded SASL %s module",n); +a[t],i[t]=o(e,t); +elseif not tostring(o):match("not found")then +e:debug("Loading failed: %s",tostring(o)); +end +end +end +local t={}; +for e in pairs(a)do +table.insert(t,e); +end +if not t[1]then +e:event("authentication-failure",{condition="no-supported-sasl-mechanisms"}); +e:close(); +return; +end +table.sort(t,function(e,t)return i[e]>i[t];end); +local t,i=t[1]; +e:debug("Selecting %s mechanism...",t); +e.sasl_mechanism=coroutine.wrap(a[t]); +i=e:sasl_mechanism(t); +local t=verse.stanza("auth",{xmlns=o,mechanism=t}); +if i then +t:text(s(i)); +end +e:send(t); +return true; +end +local function i(t) +if t.name=="failure"then +local a=t.tags[1]; +local t=t:get_child_text("text"); +e:event("authentication-failure",{condition=a.name,text=t}); +e:close(); +return false; +end +local t,a=e.sasl_mechanism(t.name,r(t:get_text())); +if not t then +e:event("authentication-failure",{condition=a}); +e:close(); +return false; +elseif t==true then +e:event("authentication-success"); +e.authenticated=true +e:reopen(); +else +e:send(verse.stanza("response",{xmlns=o}):text(s(t))); +end +return true; +end +e:hook("stream-features",h,300); +e:hook("stream/"..o,i); +return true; +end +end) +package.preload['verse.plugins.bind']=(function(...) +local t=require"verse"; +local o=require"util.jid"; +local a="urn:ietf:params:xml:ns:xmpp-bind"; +function t.plugins.bind(e) +local function i(i) +if e.bound then return;end +e:debug("Binding resource..."); +e:send_iq(t.iq({type="set"}):tag("bind",{xmlns=a}):tag("resource"):text(e.resource), +function(t) +if t.attr.type=="result"then +local t=t +:get_child("bind",a) +:get_child_text("jid"); +e.username,e.host,e.resource=o.split(t); +e.jid,e.bound=t,true; +e:event("bind-success",{jid=t}); +elseif t.attr.type=="error"then +local a=t:child_with_name("error"); +local a,t,o=t:get_error(); +e:event("bind-failure",{error=t,text=o,type=a}); +end +end); +end +e:hook("stream-features",i,200); +return true; +end +end) +package.preload['verse.plugins.session']=(function(...) +local a=require"verse"; +local o="urn:ietf:params:xml:ns:xmpp-session"; +function a.plugins.session(e) +local function n(t) +local t=t:get_child("session",o); +if t and not t:get_child("optional")then +local function i(t) +e:debug("Establishing Session..."); +e:send_iq(a.iq({type="set"}):tag("session",{xmlns=o}), +function(t) +if t.attr.type=="result"then +e:event("session-success"); +elseif t.attr.type=="error"then +local a=t:child_with_name("error"); +local t,o,a=t:get_error(); +e:event("session-failure",{error=o,text=a,type=t}); +end +end); +return true; +end +e:hook("bind-success",i); +end +end +e:hook("stream-features",n); +return true; +end +end) +package.preload['verse.plugins.legacy']=(function(...) +local i=require"verse"; +local n=require"util.uuid".generate; +local a="jabber:iq:auth"; +function i.plugins.legacy(e) +function handle_auth_form(t) +local o=t:get_child("query",a); +if t.attr.type~="result"or not o then +local o,t,a=t:get_error(); +e:debug("warn","%s %s: %s",o,t,a); +end +local t={ +username=e.username; +password=e.password; +resource=e.resource or n(); +digest=false,sequence=false,token=false; +}; +local a=i.iq({to=e.host,type="set"}) +:tag("query",{xmlns=a}); +if#o>0 then +for o in o:childtags()do +local o=o.name; +local i=t[o]; +if i then +a:tag(o):text(t[o]):up(); +elseif i==nil then +local t="feature-not-implemented"; +e:event("authentication-failure",{condition=t}); +return false; +end +end +else +for t,e in pairs(t)do +if e then +a:tag(t):text(e):up(); +end +end +end +e:send_iq(a,function(a) +if a.attr.type=="result"then +e.resource=t.resource; +e.jid=t.username.."@"..e.host.."/"..t.resource; +e:event("authentication-success"); +e:event("bind-success",e.jid); +else +local a,t,a=a:get_error(); +e:event("authentication-failure",{condition=t}); +end +end); +end +function handle_opened(t) +if not t.version then +e:send_iq(i.iq({type="get"}) +:tag("query",{xmlns="jabber:iq:auth"}) +:tag("username"):text(e.username), +handle_auth_form); +end +end +e:hook("opened",handle_opened); +end +end) +package.preload['verse.plugins.compression']=(function(...) +local a=require"verse"; +local e=require"zlib"; +local t="http://jabber.org/features/compress" +local t="http://jabber.org/protocol/compress" +local o="http://etherx.jabber.org/streams"; +local n=9; +local function i(o) +local i,e=pcall(e.deflate,n); +if i==false then +local t=a.stanza("failure",{xmlns=t}):tag("setup-failed"); +o:send(t); +o:error("Failed to create zlib.deflate filter: %s",tostring(e)); +return +end +return e +end +local function s(o) +local i,e=pcall(e.inflate); +if i==false then +local t=a.stanza("failure",{xmlns=t}):tag("setup-failed"); +o:send(t); +o:error("Failed to create zlib.inflate filter: %s",tostring(e)); +return +end +return e +end +local function n(e,o) +function e:send(i) +local i,o,n=pcall(o,tostring(i),'sync'); +if i==false then +e:close({ +condition="undefined-condition"; +text=o; +extra=a.stanza("failure",{xmlns=t}):tag("processing-failed"); +}); +e:warn("Compressed send failed: %s",tostring(o)); +return; +end +e.conn:write(o); +end; +end +local function h(e,o) +local s=e.data +e.data=function(i,n) +e:debug("Decompressing data..."); +local n,o,h=pcall(o,n); +if n==false then +e:close({ +condition="undefined-condition"; +text=o; +extra=a.stanza("failure",{xmlns=t}):tag("processing-failed"); +}); +stream:warn("%s",tostring(o)); +return; +end +return s(i,o); +end; +end +function a.plugins.compression(e) +local function r(o) +if not e.compressed then +local o=o:child_with_name("compression"); +if o then +for o in o:children()do +local o=o[1] +if o=="zlib"then +e:send(a.stanza("compress",{xmlns=t}):tag("method"):text("zlib")) +e:debug("Enabled compression using zlib.") +return true; +end +end +session:debug("Remote server supports no compression algorithm we support.") +end +end +end +local function o(t) +if t.name=="compressed"then +e:debug("Activating compression...") +local t=i(e); +if not t then return end +local a=s(e); +if not a then return end +n(e,t); +h(e,a); +e.compressed=true; +e:reopen(); +elseif t.name=="failure"then +e:warn("Failed to establish compression"); +end +end +e:hook("stream-features",r,250); +e:hook("stream/"..t,o); +end +end) +package.preload['verse.plugins.smacks']=(function(...) +local i=require"verse"; +local h=socket.gettime; +local s="urn:xmpp:sm:2"; +function i.plugins.smacks(e) +local t={}; +local o=0; +local r=h(); +local a; +local n=0; +local function d(t) +if t.attr.xmlns=="jabber:client"or not t.attr.xmlns then +n=n+1; +e:debug("Increasing handled stanzas to %d for %s",n,t:top_tag()); +end +end +function outgoing_stanza(o) +if o.name and not o.attr.xmlns then +t[#t+1]=tostring(o); +r=h(); +if not a then +a=true; +e:debug("Waiting to send ack request..."); +i.add_task(1,function() +if#t==0 then +a=false; +return; +end +local o=h()-r; +if o<1 and#t<10 then +return 1-o; +end +e:debug("Time up, sending ..."); +a=false; +e:send(i.stanza("r",{xmlns=s})); +end); +end +end +end +local function h() +e:debug("smacks: connection lost"); +e.stream_management_supported=nil; +if e.resumption_token then +e:debug("smacks: have resumption token, reconnecting in 1s..."); +e.authenticated=nil; +i.add_task(1,function() +e:connect(e.connect_host or e.host,e.connect_port or 5222); +end); +return true; +end +end +local function r() +e.resumption_token=nil; +e:unhook("disconnected",h); +end +local function l(a) +if a.name=="r"then +e:debug("Ack requested... acking %d handled stanzas",n); +e:send(i.stanza("a",{xmlns=s,h=tostring(n)})); +elseif a.name=="a"then +local a=tonumber(a.attr.h); +if a>o then +local i=#t; +for a=o+1,a do +table.remove(t,1); +end +e:debug("Received ack: New ack: "..a.." Last ack: "..o.." Unacked stanzas now: "..#t.." (was "..i..")"); +o=a; +else +e:warn("Received bad ack for "..a.." when last ack was "..o); +end +elseif a.name=="enabled"then +if a.attr.id then +e.resumption_token=a.attr.id; +e:hook("closed",r,100); +e:hook("disconnected",h,100); +end +elseif a.name=="resumed"then +local a=tonumber(a.attr.h); +if a>o then +local i=#t; +for a=o+1,a do +table.remove(t,1); +end +e:debug("Received ack: New ack: "..a.." Last ack: "..o.." Unacked stanzas now: "..#t.." (was "..i..")"); +o=a; +end +for a=1,#t do +e:send(t[a]); +end +t={}; +e:debug("Resumed successfully"); +e:event("resumed"); +else +e:warn("Don't know how to handle "..s.."/"..a.name); +end +end +local function o() +if not e.smacks then +e:debug("smacks: sending enable"); +e:send(i.stanza("enable",{xmlns=s,resume="true"})); +e.smacks=true; +e:hook("stanza",d); +e:hook("outgoing",outgoing_stanza); +end +end +local function a(t) +if t:get_child("sm",s)then +e.stream_management_supported=true; +if e.smacks and e.bound then +e:debug("Resuming stream with %d handled stanzas",n); +e:send(i.stanza("resume",{xmlns=s, +h=n,previd=e.resumption_token})); +return true; +else +e:hook("bind-success",o,1); +end +end +end +e:hook("stream-features",a,250); +e:hook("stream/"..s,l); +end +end) +package.preload['verse.plugins.keepalive']=(function(...) +local t=require"verse"; +function t.plugins.keepalive(e) +e.keepalive_timeout=e.keepalive_timeout or 300; +t.add_task(e.keepalive_timeout,function() +e.conn:write(" "); +return e.keepalive_timeout; +end); +end +end) +package.preload['verse.plugins.disco']=(function(...) +local a=require"verse"; +local r=require("mime").b64; +local l=require("util.sha1").sha1; +local n="http://jabber.org/protocol/caps"; +local e="http://jabber.org/protocol/disco"; +local o=e.."#info"; +local i=e.."#items"; +function a.plugins.disco(e) +e:add_plugin("presence"); +local s={ +__index=function(a,e) +local t={identities={},features={}}; +if e=="identities"or e=="features"then +return a[false][e] +end +a[e]=t; +return t; +end, +}; +local t={ +__index=function(a,t) +local e={}; +a[t]=e; +return e; +end, +}; +e.disco={ +cache={}, +info=setmetatable({ +[false]={ +identities={ +{category='client',type='pc',name='Verse'}, +}, +features={ +[n]=true, +[o]=true, +[i]=true, +}, +}, +},s); +items=setmetatable({[false]={}},t); +}; +e.caps={} +e.caps.node='http://code.matthewwild.co.uk/verse/' +local function d(t,e) +if t.category0 then +self.connecting_peer_candidates=true; +local function n(e,a) +self.jingle:send_command("transport-info",t.stanza("content",{creator=self.creator,name=self.name}) +:tag("transport",{xmlns=o,sid=self.s5b_sid}) +:tag("candidate-used",{cid=e.cid})); +self.onconnect_callback=i; +self.conn=a; +end +local e=s(self.s5b_sid..self.peer..e.jid,true); +h(n,a,e); +else +e:warn("Actually, I'm going to wait for my peer to tell me its streamhost..."); +self.onconnect_callback=i; +end +end +function a:info_received(a) +e:warn("Info received"); +local n=a:child_with_name("content"); +local i=n:child_with_name("transport"); +if i:get_child("candidate-used")and not self.connecting_peer_candidates then +local a=i:child_with_name("candidate-used"); +if a then +local function r(i,e) +if self.jingle.role=="initiator"then +self.jingle.stream:send_iq(t.iq({to=i.jid,type="set"}) +:tag("query",{xmlns=d,sid=self.s5b_sid}) +:tag("activate"):text(self.jingle.peer),function(i) +if i.attr.type=="result"then +self.jingle:send_command("transport-info",t.stanza("content",n.attr) +:tag("transport",{xmlns=o,sid=self.s5b_sid}) +:tag("activated",{cid=a.attr.cid})); +self.conn=e; +self.onconnect_callback(e); +else +self.jingle.stream:error("Failed to activate bytestream"); +end +end); +end +end +self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]); +local t={ +self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]; +}; +local e=s(self.s5b_sid..e.jid..self.peer,true); +h(r,t,e); +end +elseif i:get_child("activated")then +self.onconnect_callback(self.conn); +end +end +function a:disconnect() +if self.conn then +self.conn:close(); +end +end +function a:handle_accepted(e) +end +local t={__index=a}; +e:hook("jingle/transport/"..o,function(e) +return setmetatable({ +role=e.role, +peer=e.peer, +stream=e.stream, +jingle=e, +},t); +end); +end +end) +package.preload['verse.plugins.proxy65']=(function(...) +local e=require"util.events"; +local r=require"util.uuid"; +local h=require"util.sha1"; +local i={}; +i.__index=i; +local o="http://jabber.org/protocol/bytestreams"; +local n; +function verse.plugins.proxy65(t) +t.proxy65=setmetatable({stream=t},i); +t.proxy65.available_streamhosts={}; +local e=0; +t:hook("disco/service-discovered/proxy",function(a) +if a.type=="bytestreams"then +e=e+1; +t:send_iq(verse.iq({to=a.jid,type="get"}) +:tag("query",{xmlns=o}),function(a) +e=e-1; +if a.attr.type=="result"then +local e=a:get_child("query",o) +:get_child("streamhost").attr; +t.proxy65.available_streamhosts[e.jid]={ +jid=e.jid; +host=e.host; +port=tonumber(e.port); +}; +end +if e==0 then +t:event("proxy65/discovered-proxies",t.proxy65.available_streamhosts); +end +end); +end +end); +t:hook("iq/"..o,function(a) +local e=verse.new(nil,{ +initiator_jid=a.attr.from, +streamhosts={}, +current_host=0; +}); +for t in a.tags[1]:childtags()do +if t.name=="streamhost"then +table.insert(e.streamhosts,t.attr); +end +end +local function o() +if e.current_host<#e.streamhosts then +e.current_host=e.current_host+1; +e:connect( +e.streamhosts[e.current_host].host, +e.streamhosts[e.current_host].port +); +n(t,e,a.tags[1].attr.sid,a.attr.from,t.jid); +return true; +end +e:unhook("disconnected",o); +t:send(verse.error_reply(a,"cancel","item-not-found")); +end +function e:accept() +e:hook("disconnected",o,100); +e:hook("connected",function() +e:unhook("disconnected",o); +local e=verse.reply(a) +:tag("query",a.tags[1].attr) +:tag("streamhost-used",{jid=e.streamhosts[e.current_host].jid}); +t:send(e); +end,100); +o(); +end +function e:refuse() +end +t:event("proxy65/request",e); +end); +end +function i:new(t,s) +local e=verse.new(nil,{ +target_jid=t; +bytestream_sid=r.generate(); +}); +local a=verse.iq{type="set",to=t} +:tag("query",{xmlns=o,mode="tcp",sid=e.bytestream_sid}); +for t,e in ipairs(s or self.proxies)do +a:tag("streamhost",e):up(); +end +self.stream:send_iq(a,function(a) +if a.attr.type=="error"then +local t,a,o=a:get_error(); +e:event("connection-failed",{conn=e,type=t,condition=a,text=o}); +else +local a=a.tags[1]:get_child("streamhost-used"); +if not a then +end +e.streamhost_jid=a.attr.jid; +local i,a; +for o,t in ipairs(s or self.proxies)do +if t.jid==e.streamhost_jid then +i,a=t.host,t.port; +break; +end +end +if not(i and a)then +end +e:connect(i,a); +local function a() +e:unhook("connected",a); +local t=verse.iq{to=e.streamhost_jid,type="set"} +:tag("query",{xmlns=o,sid=e.bytestream_sid}) +:tag("activate"):text(t); +self.stream:send_iq(t,function(t) +if t.attr.type=="result"then +e:event("connected",e); +else +end +end); +return true; +end +e:hook("connected",a,100); +n(self.stream,e,e.bytestream_sid,self.stream.jid,t); +end +end); +return e; +end +function n(i,e,t,o,a) +local n=h.sha1(t..o..a); +local function s() +e:unhook("connected",s); +return true; +end +local function o(t) +e:unhook("incoming-raw",o); +if t:sub(1,2)~="\005\000"then +return e:event("error","connection-failure"); +end +e:event("connected"); +return true; +end +local function i(a) +e:unhook("incoming-raw",i); +if a~="\005\000"then +local t="version-mismatch"; +if a:sub(1,1)=="\005"then +t="authentication-failure"; +end +return e:event("error",t); +end +e:send(string.char(5,1,0,3,#n)..n.."\0\0"); +e:hook("incoming-raw",o,100); +return true; +end +e:hook("connected",s,200); +e:hook("incoming-raw",i,100); +e:send("\005\001\000"); +end +end) +package.preload['verse.plugins.jingle_ibb']=(function(...) +local e=require"verse"; +local i=require"util.encodings".base64; +local h=require"util.uuid".generate; +local n="urn:xmpp:jingle:transports:ibb:1"; +local o="http://jabber.org/protocol/ibb"; +assert(i.encode("This is a test.")=="VGhpcyBpcyBhIHRlc3Qu","Base64 encoding failed"); +assert(i.decode("VGhpcyBpcyBhIHRlc3Qu")=="This is a test.","Base64 decoding failed"); +local t=table.concat +local a={}; +local s={__index=a}; +local function r(t) +local t=setmetatable({stream=t},s) +t=e.eventable(t); +return t; +end +function a:initiate(t,e,a) +self.block=2048; +self.stanza=a or'iq'; +self.peer=t; +self.sid=e or tostring(self):match("%x+$"); +self.iseq=0; +self.oseq=0; +local e=function(e) +return self:feed(e) +end +self.feeder=e; +print("Hooking incomming IQs"); +local t=self.stream; +t:hook("iq/"..o,e) +if a=="message"then +t:hook("message",e) +end +end +function a:open(t) +self.stream:send_iq(e.iq{to=self.peer,type="set"} +:tag("open",{ +xmlns=o, +["block-size"]=self.block, +sid=self.sid, +stanza=self.stanza +}) +,function(e) +if t then +if e.attr.type~="error"then +t(true) +else +t(false,e:get_error()) +end +end +end); +end +function a:send(n) +local a=self.stanza; +local t; +if a=="iq"then +t=e.iq{type="set",to=self.peer} +elseif a=="message"then +t=e.message{to=self.peer} +end +local e=self.oseq; +self.oseq=e+1; +t:tag("data",{xmlns=o,sid=self.sid,seq=e}) +:text(i.encode(n)); +if a=="iq"then +self.stream:send_iq(t,function(e) +self:event(e.attr.type=="result"and"drained"or"error"); +end) +else +stream:send(t) +self:event("drained"); +end +end +function a:feed(t) +if t.attr.from~=self.peer then return end +local a=t[1]; +if a.attr.sid~=self.sid then return end +local n; +if a.name=="open"then +self:event("connected"); +self.stream:send(e.reply(t)) +return true +elseif a.name=="data"then +local o=t:get_child_text("data",o); +local a=tonumber(a.attr.seq); +local n=self.iseq; +if o and a then +if a~=n then +self.stream:send(e.error_reply(t,"cancel","not-acceptable","Wrong sequence. Packet lost?")) +self:close(); +self:event("error"); +return true; +end +self.iseq=a+1; +local a=i.decode(o); +if self.stanza=="iq"then +self.stream:send(e.reply(t)) +end +self:event("incoming-raw",a); +return true; +end +elseif a.name=="close"then +self.stream:send(e.reply(t)) +self:close(); +return true +end +end +function a:close() +self.stream:unhook("iq/"..o,self.feeder) +self:event("disconnected"); +end +function e.plugins.jingle_ibb(a) +a:hook("ready",function() +a:add_disco_feature(n); +end,10); +local t={}; +function t:_setup() +local e=r(self.stream); +e.sid=self.sid or e.sid; +e.stanza=self.stanza or e.stanza; +e.block=self.block or e.block; +e:initiate(self.peer,self.sid,self.stanza); +self.conn=e; +end +function t:generate_initiate() +print("ibb:generate_initiate() as "..self.role); +local t=h(); +self.sid=t; +self.stanza='iq'; +self.block=2048; +local e=e.stanza("transport",{xmlns=n, +sid=self.sid,stanza=self.stanza,["block-size"]=self.block}); +return e; +end +function t:generate_accept(t) +print("ibb:generate_accept() as "..self.role); +local e=t.attr; +self.sid=e.sid or self.sid; +self.stanza=e.stanza or self.stanza; +self.block=e["block-size"]or self.block; +self:_setup(); +return t; +end +function t:connect(t) +if not self.conn then +self:_setup(); +end +local e=self.conn; +print("ibb:connect() as "..self.role); +if self.role=="initiator"then +e:open(function(a,...) +assert(a,table.concat({...},", ")); +t(e); +end); +else +t(e); +end +end +function t:info_received(e) +print("ibb:info_received()"); +end +function t:disconnect() +if self.conn then +self.conn:close() +end +end +function t:handle_accepted(e)end +local t={__index=t}; +a:hook("jingle/transport/"..n,function(e) +return setmetatable({ +role=e.role, +peer=e.peer, +stream=e.stream, +jingle=e, +},t); +end); +end +end) +package.preload['verse.plugins.pubsub']=(function(...) +local h=require"verse"; +local e=require"util.jid".bare; +local n=table.insert; +local o="http://jabber.org/protocol/pubsub"; +local i="http://jabber.org/protocol/pubsub#owner"; +local r="http://jabber.org/protocol/pubsub#event"; +local e="http://jabber.org/protocol/pubsub#errors"; +local e={}; +local s={__index=e}; +function h.plugins.pubsub(e) +e.pubsub=setmetatable({stream=e},s); +e:hook("message",function(t) +local a=t.attr.from; +for t in t:childtags("event",r)do +local t=t:get_child("items"); +if t then +local o=t.attr.node; +for t in t:childtags("item")do +e:event("pubsub/event",{ +from=a; +node=o; +item=t; +}); +end +end +end +end); +return true; +end +function e:create(e,t,a) +return self:service(e):node(t):create(nil,a); +end +function e:subscribe(o,a,t,e) +return self:service(o):node(a):subscribe(t,nil,e); +end +function e:publish(a,i,o,e,t) +return self:service(a):node(i):publish(o,nil,e,t); +end +local a={}; +local t={__index=a}; +function e:service(e) +return setmetatable({stream=self.stream,service=e},t) +end +local function t(r,i,t,a,n,s,e) +local t=h.iq{type=r or"get",to=i} +:tag("pubsub",{xmlns=t or o}) +if a then t:tag(a,{node=n,jid=s});end +if e then t:tag("item",{id=e~=true and e or nil});end +return t; +end +function a:subscriptions(a) +self.stream:send_iq(t(nil,self.service,nil,"subscriptions") +,a and function(t) +if t.attr.type=="result"then +local e=t:get_child("pubsub",o); +local e=e and e:get_child("subscriptions"); +local o={}; +if e then +for t in e:childtags("subscription")do +local e=self:node(t.attr.node) +e.subscription=t; +e.subscribed_jid=t.attr.jid; +n(o,e); +end +end +a(o); +else +a(false,t:get_error()); +end +end or nil); +end +function a:affiliations(a) +self.stream:send_iq(t(nil,self.service,nil,"affiliations") +,a and function(e) +if e.attr.type=="result"then +local e=e:get_child("pubsub",o); +local e=e and e:get_child("affiliations")or{}; +local t={}; +if e then +for a in e:childtags("affiliation")do +local e=self:node(a.attr.node) +e.affiliation=a; +n(t,e); +end +end +a(t); +else +a(false,e:get_error()); +end +end or nil); +end +function a:nodes(a) +self.stream:disco_items(self.service,nil,function(e,...) +if e then +for t=1,#e do +e[t]=self:node(e[t].node); +end +end +a(e,...) +end); +end +local e={}; +local o={__index=e}; +function a:node(e) +return setmetatable({stream=self.stream,service=self.service,node=e},o) +end +function s:__call(t,e) +local t=self:service(t); +return e and t:node(e)or t; +end +function e:hook(a,o) +self._hooks=self._hooks or setmetatable({},{__mode='kv'}); +local function t(e) +if(not e.service or e.from==self.service)and e.node==self.node then +return a(e) +end +end +self._hooks[a]=t; +self.stream:hook("pubsub/event",t,o); +return t; +end +function e:unhook(e) +if e then +local e=self._hooks[e]; +self.stream:unhook("pubsub/event",e); +elseif self._hooks then +for e in pairs(self._hooks)do +self.stream:unhook("pubsub/event",e); +end +end +end +function e:create(e,a) +if e~=nil then +error("Not implemented yet."); +else +self.stream:send_iq(t("set",self.service,nil,"create",self.node),a); +end +end +function e:configure(e,a) +if e~=nil then +error("Not implemented yet."); +end +self.stream:send_iq(t("set",self.service,nil,e==nil and"default"or"configure",self.node),a); +end +function e:publish(o,a,e,i) +if a~=nil then +error("Node configuration is not implemented yet."); +end +self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,o or true) +:add_child(e) +,i); +end +function e:subscribe(e,a,o) +e=e or self.stream.jid; +if a~=nil then +error("Subscription configuration is not implemented yet."); +end +self.stream:send_iq(t("set",self.service,nil,"subscribe",self.node,e,id) +,o); +end +function e:subscription(e) +error("Not implemented yet."); +end +function e:affiliation(e) +error("Not implemented yet."); +end +function e:unsubscribe(e,a) +e=e or self.subscribed_jid or self.stream.jid; +self.stream:send_iq(t("set",self.service,nil,"unsubscribe",self.node,e) +,a); +end +function e:configure_subscription(e,e) +error("Not implemented yet."); +end +function e:items(a,e) +if a then +self.stream:send_iq(t("get",self.service,nil,"items",self.node) +,e); +else +self.stream:disco_items(self.service,self.node,e); +end +end +function e:item(a,e) +self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,a) +,e); +end +function e:retract(e,a) +self.stream:send_iq(t("set",self.service,nil,"retract",self.node,nil,e) +,a); +end +function e:purge(e,a) +assert(not e,"Not implemented yet."); +self.stream:send_iq(t("set",self.service,i,"purge",self.node) +,a); +end +function e:delete(a,e) +assert(not a,"Not implemented yet."); +self.stream:send_iq(t("set",self.service,i,"delete",self.node) +,e); +end +end) +package.preload['verse.plugins.pep']=(function(...) +local e=require"verse"; +local t="http://jabber.org/protocol/pubsub"; +local t=t.."#event"; +function e.plugins.pep(e) +e:add_plugin("disco"); +e:add_plugin("pubsub"); +e.pep={}; +e:hook("pubsub/event",function(t) +return e:event("pep/"..t.node,{from=t.from,item=t.item.tags[1]}); +end); +function e:hook_pep(t,i,o) +local a=e.events._handlers["pep/"..t]; +if not(a)or#a==0 then +e:add_disco_feature(t.."+notify"); +end +e:hook("pep/"..t,i,o); +end +function e:unhook_pep(t,a) +e:unhook("pep/"..t,a); +local a=e.events._handlers["pep/"..t]; +if not(a)or#a==0 then +e:remove_disco_feature(t.."+notify"); +end +end +function e:publish_pep(t,a) +return e.pubsub:service(nil):node(a or t.attr.xmlns):publish(nil,nil,t) +end +end +end) +package.preload['verse.plugins.adhoc']=(function(...) +local o=require"verse"; +local n=require"lib.adhoc"; +local t="http://jabber.org/protocol/commands"; +local s="jabber:x:data"; +local a={}; +a.__index=a; +local i={}; +function o.plugins.adhoc(e) +e:add_plugin("disco"); +e:add_disco_feature(t); +function e:query_commands(a,o) +e:disco_items(a,t,function(a) +e:debug("adhoc list returned") +local t={}; +for o,a in ipairs(a)do +t[a.node]=a.name; +end +e:debug("adhoc calling callback") +return o(t); +end); +end +function e:execute_command(t,o,i) +local e=setmetatable({ +stream=e,jid=t, +command=o,callback=i +},a); +return e:execute(); +end +local function r(t,e) +if not(e)or e=="user"then return true;end +if type(e)=="function"then +return e(t); +end +end +function e:add_adhoc_command(o,a,h,s) +i[a]=n.new(o,a,h,s); +e:add_disco_item({jid=e.jid,node=a,name=o},t); +return i[a]; +end +local function s(a) +local t=a.tags[1]; +local t=t.attr.node; +local t=i[t]; +if not t then return;end +if not r(a.attr.from,t.permission)then +e:send(o.error_reply(a,"auth","forbidden","You don't have permission to execute this command"):up() +:add_child(t:cmdtag("canceled") +:tag("note",{type="error"}):text("You don't have permission to execute this command"))); +return true +end +return n.handle_cmd(t,{send=function(t)return e:send(t)end},a); +end +e:hook("iq/"..t,function(e) +local a=e.attr.type; +local t=e.tags[1].name; +if a=="set"and t=="command"then +return s(e); +end +end); +end +function a:_process_response(e) +if e.attr.type=="error"then +self.status="canceled"; +self.callback(self,{}); +return; +end +local e=e:get_child("command",t); +self.status=e.attr.status; +self.sessionid=e.attr.sessionid; +self.form=e:get_child("x",s); +self.note=e:get_child("note"); +self.callback(self); +end +function a:execute() +local e=o.iq({to=self.jid,type="set"}) +:tag("command",{xmlns=t,node=self.command}); +self.stream:send_iq(e,function(e) +self:_process_response(e); +end); +end +function a:next(e) +local t=o.iq({to=self.jid,type="set"}) +:tag("command",{ +xmlns=t, +node=self.command, +sessionid=self.sessionid +}); +if e then t:add_child(e);end +self.stream:send_iq(t,function(e) +self:_process_response(e); +end); +end +end) +package.preload['verse.plugins.presence']=(function(...) +local a=require"verse"; +function a.plugins.presence(e) +e.last_presence=nil; +e:hook("presence-out",function(t) +if not t.attr.to then +e.last_presence=t; +end +end,1); +function e:resend_presence() +if last_presence then +e:send(last_presence); +end +end +function e:set_status(t) +local a=a.presence(); +if type(t)=="table"then +if t.show then +a:tag("show"):text(t.show):up(); +end +if t.prio then +a:tag("priority"):text(tostring(t.prio)):up(); +end +if t.msg then +a:tag("status"):text(t.msg):up(); +end +end +e:send(a); +end +end +end) +package.preload['verse.plugins.private']=(function(...) +local a=require"verse"; +local t="jabber:iq:private"; +function a.plugins.private(i) +function i:private_set(o,i,e,n) +local t=a.iq({type="set"}) +:tag("query",{xmlns=t}); +if e then +if e.name==o and e.attr and e.attr.xmlns==i then +t:add_child(e); +else +t:tag(o,{xmlns=i}) +:add_child(e); +end +end +self:send_iq(t,n); +end +function i:private_get(e,o,i) +self:send_iq(a.iq({type="get"}) +:tag("query",{xmlns=t}) +:tag(e,{xmlns=o}), +function(a) +if a.attr.type=="result"then +local t=a:get_child("query",t); +local e=t:get_child(e,o); +i(e); +end +end); +end +end +end) +package.preload['verse.plugins.roster']=(function(...) +local o=require"verse"; +local d=require"util.jid".bare; +local a="jabber:iq:roster"; +local n="urn:xmpp:features:rosterver"; +local i=table.insert; +function o.plugins.roster(t) +local s=false; +local e={ +items={}; +ver=""; +}; +t.roster=e; +t:hook("stream-features",function(e) +if e:get_child("ver",n)then +s=true; +end +end); +local function h(t) +local e=o.stanza("item",{xmlns=a}); +for a,t in pairs(t)do +if a~="groups"then +e.attr[a]=t; +else +for a=1,#t do +e:tag("group"):text(t[a]):up(); +end +end +end +return e; +end +local function r(t) +local e={}; +local a={}; +e.groups=a; +local o=t.attr.jid; +for t,a in pairs(t.attr)do +if t~="xmlns"then +e[t]=a +end +end +for e in t:childtags("group")do +i(a,e:get_text()) +end +return e; +end +function e:load(t) +e.ver,e.items=t.ver,t.items; +end +function e:dump() +return{ +ver=e.ver, +items=e.items, +}; +end +function e:add_contact(s,i,n,e) +local i={jid=s,name=i,groups=n}; +local a=o.iq({type="set"}) +:tag("query",{xmlns=a}) +:add_child(h(i)); +t:send_iq(a,function(t) +if not e then return end +if t.attr.type=="result"then +e(true); +else +local a,o,t=t:get_error(); +e(nil,{a,o,t}); +end +end); +end +function e:delete_contact(i,n) +i=(type(i)=="table"and i.jid)or i; +local s={jid=i,subscription="remove"} +if not e.items[i]then return false,"item-not-found";end +t:send_iq(o.iq({type="set"}) +:tag("query",{xmlns=a}) +:add_child(h(s)), +function(e) +if not n then return end +if e.attr.type=="result"then +n(true); +else +local e,t,a=e:get_error(); +n(nil,{e,t,a}); +end +end); +end +local function h(t) +local t=r(t); +e.items[t.jid]=t; +end +local function r(t) +local a=e.items[t]; +e.items[t]=nil; +return a; +end +function e:fetch(i) +t:send_iq(o.iq({type="get"}):tag("query",{xmlns=a,ver=s and e.ver or nil}), +function(t) +if t.attr.type=="result"then +local t=t:get_child("query",a); +if t then +e.items={}; +for t in t:childtags("item")do +h(t) +end +e.ver=t.attr.ver or""; +end +i(e); +else +local e,t,a=stanza:get_error(); +i(nil,{e,t,a}); +end +end); +end +t:hook("iq/"..a,function(i) +local s,n=i.attr.type,i.attr.from; +if s=="set"and(not n or n==d(t.jid))then +local s=i:get_child("query",a); +local a=s and s:get_child("item"); +if a then +local n,o; +local i=a.attr.jid; +if a.attr.subscription=="remove"then +n="removed" +o=r(i); +else +n=e.items[i]and"changed"or"added"; +h(a) +o=e.items[i]; +end +e.ver=s.attr.ver; +if o then +t:event("roster/item-"..n,o); +end +end +t:send(o.reply(i)) +return true; +end +end); +end +end) +package.preload['verse.plugins.register']=(function(...) +local t=require"verse"; +local o="jabber:iq:register"; +function t.plugins.register(e) +local function a(i) +if i:get_child("register","http://jabber.org/features/iq-register")then +local t=t.iq({to=e.host_,type="set"}) +:tag("query",{xmlns=o}) +:tag("username"):text(e.username):up() +:tag("password"):text(e.password):up(); +if e.register_email then +t:tag("email"):text(e.register_email):up(); +end +e:send_iq(t,function(t) +if t.attr.type=="result"then +e:event("registration-success"); +else +local o,t,a=t:get_error(); +e:debug("Registration failed: %s",t); +e:event("registration-failure",{type=o,condition=t,text=a}); +end +end); +else +e:debug("In-band registration not offered by server"); +e:event("registration-failure",{condition="service-unavailable"}); +end +e:unhook("stream-features",a); +return true; +end +e:hook("stream-features",a,310); +end +end) +package.preload['verse.plugins.groupchat']=(function(...) +local i=require"verse"; +local e=require"events"; +local n=require"util.jid"; +local a={}; +a.__index=a; +local h="urn:xmpp:delay"; +local s="http://jabber.org/protocol/muc"; +function i.plugins.groupchat(o) +o:add_plugin("presence") +o.rooms={}; +o:hook("stanza",function(e) +local a=n.bare(e.attr.from); +if not a then return end +local t=o.rooms[a] +if not t and e.attr.to and a then +t=o.rooms[e.attr.to.." "..a] +end +if t and t.opts.source and e.attr.to~=t.opts.source then return end +if t then +local o=select(3,n.split(e.attr.from)); +local n=e:get_child_text("body"); +local i=e:get_child("delay",h); +local a={ +room_jid=a; +room=t; +sender=t.occupants[o]; +nick=o; +body=n; +stanza=e; +delay=(i and i.attr.stamp); +}; +local t=t:event(e.name,a); +return t or(e.name=="message")or nil; +end +end,500); +function o:join_room(n,h,t) +if not h then +return false,"no nickname supplied" +end +t=t or{}; +local e=setmetatable(i.eventable{ +stream=o,jid=n,nick=h, +subject=nil, +occupants={}, +opts=t, +},a); +if t.source then +self.rooms[t.source.." "..n]=e; +else +self.rooms[n]=e; +end +local a=e.occupants; +e:hook("presence",function(o) +local t=o.nick or h; +if not a[t]and o.stanza.attr.type~="unavailable"then +a[t]={ +nick=t; +jid=o.stanza.attr.from; +presence=o.stanza; +}; +local o=o.stanza:get_child("x",s.."#user"); +if o then +local e=o:get_child("item"); +if e and e.attr then +a[t].real_jid=e.attr.jid; +a[t].affiliation=e.attr.affiliation; +a[t].role=e.attr.role; +end +end +if t==e.nick then +e.stream:event("groupchat/joined",e); +else +e:event("occupant-joined",a[t]); +end +elseif a[t]and o.stanza.attr.type=="unavailable"then +if t==e.nick then +e.stream:event("groupchat/left",e); +if e.opts.source then +self.rooms[e.opts.source.." "..n]=nil; +else +self.rooms[n]=nil; +end +else +a[t].presence=o.stanza; +e:event("occupant-left",a[t]); +a[t]=nil; +end +end +end); +e:hook("message",function(a) +local t=a.stanza:get_child_text("subject"); +if not t then return end +t=#t>0 and t or nil; +if t~=e.subject then +local o=e.subject; +e.subject=t; +return e:event("subject-changed",{from=o,to=t,by=a.sender,event=a}); +end +end,2e3); +local t=i.presence():tag("x",{xmlns=s}):reset(); +self:event("pre-groupchat/joining",t); +e:send(t) +self:event("groupchat/joining",e); +return e; +end +o:hook("presence-out",function(e) +if not e.attr.to then +for a,t in pairs(o.rooms)do +t:send(e); +end +e.attr.to=nil; +end +end); +end +function a:send(e) +if e.name=="message"and not e.attr.type then +e.attr.type="groupchat"; +end +if e.name=="presence"then +e.attr.to=self.jid.."/"..self.nick; +end +if e.attr.type=="groupchat"or not e.attr.to then +e.attr.to=self.jid; +end +if self.opts.source then +e.attr.from=self.opts.source +end +self.stream:send(e); +end +function a:send_message(e) +self:send(i.message():tag("body"):text(e)); +end +function a:set_subject(e) +self:send(i.message():tag("subject"):text(e)); +end +function a:leave(e) +self.stream:event("groupchat/leaving",self); +local t=i.presence({type="unavailable"}); +if e then +t:tag("status"):text(e); +end +self:send(t); +end +function a:admin_set(t,e,a,o) +self:send(i.iq({type="set"}) +:query(s.."#admin") +:tag("item",{nick=t,[e]=a}) +:tag("reason"):text(o or"")); +end +function a:set_role(a,t,e) +self:admin_set(a,"role",t,e); +end +function a:set_affiliation(a,e,t) +self:admin_set(a,"affiliation",e,t); +end +function a:kick(e,t) +self:set_role(e,"none",t); +end +function a:ban(e,t) +self:set_affiliation(e,"outcast",t); +end +end) +package.preload['verse.plugins.vcard']=(function(...) +local i=require"verse"; +local o=require"util.vcard"; +local n="vcard-temp"; +function i.plugins.vcard(a) +function a:get_vcard(t,e) +a:send_iq(i.iq({to=t,type="get"}) +:tag("vCard",{xmlns=n}),e and function(t) +local a,a; +vCard=t:get_child("vCard",n); +if t.attr.type=="result"and vCard then +vCard=o.from_xep54(vCard) +e(vCard) +else +e(false) +end +end or nil); +end +function a:set_vcard(e,n) +local t; +if type(e)=="table"and e.name then +t=e; +elseif type(e)=="string"then +t=o.to_xep54(o.from_text(e)[1]); +elseif type(e)=="table"then +t=o.to_xep54(e); +error("Converting a table to vCard not implemented") +end +if not t then return false end +a:debug("setting vcard to %s",tostring(t)); +a:send_iq(i.iq({type="set"}) +:add_child(t),n); +end +end +end) +package.preload['verse.plugins.vcard_update']=(function(...) +local n=require"verse"; +local e,i="vcard-temp","vcard-temp:x:update"; +local e,t=pcall(function()return require("util.hashes").sha1;end); +if not e then +e,t=pcall(function()return require("util.sha1").sha1;end); +if not e then +error("Could not find a sha1()") +end +end +local s=t; +local e,t=pcall(function() +local e=require("util.encodings").base64.decode; +assert(e("SGVsbG8=")=="Hello") +return e; +end); +if not e then +e,t=pcall(function()return require("mime").unb64;end); +if not e then +error("Could not find a base64 decoder") +end +end +local h=t; +function n.plugins.vcard_update(e) +e:add_plugin("vcard"); +e:add_plugin("presence"); +local t; +function update_vcard_photo(o) +local a; +for e=1,#o do +if o[e].name=="PHOTO"then +a=o[e][1]; +break +end +end +if a then +local a=s(h(a),true); +t=n.stanza("x",{xmlns=i}) +:tag("photo"):text(a); +e:resend_presence() +else +t=nil; +end +end +local a=e.set_vcard; +local a; +e:hook("ready",function(t) +if a then return;end +a=true; +e:get_vcard(nil,function(t) +if t then +update_vcard_photo(t) +end +e:event("ready"); +end); +return true; +end,3); +e:hook("presence-out",function(e) +if t and not e:get_child("x",i)then +e:add_child(t); +end +end,10); +end +end) +package.preload['verse.plugins.carbons']=(function(...) +local o=require"verse"; +local a="urn:xmpp:carbons:2"; +local h="urn:xmpp:forward:0"; +local n=os.time; +local s=require"util.datetime".parse; +local r=require"util.jid".bare; +function o.plugins.carbons(e) +local t={}; +t.enabled=false; +e.carbons=t; +function t:enable(i) +e:send_iq(o.iq{type="set"} +:tag("enable",{xmlns=a}) +,function(e) +local e=e.attr.type=="result"; +if e then +t.enabled=true; +end +if i then +i(e); +end +end or nil); +end +function t:disable(i) +e:send_iq(o.iq{type="set"} +:tag("disable",{xmlns=a}) +,function(e) +local e=e.attr.type=="result"; +if e then +t.enabled=false; +end +if i then +i(e); +end +end or nil); +end +local i; +e:hook("bind-success",function() +i=r(e.jid); +end); +e:hook("message",function(o) +local t=o:get_child(nil,a); +if o.attr.from==i and t then +local o=t.name; +local t=t:get_child("forwarded",h); +local a=t and t:get_child("message","jabber:client"); +local t=t:get_child("delay","urn:xmpp:delay"); +local t=t and t.attr.stamp; +t=t and s(t); +if a then +return e:event("carbon",{ +dir=o, +stanza=a, +timestamp=t or n(), +}); +end +end +end,1); +end +end) +package.preload['verse.plugins.archive']=(function(...) +local t=require"verse"; +local e=require"util.stanza"; +local a="urn:xmpp:mam:0" +local s="urn:xmpp:forward:0"; +local l="urn:xmpp:delay"; +local i=require"util.uuid".generate; +local m=require"util.datetime".parse; +local h=require"util.datetime".datetime; +local o=require"util.dataforms".new; +local r=require"util.rsm"; +local c={}; +local u=o{ +{name="FORM_TYPE";type="hidden";value=a;}; +{name="with";type="jid-single";}; +{name="start";type="text-single"}; +{name="end";type="text-single";}; +}; +function t.plugins.archive(n) +function n:query_archive(o,t,d) +local i=i(); +local o=e.iq{type="set",to=o} +:tag("query",{xmlns=a,queryid=i}); +local e,n=tonumber(t["start"]),tonumber(t["end"]); +t["start"]=e and h(e); +t["end"]=n and h(n); +o:add_child(u:form(t,"submit")); +o:add_child(r.generate(t)); +local t={}; +local function n(o) +local e=o:get_child("fin",a) +if e and e.attr.queryid==i then +local e=r.get(e); +for a,e in pairs(e or c)do t[a]=e;end +self:unhook("message",n); +d(t); +return true +end +local e=o:get_child("result",a); +if e and e.attr.queryid==i then +local a=e:get_child("forwarded",s); +a=a or o:get_child("forwarded",s); +local o=e.attr.id; +local e=a:get_child("delay",l); +local e=e and m(e.attr.stamp)or nil; +local a=a:get_child("message","jabber:client") +t[#t+1]={id=o,stamp=e,message=a}; +return true +end +end +self:hook("message",n,1); +self:send_iq(o,function(e) +if e.attr.type=="error"then +self:warn(table.concat({e:get_error()}," ")) +self:unhook("message",n); +d(false,e:get_error()) +end +return true +end); +end +local i={ +always=true,[true]="always", +never=false,[false]="never", +roster="roster", +} +local function h(t) +local e={}; +local a=t.attr.default; +if a then +e[false]=i[a]; +end +local a=t:get_child("always"); +if a then +for t in a:childtags("jid")do +local t=t:get_text(); +e[t]=true; +end +end +local t=t:get_child("never"); +if t then +for t in t:childtags("jid")do +local t=t:get_text(); +e[t]=false; +end +end +return e; +end +local function s(o) +local t +t,o[false]=o[false],nil; +if t~=nil then +t=i[t]; +end +local i=e.stanza("prefs",{xmlns=a,default=t}) +local t=e.stanza("always"); +local e=e.stanza("never"); +for a,o in pairs(o)do +(o and t or e):tag("jid"):text(a):up(); +end +return i:add_child(t):add_child(e); +end +function n:archive_prefs_get(t) +self:send_iq(e.iq{type="get"}:tag("prefs",{xmlns=a}), +function(e) +if e and e.attr.type=="result"and e.tags[1]then +local a=h(e.tags[1]); +t(a,e); +else +t(nil,e); +end +end); +end +function n:archive_prefs_set(t,a) +self:send_iq(e.iq{type="set"}:add_child(s(t)),a); +end +end +end) +package.preload['net.httpclient_listener']=(function(...) +local n=require"util.logger".init("httpclient_listener"); +local i,h=table.concat,table.insert; +local s=require"net.connlisteners".register; +local t={}; +local e={}; +local o={default_port=80,default_mode="*a"}; +function o.onconnect(a) +local e=t[a]; +local t={e.method or"GET"," ",e.path," HTTP/1.1\r\n"}; +if e.query then +h(t,4,"?"..e.query); +end +a:write(i(t)); +local t={[2]=": ",[4]="\r\n"}; +for o,e in pairs(e.headers)do +t[1],t[3]=o,e; +a:write(i(t)); +end +a:write("\r\n"); +if e.body then +a:write(e.body); +end +end +function o.onincoming(o,a) +local e=t[o]; +if not e then +n("warn","Received response from connection %s with no request attached!",tostring(o)); +return; +end +if a and e.reader then +e:reader(a); +end +end +function o.ondisconnect(a,e) +local e=t[a]; +if e and e.conn then +e:reader(nil); +end +t[a]=nil; +end +function o.register_request(a,e) +n("debug","Attaching request %s to connection %s",tostring(e.id or e),tostring(a)); +t[a]=e; +end +s("httpclient",o); +end) +package.preload['net.connlisteners']=(function(...) +local h=(CFG_SOURCEDIR or".").."/net/"; +local u=require"net.server"; +local o=require"util.logger".init("connlisteners"); +local i=tostring; +local d=type +local r=ipairs +local n,c,s= +dofile,xpcall,error +local l=debug.traceback; +module"connlisteners" +local e={}; +function register(t,a) +if e[t]and e[t]~=a then +o("debug","Listener %s is already registered, not registering any more",t); +return false; +end +e[t]=a; +o("debug","Registered connection listener %s",t); +return true; +end +function deregister(t) +e[t]=nil; +end +function get(t) +local a=e[t]; +if not a then +local s,n=c(function()n(h..t:gsub("[^%w%-]","_").."_listener.lua")end,l); +if not s then +o("error","Error while loading listener '%s': %s",i(t),i(n)); +return nil,n; +end +a=e[t]; +end +return a; +end +function start(i,e) +local a,t=get(i); +if not a then +s("No such connection module: "..i..(t and(" ("..t..")")or""),0); +end +local o=(e and e.interface)or a.default_interface or"*"; +if d(o)=="string"then o={o};end +local h=(e and e.port)or a.default_port or s("Can't start listener "..i.." because no port was specified, and it has no default port",0); +local s=(e and e.mode)or a.default_mode or 1; +local n=(e and e.ssl)or nil; +local i=e and e.type=="ssl"; +if i and not n then +return nil,"no ssl context"; +end +ok,t=true,{}; +for e,o in r(o)do +local e +e,t[o]=u.addserver(o,h,a,s,i and n or nil); +ok=ok and e; +end +return ok,t; +end +return _M; +end) +package.preload['util.httpstream']=(function(...) +local t=coroutine; +local n=tonumber; +local h=t.create(function()end); +t.resume(h); +module("httpstream") +local function c(l,o,d) +local e=t.yield(); +local function i() +local a=e:find("\r\n",nil,true); +while not a do +e=e..t.yield(); +a=e:find("\r\n",nil,true); +end +local t=e:sub(1,a-1); +e=e:sub(a+2); +return t; +end +local function h(a) +while#e=100 and a<200)); +local o; +if d then +local a=n(s["content-length"]); +if s["transfer-encoding"]=="chunked"then +o=""; +while true do +local e=i():match("^%x+"); +if not e then t.yield("invalid-chunk-size");end +e=n(e,16) +if e==0 then break;end +o=o..h(e); +if i()~=""then t.yield("invalid-chunk-ending");end +end +local e=r(); +elseif a then +o=h(a); +else +repeat +local t=t.yield(); +e=e..t; +until t==""; +o,e=e,""; +end +end +l({ +code=a; +httpversion=u; +headers=s; +body=o; +responseversion=u; +responseheaders=s; +}); +end +else t.yield("unknown-parser-type");end +end +function new(n,i,o,a) +local e=t.create(c); +t.resume(e,n,o,a) +return{ +feed=function(n,a) +if not a then +if o=="client"then t.resume(e,"");end +e=h; +return i(); +end +local a,t=t.resume(e,a); +if t then +e=h; +return i(t); +end +end; +}; +end +return _M; +end) +package.preload['net.http']=(function(...) +local c=require"socket" +local u=require"mime" +local h=require"socket.url" +local f=require"util.httpstream".new; +local m=require"net.server" +local e=require"net.connlisteners".get; +local n=e("httpclient")or error("No httpclient listener!"); +local o,s=table.insert,table.concat; +local i,p=pairs,ipairs; +local d,l,w,y,v,a,t= +tonumber,tostring,xpcall,select,debug.traceback,string.char,string.format; +local r=require"util.logger".init("http"); +module"http" +function urlencode(e)return e and(e:gsub("%W",function(e)return t("%%%02x",e:byte());end));end +function urldecode(e)return e and(e:gsub("%%(%x%x)",function(e)return a(d(e,16));end));end +local function e(e) +return e and(e:gsub("%W",function(e) +if e~=" "then +return t("%%%02x",e:byte()); +else +return"+"; +end +end)); +end +function formencode(t) +local a={}; +if t[1]then +for i,t in p(t)do +o(a,e(t.name).."="..e(t.value)); +end +else +for t,i in i(t)do +o(a,e(t).."="..e(i)); +end +end +return s(a,"&"); +end +function formdecode(e) +if not e:match("=")then return urldecode(e);end +local a={}; +for t,e in e:gmatch("([^=&]*)=([^&]*)")do +t,e=t:gsub("%+","%%20"),e:gsub("%+","%%20"); +t,e=urldecode(t),urldecode(e); +o(a,{name=t,value=e}); +a[t]=e; +end +return a; +end +local function p(e,a,t) +if not e.parser then +if not a then return;end +local function o(t) +if e.callback then +for a,t in i(t)do e[a]=t;end +e.callback(t.body,t.code,e,t); +e.callback=nil; +end +destroy_request(e); +end +local function a(t) +if e.callback then +e.callback(t or"connection-closed",0,e); +e.callback=nil; +end +destroy_request(e); +end +local function t() +return e; +end +e.parser=f(o,a,"client",t); +end +e.parser:feed(a); +end +local function f(e)r("error","Traceback[http]: %s: %s",l(e),v());end +function request(e,t,s) +local e=h.parse(e); +if not(e and e.host)then +s(nil,0,e); +return nil,"invalid-url"; +end +if not e.path then +e.path="/"; +end +local h,a,o; +a={ +["Host"]=e.host; +["User-Agent"]="Prosody XMPP Server"; +}; +if e.userinfo then +a["Authorization"]="Basic "..u.b64(e.userinfo); +end +if t then +e.onlystatus=t.onlystatus; +o=t.body; +if o then +h="POST"; +a["Content-Length"]=l(#o); +a["Content-Type"]="application/x-www-form-urlencoded"; +end +if t.method then h=t.method;end +if t.headers then +for t,e in i(t.headers)do +a[t]=e; +end +end +end +e.method,e.headers,e.body=h,a,o; +local o=e.scheme=="https"; +local i=d(e.port)or(o and 443 or 80); +local t=c.tcp(); +t:settimeout(10); +local h,a=t:connect(e.host,i); +if not h and a~="timeout"then +s(nil,0,e); +return nil,a; +end +e.handler,e.conn=m.wrapclient(t,e.host,i,n,"*a",o and{mode="client",protocol="sslv23"}); +e.write=function(...)return e.handler:write(...);end +e.callback=function(o,t,a,i)r("debug","Calling callback, status %s",t or"---");return y(2,w(function()return s(o,t,a,i)end,f));end +e.reader=p; +e.state="status"; +n.register_request(e.handler,e); +return e; +end +function destroy_request(e) +if e.conn then +e.conn=nil; +e.handler:close() +n.ondisconnect(e.handler,"closed"); +end +end +_M.urlencode=urlencode; +return _M; +end) +package.preload['verse.bosh']=(function(...) +local n=require"util.xmppstream".new; +local r=require"util.stanza"; +require"net.httpclient_listener"; +local i=require"net.http"; +local e=setmetatable({},{__index=verse.stream_mt}); +e.__index=e; +local h="http://etherx.jabber.org/streams"; +local s="http://jabber.org/protocol/httpbind"; +local o=5; +function verse.new_bosh(a,t) +local t={ +bosh_conn_pool={}; +bosh_waiting_requests={}; +bosh_rid=math.random(1,999999); +bosh_outgoing_buffer={}; +bosh_url=t; +conn={}; +}; +function t:reopen() +self.bosh_need_restart=true; +self:flush(); +end +local t=verse.new(a,t); +return setmetatable(t,e); +end +function e:connect() +self:_send_session_request(); +end +function e:send(e) +self:debug("Putting into BOSH send buffer: %s",tostring(e)); +self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1]=r.clone(e); +self:flush(); +end +function e:flush() +if self.connected +and#self.bosh_waiting_requests0 +or self.bosh_need_restart)then +self:debug("Flushing..."); +local t=self:_make_body(); +local e=self.bosh_outgoing_buffer; +for o,a in ipairs(e)do +t:add_child(a); +e[o]=nil; +end +self:_make_request(t); +else +self:debug("Decided not to flush."); +end +end +function e:_make_request(a) +local e,t=i.request(self.bosh_url,{body=tostring(a)},function(i,e,t) +if e~=0 then +self.inactive_since=nil; +return self:_handle_response(i,e,t); +end +local e=os.time(); +if not self.inactive_since then +self.inactive_since=e; +elseif e-self.inactive_since>self.bosh_max_inactivity then +return self:_disconnected(); +else +self:debug("%d seconds left to reconnect, retrying in %d seconds...", +self.bosh_max_inactivity-(e-self.inactive_since),o); +end +timer.add_task(o,function() +self:debug("Retrying request..."); +for a,e in ipairs(self.bosh_waiting_requests)do +if e==t then +table.remove(self.bosh_waiting_requests,a); +break; +end +end +self:_make_request(a); +end); +end); +if e then +table.insert(self.bosh_waiting_requests,e); +else +self:warn("Request failed instantly: %s",t); +end +end +function e:_disconnected() +self.connected=nil; +self:event("disconnected"); +end +function e:_send_session_request() +local e=self:_make_body(); +e.attr.hold="1"; +e.attr.wait="60"; +e.attr["xml:lang"]="en"; +e.attr.ver="1.6"; +e.attr.from=self.jid; +e.attr.to=self.host; +e.attr.secure='true'; +i.request(self.bosh_url,{body=tostring(e)},function(e,t) +if t==0 then +return self:_disconnected(); +end +local e=self:_parse_response(e) +if not e then +self:warn("Invalid session creation response"); +self:_disconnected(); +return; +end +self.bosh_sid=e.attr.sid; +self.bosh_wait=tonumber(e.attr.wait); +self.bosh_hold=tonumber(e.attr.hold); +self.bosh_max_inactivity=tonumber(e.attr.inactivity); +self.bosh_max_requests=tonumber(e.attr.requests)or self.bosh_hold; +self.connected=true; +self:event("connected"); +self:_handle_response_payload(e); +end); +end +function e:_handle_response(o,t,e) +if self.bosh_waiting_requests[1]~=e then +self:warn("Server replied to request that wasn't the oldest"); +for t,a in ipairs(self.bosh_waiting_requests)do +if a==e then +self.bosh_waiting_requests[t]=nil; +break; +end +end +else +table.remove(self.bosh_waiting_requests,1); +end +local e=self:_parse_response(o); +if e then +self:_handle_response_payload(e); +end +self:flush(); +end +function e:_handle_response_payload(t) +local e=t.tags; +for t=1,#e do +local e=e[t]; +if e.attr.xmlns==h then +self:event("stream-"..e.name,e); +elseif e.attr.xmlns then +self:event("stream/"..e.attr.xmlns,e); +else +self:event("stanza",e); +end +end +if t.attr.type=="terminate"then +self:_disconnected({reason=t.attr.condition}); +end +end +local a={ +stream_ns="http://jabber.org/protocol/httpbind",stream_tag="body", +default_ns="jabber:client", +streamopened=function(e,t)e.notopen=nil;e.payload=verse.stanza("body",t);return true;end; +handlestanza=function(t,e)t.payload:add_child(e);end; +}; +function e:_parse_response(e) +self:debug("Parsing response: %s",e); +if e==nil then +self:debug("%s",debug.traceback()); +self:_disconnected(); +return; +end +local t={notopen=true,stream=self}; +local a=n(t,a); +a:feed(e); +return t.payload; +end +function e:_make_body() +self.bosh_rid=self.bosh_rid+1; +local e=verse.stanza("body",{ +xmlns=s; +content="text/xml; charset=utf-8"; +sid=self.bosh_sid; +rid=self.bosh_rid; +}); +if self.bosh_need_restart then +self.bosh_need_restart=nil; +e.attr.restart='true'; +end +return e; +end +end) +package.preload['verse.client']=(function(...) +local t=require"verse"; +local i=t.stream_mt; +local s=require"util.jid".split; +local r=require"net.adns"; +local e=require"lxp"; +local a=require"util.stanza"; +t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply= +a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply; +local h=require"util.xmppstream".new; +local n="http://etherx.jabber.org/streams"; +local function d(t,e) +return t.prioritye.weight); +end +local o={ +stream_ns=n, +stream_tag="stream", +default_ns="jabber:client"}; +function o.streamopened(e,t) +e.stream_id=t.id; +if not e:event("opened",t)then +e.notopen=nil; +end +return true; +end +function o.streamclosed(e) +e.notopen=true; +if not e.closed then +e:send(""); +e.closed=true; +end +e:event("closed"); +return e:close("stream closed") +end +function o.handlestanza(t,e) +if e.attr.xmlns==n then +return t:event("stream-"..e.name,e); +elseif e.attr.xmlns then +return t:event("stream/"..e.attr.xmlns,e); +end +return t:event("stanza",e); +end +function o.error(a,t,e) +if a:event(t,e)==nil then +if e then +local t=e:get_child(nil,"urn:ietf:params:xml:ns:xmpp-streams"); +local e=e:get_child_text("text","urn:ietf:params:xml:ns:xmpp-streams"); +error(t.name..(e and": "..e or"")); +else +error(e and e.name or t or"unknown-error"); +end +end +end +function i:reset() +if self.stream then +self.stream:reset(); +else +self.stream=h(self,o); +end +self.notopen=true; +return true; +end +function i:connect_client(e,a) +self.jid,self.password=e,a; +self.username,self.host,self.resource=s(e); +self:add_plugin("tls"); +self:add_plugin("sasl"); +self:add_plugin("bind"); +self:add_plugin("session"); +function self.data(t,e) +local a,t=self.stream:feed(e); +if a then return;end +self:debug("debug","Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," ")); +self:close("xml-not-well-formed"); +end +self:hook("connected",function()self:reopen();end); +self:hook("incoming-raw",function(e)return self.data(self.conn,e);end); +self.curr_id=0; +self.tracked_iqs={}; +self:hook("stanza",function(e) +local t,a=e.attr.id,e.attr.type; +if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then +self.tracked_iqs[t](e); +self.tracked_iqs[t]=nil; +return true; +end +end); +self:hook("stanza",function(e) +local a; +if e.attr.xmlns==nil or e.attr.xmlns=="jabber:client"then +if e.name=="iq"and(e.attr.type=="get"or e.attr.type=="set")then +local o=e.tags[1]and e.tags[1].attr.xmlns; +if o then +a=self:event("iq/"..o,e); +if not a then +a=self:event("iq",e); +end +end +if a==nil then +self:send(t.error_reply(e,"cancel","service-unavailable")); +return true; +end +else +a=self:event(e.name,e); +end +end +return a; +end,-1); +self:hook("outgoing",function(e) +if e.name then +self:event("stanza-out",e); +end +end); +self:hook("stanza-out",function(e) +if not e.attr.xmlns then +self:event(e.name.."-out",e); +end +end); +local function e() +self:event("ready"); +end +self:hook("session-success",e,-1) +self:hook("bind-success",e,-1); +local t=self.close; +function self:close(e) +self.close=t; +if not self.closed then +self:send(""); +self.closed=true; +else +return self:close(e); +end +end +local function a() +self:connect(self.connect_host or self.host,self.connect_port or 5222); +end +if not(self.connect_host or self.connect_port)then +r.lookup(function(t) +if t then +local e={}; +self.srv_hosts=e; +for a,t in ipairs(t)do +table.insert(e,t.srv); +end +table.sort(e,d); +local t=e[1]; +self.srv_choice=1; +if t then +self.connect_host,self.connect_port=t.target,t.port; +self:debug("Best record found, will connect to %s:%d",self.connect_host or self.host,self.connect_port or 5222); +end +self:hook("disconnected",function() +if self.srv_hosts and self.srv_choice<#self.srv_hosts then +self.srv_choice=self.srv_choice+1; +local e=e[self.srv_choice]; +self.connect_host,self.connect_port=e.target,e.port; +a(); +return true; +end +end,1e3); +self:hook("connected",function() +self.srv_hosts=nil; +end,1e3); +end +a(); +end,"_xmpp-client._tcp."..(self.host)..".","SRV"); +else +a(); +end +end +function i:reopen() +self:reset(); +self:send(a.stanza("stream:stream",{to=self.host,["xmlns:stream"]='http://etherx.jabber.org/streams', +xmlns="jabber:client",version="1.0"}):top_tag()); +end +function i:send_iq(e,a) +local t=self:new_id(); +self.tracked_iqs[t]=a; +e.attr.id=t; +self:send(e); +end +function i:new_id() +self.curr_id=self.curr_id+1; +return tostring(self.curr_id); +end +end) +package.preload['verse.component']=(function(...) +local t=require"verse"; +local a=t.stream_mt; +local d=require"util.jid".split; +local e=require"lxp"; +local o=require"util.stanza"; +local r=require"util.sha1".sha1; +t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply= +o.message,o.presence,o.iq,o.stanza,o.reply,o.error_reply; +local h=require"util.xmppstream".new; +local s="http://etherx.jabber.org/streams"; +local i="jabber:component:accept"; +local n={ +stream_ns=s, +stream_tag="stream", +default_ns=i}; +function n.streamopened(e,t) +e.stream_id=t.id; +if not e:event("opened",t)then +e.notopen=nil; +end +return true; +end +function n.streamclosed(e) +return e:event("closed"); +end +function n.handlestanza(t,e) +if e.attr.xmlns==s then +return t:event("stream-"..e.name,e); +elseif e.attr.xmlns or e.name=="handshake"then +return t:event("stream/"..(e.attr.xmlns or i),e); +end +return t:event("stanza",e); +end +function a:reset() +if self.stream then +self.stream:reset(); +else +self.stream=h(self,n); +end +self.notopen=true; +return true; +end +function a:connect_component(e,n) +self.jid,self.password=e,n; +self.username,self.host,self.resource=d(e); +function self.data(t,e) +local o,t=self.stream:feed(e); +if o then return;end +a:debug("debug","Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," ")); +a:close("xml-not-well-formed"); +end +self:hook("incoming-raw",function(e)return self.data(self.conn,e);end); +self.curr_id=0; +self.tracked_iqs={}; +self:hook("stanza",function(e) +local t,a=e.attr.id,e.attr.type; +if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then +self.tracked_iqs[t](e); +self.tracked_iqs[t]=nil; +return true; +end +end); +self:hook("stanza",function(e) +local a; +if e.attr.xmlns==nil or e.attr.xmlns=="jabber:client"then +if e.name=="iq"and(e.attr.type=="get"or e.attr.type=="set")then +local o=e.tags[1]and e.tags[1].attr.xmlns; +if o then +a=self:event("iq/"..o,e); +if not a then +a=self:event("iq",e); +end +end +if a==nil then +self:send(t.error_reply(e,"cancel","service-unavailable")); +return true; +end +else +a=self:event(e.name,e); +end +end +return a; +end,-1); +self:hook("opened",function(e) +print(self.jid,self.stream_id,e.id); +local e=r(self.stream_id..n,true); +self:send(o.stanza("handshake",{xmlns=i}):text(e)); +self:hook("stream/"..i,function(e) +if e.name=="handshake"then +self:event("authentication-success"); +end +end); +end); +local function e() +self:event("ready"); +end +self:hook("authentication-success",e,-1); +self:connect(self.connect_host or self.host,self.connect_port or 5347); +self:reopen(); +end +function a:reopen() +self:reset(); +self:send(o.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams', +xmlns=i,version="1.0"}):top_tag()); +end +function a:close(t) +if not self.notopen then +self:send(""); +end +local e=self.conn.disconnect(); +self.conn:close(); +e(conn,t); +end +function a:send_iq(t,a) +local e=self:new_id(); +self.tracked_iqs[e]=a; +t.attr.id=e; +self:send(t); +end +function a:new_id() +self.curr_id=self.curr_id+1; +return tostring(self.curr_id); +end +end) +pcall(require,"luarocks.require"); +local s=require"socket"; +pcall(require,"ssl"); +local a=require"net.server"; +local n=require"util.events"; +local o=require"util.logger"; +module("verse",package.seeall); +local e=_M; +_M.server=a; +local t={}; +t.__index=t; +stream_mt=t; +e.plugins={}; +function e.init(...) +for e=1,select("#",...)do +local t,a=pcall(require,"verse."..select(e,...)); +if not t then +error("Verse connection module not found: verse."..select(e,...).."\n"..a); +end +end +return e; +end +local i=0; +function e.new(o,a) +local t=setmetatable(a or{},t); +i=i+1; +t.id=tostring(i); +t.logger=o or e.new_logger("stream"..t.id); +t.events=n.new(); +t.plugins={}; +t.verse=e; +return t; +end +e.add_task=require"util.timer".add_task; +e.logger=o.init; +e.new_logger=o.init; +e.log=e.logger("verse"); +local function i(a,...) +local e,o,t=0,{...},select('#',...); +return(a:gsub("%%(.)",function(a)if e<=t then e=e+1;return tostring(o[e]);end end)); +end +function e.set_log_handler(e,t) +t=t or{"debug","info","warn","error"}; +o.reset(); +if io.type(e)=="file"then +local t=e; +function e(a,e,o) +t:write(a,"\t",e,"\t",o,"\n"); +end +end +if e then +local function a(t,a,o,...) +return e(t,a,i(o,...)); +end +for t,e in ipairs(t)do +o.add_level_sink(e,a); +end +end +end +function _default_log_handler(o,a,t) +return io.stderr:write(o,"\t",a,"\t",t,"\n"); +end +e.set_log_handler(_default_log_handler,{"error"}); +local function o(t) +e.log("error","Error: %s",t); +e.log("error","Traceback: %s",debug.traceback()); +end +function e.set_error_handler(e) +o=e; +end +function e.loop() +return xpcall(a.loop,o); +end +function e.step() +return xpcall(a.step,o); +end +function e.quit() +return a.setquitting(true); +end +function t:listen(t,e) +t=t or"localhost"; +e=e or 0; +local a,o=a.addserver(t,e,new_listener(self,"server"),"*a"); +if a then +self:debug("Bound to %s:%s",t,e); +self.server=a; +end +return a,o; +end +function t:connect(t,o) +t=t or"localhost"; +o=tonumber(o)or 5222; +local i=s.tcp() +i:settimeout(0); +local n,e=i:connect(t,o); +if not n and e~="timeout"then +self:warn("connect() to %s:%d failed: %s",t,o,e); +return self:event("disconnected",{reason=e})or false,e; +end +local t=a.wrapclient(i,t,o,new_listener(self),"*a"); +if not t then +self:warn("connection initialisation failed: %s",e); +return self:event("disconnected",{reason=e})or false,e; +end +self:set_conn(t); +return true; +end +function t:set_conn(t) +self.conn=t; +self.send=function(a,e) +self:event("outgoing",e); +e=tostring(e); +self:event("outgoing-raw",e); +return t:write(e); +end; +end +function t:close(t) +if not self.conn then +e.log("error","Attempt to close disconnected connection - possibly a bug"); +return; +end +local e=self.conn.disconnect(); +self.conn:close(); +e(self.conn,t); +end +function t:debug(...) +return self.logger("debug",...); +end +function t:info(...) +return self.logger("info",...); +end +function t:warn(...) +return self.logger("warn",...); +end +function t:error(...) +return self.logger("error",...); +end +function t:event(e,...) +self:debug("Firing event: "..tostring(e)); +return self.events.fire_event(e,...); +end +function t:hook(e,...) +return self.events.add_handler(e,...); +end +function t:unhook(t,e) +return self.events.remove_handler(t,e); +end +function e.eventable(e) +e.events=n.new(); +e.hook,e.unhook=t.hook,t.unhook; +local t=e.events.fire_event; +function e:event(e,...) +return t(e,...); +end +return e; +end +function t:add_plugin(t) +if self.plugins[t]then return true;end +if require("verse.plugins."..t)then +local a,e=e.plugins[t](self); +if a~=false then +self:debug("Loaded %s plugin",t); +self.plugins[t]=true; +else +self:warn("Failed to load %s plugin: %s",t,e); +end +end +return self; +end +function new_listener(t) +local a={}; +function a.onconnect(a) +if t.server then +local e=e.new(); +a:setlistener(new_listener(e)); +e:set_conn(a); +t:event("connected",{client=e}); +else +t.connected=true; +t:event("connected"); +end +end +function a.onincoming(a,e) +t:event("incoming-raw",e); +end +function a.ondisconnect(a,e) +if a~=t.conn then return end +t.connected=false; +t:event("disconnected",{reason=e}); +end +function a.ondrain(e) +t:event("drained"); +end +function a.onstatus(a,e) +t:event("status",e); +end +return a; +end +return e;