Thu, 03 Dec 2020 17:05:27 +0000
Initial commit
package.preload['util.encodings']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local function e() error("Function not implemented"); end local e=require"mime"; a"encodings" idna={}; stringprep={}; base64={encode=e.b64,decode=e.unb64}; utf8={ valid=(utf8 and utf8.len)and function(e)return not not utf8.len(e);end or function()return true;end; }; return _M; end) package.preload['util.hashes']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local function e(t,e) error("Hash method "..e.." not available",2); end local e=setmetatable({},{__index=e}); local function t(e,a) local e,o=pcall(require,e); if e then a(o);end end t("bgcrypto.md5",function(t) e.md5=t.digest; e.hmac_md5=t.hmac.digest; end); t("bgcrypto.sha1",function(t) e.sha1=t.digest; e.hmac_sha1=t.hmac.digest; e.scram_Hi_sha1=function(a,o,e)return t.pbkdf2(a,o,e,20);end; end); t("bgcrypto.sha256",function(t) e.sha256=t.digest; e.hmac_sha256=t.hmac.digest; end); t("bgcrypto.sha512",function(t) e.sha512=t.digest; e.hmac_sha512=t.hmac.digest; end); t("sha1",function(t) e.sha1=function(e,a) if a then return t.sha1(e); else return(t.binary(e)); end end; end); return e; end) package.preload['lib.adhoc']=(function(...) local _ENV=_ENV; local function d(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local s,i=require"util.stanza",require"util.uuid"; local r="http://jabber.org/protocol/commands"; local n={} local h={}; local function o(e,o,a,t) local e=s.stanza("command",{xmlns=r,node=e.node,status=o}); if a then e.attr.sessionid=a;end if t then e.attr.action=t;end return e; end function h.new(t,i,e,a) return{name=t,node=i,handler=e,cmdtag=o,permission=(a or"user")}; end function h.handle_cmd(o,h,t) local e=t.tags[1].attr.sessionid or i.generate(); local a={}; a.to=t.attr.to; a.from=t.attr.from; a.action=t.tags[1].attr.action or"execute"; a.form=t.tags[1]:child_with_ns("jabber:x:data"); local a,i=o:handler(a,n[e]); n[e]=i; local i=s.reply(t); local t; if a.status=="completed"then n[e]=nil; t=o:cmdtag("completed",e); elseif a.status=="canceled"then n[e]=nil; t=o:cmdtag("canceled",e); elseif a.status=="error"then n[e]=nil; i=s.error_reply(i,a.error.type,a.error.condition,a.error.message); h.send(i); return true; else t=o:cmdtag("executing",e); end for a,e in pairs(a)do if a=="info"then t:tag("note",{type="info"}):text(e):up(); elseif a=="warn"then t:tag("note",{type="warn"}):text(e):up(); elseif a=="error"then t:tag("note",{type="error"}):text(e.message):up(); elseif a=="actions"then local a=s.stanza("actions"); for i,e in ipairs(e)do if(e=="prev")or(e=="next")or(e=="complete")then a:tag(e):up(); else d:log("error",'Command "'..o.name.. '" at node "'..o.node..'" provided an invalid action "'..e..'"'); end end t:add_child(a); elseif a=="form"then t:add_child((e.layout or e):form(e.values)); elseif a=="result"then t:add_child((e.layout or e):form(e.values,"result")); elseif a=="other"then t:add_child(e); end end i:add_child(t); h.send(i); return true; end return h; end) package.preload['util.stanza']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=error; local t=table.insert; local l=table.remove; local p=table.concat; local r=string.format; local c=string.match; local f=tostring; local w=setmetatable; local q=getmetatable; local n=pairs; local s=ipairs; local a=type; local j=string.gsub; local m=string.sub; local u=string.find; local e=os; local h=require"util.encodings".utf8.valid; local y=not e.getenv("WINDIR"); local d,o; if y then local t,e=pcall(require,"util.termcolours"); if t then d,o=e.getstyle,e.getstring; else y=nil; end end local v="urn:ietf:params:xml:ns:xmpp-stanzas"; local _ENV=nil; local e={__name="stanza"}; e.__index=e; local function g(e,t) if a(e)~="string"then i("invalid "..t.." name: expected string, got "..a(e)); elseif#e==0 then i("invalid "..t.." name: empty string"); elseif u(e,"[<>& '\"]")then i("invalid "..t.." name: contains invalid characters"); elseif not h(e)then i("invalid "..t.." name: contains invalid utf8"); end end local function b(e,t) if a(e)~="string"then i("invalid "..t.." value: expected string, got "..a(e)); elseif not h(e)then i("invalid "..t.." value: contains invalid utf8"); end end local function k(e) if e~=nil then if a(e)~="table"then i("invalid attributes, expected table got "..a(e)); end for t,e in n(e)do g(t,"attribute"); b(e,"attribute"); if a(e)~="string"then i("invalid attribute value for '"..t.."': expected string, got "..a(e)); elseif not h(e)then i("invalid attribute value for '"..t.."': contains invalid utf8"); end end end end local function h(t,a,o) g(t,"tag"); k(a); local t={name=t,attr=a or{},namespaces=o,tags={}}; return w(t,e); end local function g(t) return q(t)==e; end 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:text_tag(o,a,t,e) return self:tag(o,t,e):text(a):up(); end function e:tag(e,a,o) local a=h(e,a,o); 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) if t~=nil and t~=""then local e=self.last_add; (e and e[#e]or self):add_direct_child(t); end return self; end function e:up() local e=self.last_add; if e then l(e);end return self; end function e:reset() self.last_add=nil; return self; end function e:add_direct_child(e) if g(e)then t(self.tags,e); t(self,e); else b(e,"text"); t(self,e); end 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:remove_children(a,e) e=e or self.attr.xmlns; return self:maptags(function(t) if(not a or t.name==a)and t.attr.xmlns==e then return nil; end return t; end); end function e:get_child(a,t) for o,e in s(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 s(self.tags)do if e.name==t then return e;end end end function e:child_with_ns(t) for a,e in s(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(t,a) local e=self.tags; local o,i=1,#e; return function() for i=o,i do local e=e[i]; if(not t or e.name==t) and((not a and self.attr.xmlns==e.attr.xmlns) or e.attr.xmlns==a)then o=i+1; return e; end end end; end function e:maptags(h) local o,t=self.tags,1; local n,a=#self,#o; local s=n+1; local e=1; while t<=a and a>0 do if self[e]==o[t]then local i=h(self[e]); if i==nil then l(self,e); l(o,t); n=n-1; a=a-1; e=e-1; t=t-1; else self[e]=i; o[t]=i; end t=t+1; end e=e+1; if e>s then i("Invalid stanza state! Please report this error."); end end return self; end function e:find(a) local e=1; local s=#a+1; repeat local o,t,i; local n=m(a,e,e); if n=="@"then return self.attr[m(a,e+1)]; elseif n=="{"then o,e=c(a,"^([^}]+)}()",e+1); end t,i,e=c(a,"^([^@/#]*)([/#]?)()",e); t=t~=""and t or nil; if e==s then if i=="#"then return self:get_child_text(t,o); end return self:get_child(t,o); end self=self:get_child(t,o); until not self end local i={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; local function l(e)return(j(e,"['&<>\"]",i));end local function m(o,e,s,a,r) local i=0; local h=o.name t(e,"<"..h); for o,n in n(o.attr)do if u(o,"\1",1,true)then local o,s=c(o,"^([^\1]*)\1?(.*)$"); i=i+1; t(e," xmlns:ns"..i.."='"..a(o).."' ".."ns"..i..":"..s.."='"..a(n).."'"); elseif not(o=="xmlns"and n==r)then t(e," "..o.."='"..a(n).."'"); 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 s(i,e,s,a,o.attr.xmlns); else t(e,a(i)); end end t(e,"</"..h..">"); end end function e.__tostring(t) local e={}; m(t,e,m,l,nil); return p(e); end function e.top_tag(e) local t=""; if e.attr then for e,o in n(e.attr)do if a(e)=="string"then t=t..r(" %s='%s'",e,l(f(o)));end end end return r("<%s%s>",e.name,t); end function e.get_text(e) if#e.tags==0 then return p(e); end end function e.get_error(a) local o,t,e; local a=a:get_child("error"); if not a then return nil,nil,nil; end o=a.attr.type; for o,a in s(a.tags)do if a.attr.xmlns==v then if not e and a.name=="text"then e=a:get_text(); elseif not t then t=a.name; end if t and e then break; end end end return o,t or"undefined-condition",e; end local function m(o) local i={name=o.name,attr=o.attr}; for o,e in s(o)do if a(e)=="table"then t(i,m(e)); else t(i,e); end end return i; end e.__freeze=m; local function p(o) if o then local i=o.attr; for e=1,#i do i[e]=nil;end local h={}; for e in n(i)do if u(e,"|",1,true)and not u(e,"\1",1,true)then local a,t=c(e,"^([^|]+)|(.+)$"); h[a.."\1"..t]=i[e]; i[e]=nil; end end for e,t in n(h)do i[e]=t; end w(o,e); for t,e in s(o)do if a(e)=="table"then p(e); end end if not o.tags then local e={}; for n,i in s(o)do if a(i)=="table"then t(e,i); end end o.tags=e; end end return o; end local function u(a) local i,h={},{}; for t,e in n(a.attr)do i[t]=e;end local s,o=a.namespaces; if s then o={}; for e,t in n(s)do o[e]=t;end end local o={name=a.name,attr=i,namespaces=o,tags=h}; for e=1,#a do local e=a[e]; if e.name then e=u(e); t(h,e); end t(o,e); end return w(o,e); end local function w(t,e) if not e then return h("message",t); else return h("message",t):tag("body"):text(e):up(); end end local function b(e) if not(e and e.id)then end return h("iq",e); end local function c(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 local t={xmlns=v}; local function v(e,i,o,a) local e=c(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 local function k(e) return h("presence",e); end if y then local i=d("yellow"); local u=d("red"); local h=d("red"); local t=d("magenta"); local i=" "..o(i,"%s")..o(t,"=")..o(u,"'%s'"); local d=o(t,"<")..o(h,"%s").."%s"..o(t,">"); local h=d.."%s"..o(t,"</")..o(h,"%s")..o(t,">"); function e.pretty_print(t) local e=""; for o,t in s(t)do if a(t)=="string"then e=e..l(t); else e=e..t:pretty_print(); end end local o=""; if t.attr then for e,t in n(t.attr)do if a(e)=="string"then o=o..r(i,e,f(t));end end end return r(h,t.name,o,e,t.name); end function e.pretty_top_tag(t) local e=""; if t.attr then for t,o in n(t.attr)do if a(t)=="string"then e=e..r(i,t,f(o));end end end return r(d,t.name,e); end else e.pretty_print=e.__tostring; e.pretty_top_tag=e.top_tag; end return{ stanza_mt=e; stanza=h; is_stanza=g; preserialize=m; deserialize=p; clone=u; message=w; iq=b; reply=c; error_reply=v; presence=k; xml_escape=l; }; end) package.preload['util.timer']=(function(...) local _ENV=_ENV; local function u(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"net.server"; local h=math.min local l=math.huge local i=require"socket".gettime; local s=table.insert; local d=pairs; local r=type; local n={}; local a={}; u"timer" local e; if not o.event then function e(t,n) local i=i(); t=t+i; if t>=i then s(a,{t,n}); else local t=n(i); if t and r(t)=="number"then return e(t,n); end end end o._addtimer(function() local o=i(); if#a>0 then for t,e in d(a)do s(n,e); end a={}; end local t=l; for d,a in d(n)do local s,i=a[1],a[2]; if s<=o then n[d]=nil; local a=i(o); if r(a)=="number"then e(a,i); t=h(t,a); end else t=h(t,s-o); end end return t; end); else local t=o.event; local n=o.event_base; local o=(t.core and t.core.LEAVE)or-1; function e(a,e) local t; t=n:addevent(nil,0,function() local e=e(i()); if e then return 0,e; elseif t then return o; end end ,a); end end add_task=e; return _M; end) package.preload['util.termcolours']=(function(...) local _ENV=_ENV; local function s(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i,n=table.concat,table.insert; local t,a=string.char,string.format; local h=tonumber; local r=ipairs; local d=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(); s"termcolours" local s={ 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 l={ ["0"]=o, ["1"]=7+8, ["1;33"]=2+4+8, ["1;31"]=4+8 } local u={ [1]="font-weight: bold",[2]="opacity: 0.5",[4]="text-decoration: underline",[8]="visibility: hidden", [30]="color:black",[31]="color:red",[32]="color:green",[33]="color:#FFD700", [34]="color:blue",[35]="color: magenta",[36]="color:cyan",[37]="color: white", [40]="background-color:black",[41]="background-color:red",[42]="background-color:green", [43]="background-color:yellow",[44]="background-color:blue",[45]="background-color: magenta", [46]="background-color:cyan",[47]="background-color: white"; }; local c=t(27).."[%sm%s"..t(27).."[0m"; function getstring(t,e) if t then return a(c,t,e); else return e; end end function getstyle(...) local e,t={...},{}; for a,e in r(e)do e=s[e]; if e then n(t,e); end end return i(t,";"); end local a="0"; function setstyle(e) e=e or"0"; if e~=a then d("\27["..e.."m"); a=e; end end if e then function setstyle(t) t=t or"0"; if t~=a then e.set_consolecolor(l[t]or o); a=t; end end if not o then function setstyle(e)end end end local function a(t) if t=="0"then return"</span>";end local e={}; for t in t:gmatch("[^;]+")do n(e,u[h(t)]); end return"</span><span style='"..i(e,";").."'>"; end function tohtml(e) return e:gsub("\027%[(.-)m",a); end return _M; end) package.preload['util.uuid']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=tostring; local e=os.time; local o=os.clock; local n=require"util.hashes".sha1; a"uuid" local t=0; local function a() local e=e(); if t>=e then e=t+1;end t=e; return e; end local function t(e) return n(e..o()..i({}),true); end local e=t(a()); local function o(a) e=t(e..a); end local function t(t) if#e<t then o(a());end local a=e:sub(0,t); e=e:sub(t+1); return a; end local function e() return("%x"):format(t(1):byte()%4+8); end function generate() return t(8).."-"..t(4).."-4"..t(3).."-"..(e())..t(3).."-"..t(12); end seed=o; return _M; end) package.preload['net.dns']=(function(...) local _ENV=_ENV; local function c(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"socket"; local j=require"util.timer"; local e,y=pcall(require,"util.windows"); local E=(e and y)or os.getenv("WINDIR"); local u,_,v,a,i= coroutine,io,math,string,table; local f,s,o,m,r,b,z,x,t,e,q= ipairs,next,pairs,print,setmetatable,tostring,assert,error,unpack,select,type; local e={ get=function(t,...) local a=e('#',...); for a=1,a do t=t[e(a,...)]; if t==nil then break;end end return t; end; set=function(a,...) local i=e('#',...); local h,o=e(i-1,...); local t,n; for i=1,i-2 do local i=e(i,...) local e=a[i] if o==nil then if e==nil then return; elseif s(e,s(e))then t=nil;n=nil; elseif t==nil then t=a;n=i; end elseif e==nil then e={}; a[i]=e; end a=e end if o==nil and t then t[n]=nil; else a[h]=o; return o; end end; }; local d,l=e.get,e.set; local k=15; c('dns') local t=_M; local h=i.insert local function c(e) return(e-(e%256))/256; end local function p(e) local t={}; for o,e in o(e)do t[o]=e; t[e]=e; t[a.lower(e)]=e; end return t; end local function w(i) local e={}; for t,i in o(i)do local o=a.char(c(t),t%256); e[t]=o; e[i]=o; e[a.lower(i)]=o; end return e; end t.types={ 'A','NS','MD','MF','CNAME','SOA','MB','MG','MR','NULL','WKS', 'PTR','HINFO','MINFO','MX','TXT', [28]='AAAA',[29]='LOC',[33]='SRV', [252]='AXFR',[253]='MAILB',[254]='MAILA',[255]='*'}; t.classes={'IN','CS','CH','HS',[255]='*'}; t.type=p(t.types); t.class=p(t.classes); t.typecode=w(t.types); t.classcode=w(t.classes); local function g(e,o,i) if a.byte(e,-1)~=46 then e=e..'.';end e=a.lower(e); return e,t.type[o or'A'],t.class[i or'IN']; end local function p(t,a,o) a=a or n.gettime(); for n,e in f(t)do if e.tod then e.ttl=v.floor(e.tod-a); if e.ttl<=0 then t[e[e.type:lower()]]=nil; i.remove(t,n); return p(t,a,o); end elseif o=='soft'then z(e.ttl==0); t[e[e.type:lower()]]=nil; i.remove(t,n); end end end local e={}; e.__index=e; e.timeout=k; local function k(e) local e=e.type and e[e.type:lower()]; if q(e)~="string"then return"<UNKNOWN RDATA TYPE>"; end return e; end local w={ 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 q={}; function q.__tostring(e) local t=(w[e.type]or k)(e); return a.format('%2s %-5s %6i %-28s %s',e.class,e.type,e.ttl,e.name,t); end local k={}; function k.__tostring(t) local e={}; for a,t in f(t)do h(e,b(t)..'\n'); end return i.concat(e); end local w={}; function w.__tostring(t) local a=n.gettime(); local e={}; for i,t in o(t)do for i,t in o(t)do for o,t in o(t)do p(t,a); h(e,b(t)); end end end return i.concat(e); end function e:new() local t={active={},cache={},unsorted={}}; r(t,e); r(t.cache,w); r(t.unsorted,{__mode='kv'}); return t; end function t.random(...) v.randomseed(v.floor(1e4*n.gettime())%2147483648); t.random=v.random; return t.random(...); end local function v(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( c(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, c(e.qdcount),e.qdcount%256, c(e.ancount),e.ancount%256, c(e.nscount),e.nscount%256, c(e.arcount),e.arcount%256 ); return t,e.id; end local function c(t) local e={}; for t in a.gmatch(t,'[^.]+')do h(e,a.char(a.len(t))); h(e,t); end h(e,a.char(0)); return i.concat(e); end local function z(o,a,e) o=c(o); a=t.typecode[a or'a']; e=t.classcode[e or'in']; return o..a..e; end function e:byte(e) e=e or 1; local o=self.offset; local t=o+e-1; if t>#self.packet then x(a.format('out of bounds: %i>%i',t,#self.packet)); end self.offset=o+e; return a.byte(self.packet,o,t); end function e:word() local e,t=self:byte(2); return 256*e+t; end function e:dword() local t,e,a,o=self:byte(4); return 16777216*t+65536*e+256*a+o; 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={}; if e==0 then return"."end while e>0 do if e>=192 then a=a+1; if a>=20 then x('dns error: 20 pointers');end; local e=((e-192)*256)+self:byte(); t=t or self.offset; self.offset=e+1; else h(o,self:sub(e)..'.'); end e=self:byte(); end self.offset=t or self.offset; return i.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 o,e,t,n=self:byte(4); i.a=a.format('%i.%i.%i.%i',o,e,t,n); end function e:AAAA(a) local e={}; for t=1,a.rdlength,2 do local a,t=self:byte(2); i.insert(e,("%02x%02x"):format(a,t)); end e=i.concat(e,":"):gsub("%f[%x]0+(%x)","%1"); local t={}; for e in e:gmatch(":[0:]+:")do i.insert(t,e) end if#t==0 then a.aaaa=e; return elseif#t>1 then i.sort(t,function(t,e)return#t>#e 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 c(e,i,t) e=e-2147483648; if e<0 then i=t;e=-e;end local n,o,t; t=e%6e4; e=(e-t)/6e4; o=e%60; n=(e-o)/60; return a.format('%3d %2d %2.3f %s',n,o,t/1e3,i); end function e.LOC_tostring(e) local t={}; h(t,a.format( '%s %s %.2fm %.2fm %.2fm %.2fm', c(e.loc.latitude,'N','S'), c(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 i.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,q); 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 h(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 i=self.offset; for e=1,t.header.qdcount do h(t.question,self:question()); end t.question.raw=a.sub(self.packet,i,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 self.active[t.header.id]=nil; 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{}; h(self.server,e); end function e:setnameserver(e) self.server={}; self:addnameserver(e); end function e:adddefaultnameservers() if E then if y and y.get_nameservers then for t,e in f(y.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=_.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(a) self.socket=self.socket or{}; self.socketset=self.socketset or{}; local e=self.socket[a]; if e then return e;end local o,t; e,t=n.udp(); if e and self.socket_wrapper then e,t=self.socket_wrapper(e,self);end if not e then return nil,t; end e:settimeout(0); self.socket[a]=e; self.socketset[e]=a; o,t=e:setsockname('*',0); if not o then return self:servfail(e,t);end o,t=e:setpeername(self.server[a],53); if not o then return self:servfail(e,t);end 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 e:close(); end function e:socket_wrapper_set(e) self.socket_wrapper=e; end function e:closeall() for t,e in f(self.socket)do self.socket[t]=nil; self.socketset[e]=nil; e:close(); end end function e:remember(e,t) local a,o,i=g(e.name,e.type,e.class); if t~='*'then t=o; local t=d(self.cache,i,'*',a); if t then h(t,e);end end self.cache=self.cache or r({},w); local a=d(self.cache,i,t,a)or l(self.cache,i,t,a,r({},k)); if not a[e[o:lower()]]then a[e[o:lower()]]=true; h(a,e); end if t=='MX'then self.unsorted[a]=true;end end local function c(t,e) return(t.pref==e.pref)and(t.mx<e.mx)or(t.pref<e.pref); end function e:peek(o,t,a,h) o,t,a=g(o,t,a); local e=d(self.cache,a,t,o); if not e then if h then if h<=0 then return end else h=3 end e=d(self.cache,a,"CNAME",o); if not(e and e[1])then return end return self:peek(e[1].cname,t,a,h-1); end if p(e,n.gettime())and t=='*'or not s(e)then l(self.cache,a,t,o,nil); return nil; end if self.unsorted[e]then i.sort(e,c);self.unsorted[e]=nil;end return e; end function e:purge(e) if e=='soft'then self.time=n.gettime(); for t,e in o(self.cache or{})do for t,e in o(e)do for t,e in o(e)do p(e,self.time,'soft') end end end else self.cache=r({},w);end end function e:query(a,t,e) a,t,e=g(a,t,e) local s=u.running(); local o=d(self.wanted,e,t,a); if s and o then l(self.wanted,e,t,a,s,true); return true; end if not self.server then self:adddefaultnameservers();end local h=z(a,t,e); local o=self:peek(a,t,e); if o then return o;end local o,i=v(); local o={ packet=o..h, server=self.best_server, delay=1, retry=n.gettime()+self.delays[1] }; self.active[i]=self.active[i]or{}; self.active[i][h]=o; if s then l(self.wanted,e,t,a,s,true); end local i,h=self:getsocket(o.server) if not i then return nil,h; end i:send(o.packet) if j and self.timeout then local r=#self.server; local n=1; j.add_task(self.timeout,function() if d(self.wanted,e,t,a,s)then if n<r then n=n+1; self:servfail(i); o.server=self.best_server; i,h=self:getsocket(o.server); if i then i:send(o.packet); return self.timeout; end end self:cancel(e,t,a); end end) end return true; end function e:servfail(t,i) local h=self.socketset[t] t=self:voidsocket(t); self.time=n.gettime(); for n,a in o(self.active)do for o,e in o(a)do if e.server==h then e.server=e.server+1 if e.server>#self.server then e.server=1; end e.retries=(e.retries or 0)+1; if e.retries>=#self.server then a[o]=nil; else t,i=self:getsocket(e.server); if t then t:send(e.packet);end end end end if s(a)==nil then self.active[n]=nil; end end if h==self.best_server then self.best_server=self.best_server+1; if self.best_server>#self.server then self.best_server=1; end end return t,i; end function e:settimeout(e) self.timeout=e; end function e:receive(t) self.time=n.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 self:remember(t,e.question[1].type) end local t=self.active[e.header.id]; t[e.question.raw]=nil; if not s(t)then self.active[e.header.id]=nil;end if not s(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 e in o(t)do if u.status(e)=="suspended"then u.resume(e);end end l(self.wanted,e.class,e.type,e.name,nil); end end end end end return e; end function e:feed(a,t,e) self.time=n.gettime(); local e=self:decode(t,e); 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 s(t)then self.active[e.header.id]=nil;end if not s(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 e in o(t)do if u.status(e)=="suspended"then u.resume(e);end end l(self.wanted,e.class,e.type,e.name,nil); end end end return e; end function e:cancel(e,t,a) local i=d(self.wanted,e,t,a); if i then for e in o(i)do if u.status(e)=="suspended"then u.resume(e);end end l(self.wanted,e,t,a,nil); end end function e:pulse() while self:receive()do end if not s(self.active)then return nil;end self.time=n.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 s(t)then self.active[i]=nil;end if not s(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 s(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 f(self.socket)do e[a]=t end n.select(e,nil,4) end return self:peek(o,a,t); end function e:lookupex(o,e,t,a) return self:peek(e,t,a)or self:query(e,t,a); 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 s(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],s(t.header,e)); end for t,e in f(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 r={name=1,type=1,class=1,ttl=1,rdlength=1,rdata=1}; local e; for i,n in o({'answer','authority','additional'})do for h,i in o(t[n])do for o,t in o({'name','type','class','ttl','rdlength'})do e=a.format('%s[%i].%s',n,h,t); m(a.format('%-30s',e),i[t],s(i,t)); end for t,o in o(i)do if not r[t]then e=a.format('%s[%i].%s',n,h,t); m(a.format('%-30s %s',b(e),b(o))); end end end end end function t.resolver() local t={active={},cache={},unsorted={},wanted={},best_server=1}; r(t,e); r(t.cache,w); 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.cache() return e.cache; end function t.socket_wrapper_set(...) return e:socket_wrapper_set(...); end return t; end) package.preload['net.adns']=(function(...) local _ENV=_ENV; local function o(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local c=require"net.server"; local a=require"net.dns"; local e=require"util.logger".init("adns"); local t,t=table.insert,table.remove; local n,s,l=coroutine,tostring,pcall; local function u(a,a,t,e)return(e-t)+1;end o"adns" function lookup(d,t,h,r) return n.wrap(function(o) if o then e("debug","Records for %s already cached, using those...",t); d(o); return; end e("debug","Records for %s not in cache, sending query (%s)...",t,s(n.running())); local i,o=a.query(t,h,r); if i then n.yield({r or"IN",h or"A",t,n.running()}); e("debug","Reply for %s (%s)",t,s(n.running())); end if i then i,o=l(d,a.peek(t,h,r)); else e("error","Error sending DNS query: %s",o); i,o=l(d,nil,o); end if not i then e("error","Error in DNS response handler: %s",s(o)); end end)(a.peek(t,h,r)); end function cancel(t,o,i) e("warn","Cancelling DNS lookup for %s",s(t[3])); a.cancel(t[1],t[2],t[3],t[4],o); end function new_async_socket(i,o) local n="<unknown>"; local s={}; local t={}; local h; function s.onincoming(o,e) if e then a.feed(t,e); end end function s.ondisconnect(i,a) if a then e("warn","DNS socket for %s disconnected: %s",n,a); local t=o.server; if o.socketset[i]==o.best_server and o.best_server==#t then e("error","Exhausted all %d configured DNS servers, next lookup will try %s again",#t,t[1]); end o:servfail(i); end end t,h=c.wrapclient(i,"dns",53,s); if not t then return nil,h; end t.settimeout=function()end t.setsockname=function(e,...)return i:setsockname(...);end t.setpeername=function(o,...)n=(...);local e,a=i:setpeername(...);o:set_send(u);return e,a;end t.connect=function(e,...)return i:connect(...)end t.send=function(a,t) e("debug","Sending DNS query to %s",n); return i:send(t); end return t; end a.socket_wrapper_set(new_async_socket); return _M; end) package.preload['net.server']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local m=function(e) return _G[e] end local W,e=require("util.logger").init("socket"),table.concat; local n=function(...)return W("debug",e{...});end local E=function(...)return W("warn",e{...});end local ue=1 local f=m"type" local q=m"pairs" local me=m"ipairs" local y=m"tonumber" local u=m"tostring" local t=m"table" local a=m"string" local e=m"coroutine" local Y=math.min local ce=math.huge local fe=t.concat local de=t.insert local ye=a.sub local we=e.wrap local pe=e.yield local I,e=pcall(require,"ssl") local b=m"socket"or require"socket" local P=b.gettime local le=b.dns.getaddrinfo local ve=(I and e.wrap) local se=b.bind local be=b.select local O local Z local ie local V local B local l local ne local X local oe local te local ae local J local s local he local ee local re local p local i local C local r local h local A local v local w local k local z local a local o local g local F local M local T local j local R local Q local d local S local L local U local D local H local _ local x local N p={} i={} r={} C={} h={} v={} w={} A={} k={} a=0 o=0 g=0 F=0 M=0 T=1 j=128 R=10 S=51e3*1024 L=25e3*1024 U=30 D=6e4 H=14*60 local e=package.config:sub(1,1)=="\\" x=(e and math.huge)or b._SETSIZE or 1024 _=b._SETSIZE or 1024 N=30 te=function(y,t,m,c,g,w) if t:getfd()>=x then E("server.lua: Disallowed FD number: "..t:getfd()) t:close() return nil,"fd-too-large" end local f=0 local v,e=y.onconnect,y.ondisconnect local b=t.accept local e={} e.shutdown=function()end e.ssl=function() return w~=nil end e.sslctx=function() return w end e.remove=function() f=f-1 if e then e.resume() end end e.close=function() t:close() o=s(r,t,o) a=s(i,t,a) p[m..":"..c]=nil; h[t]=nil e=nil t=nil n"server.lua: closed server handler and removed sockets from list" end e.pause=function(o) if not e.paused then a=s(i,t,a) if o then h[t]=nil t:close() t=nil; end e.paused=true; n("server.lua: server [",m,"]:",c," paused") end end e.resume=function() if e.paused then if not t then t=se(m,c,j); t:settimeout(0) end a=l(i,t,a) h[t]=e k[e]=nil e.paused=false; n("server.lua: server [",m,"]:",c," resumed") end end e.ip=function() return m end e.serverport=function() return c end e.socket=function() return t end e.readbuffer=function() if a>=_ or o>=_ then e.pause() k[e]=d n("server.lua: refused new client connection: server full") return false end local t,o=b(t) if t then local o,a=t:getpeername() local e,i,t=ee(e,y,t,o,c,a,g,w) if t then return false end f=f+1 n("server.lua: accepted new client connection from ",u(o),":",u(a)," to ",u(c)) if v and not w then return v(e); end return; elseif o then n("server.lua: error with new client connection: ",u(o)) e.pause() k[e]=d return false end end return e end ee=function(b,f,t,R,X,D,H,q) if t:getfd()>=x then E("server.lua: Disallowed FD number: "..t:getfd()) t:close() if b then k[b]=d b.pause() end return nil,nil,"fd-too-large" end t:settimeout(0) local y local T local g local K local G=f.onincoming local B=f.onstatus local k=f.ondisconnect local W=f.ondrain local J=f.onreadtimeout; local C=f.ondetach local p={} local c=0 local Q local U local m=0 local j=false local E=false local Y,P=0,0 local x=S local _=L local e=p e.dispatch=function() return G end e.disconnect=function() return k end e.onreadtimeout=J; e.setlistener=function(a,t) if C then C(a) end G=t.onincoming k=t.ondisconnect B=t.onstatus W=t.ondrain e.onreadtimeout=t.onreadtimeout C=t.ondetach end e.getstats=function() return P,Y end e.ssl=function() return K end e.sslctx=function() return q end e.send=function(n,a,o,i) return y(t,a,o,i) end e.receive=function(o,a) return T(t,o,a) end e.shutdown=function(a) return g(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.force_close=function(t,a) if c~=0 then n("server.lua: discarding unwritten data for ",u(R),":",u(D)) c=0; end return t:close(a); end e.close=function(l,d) if not e then return true;end a=s(i,t,a) v[e]=nil if c~=0 then e.sendbuffer() if c~=0 then if e then e.write=nil end Q=true return false end end if t then z=g and g(t) t:close() o=s(r,t,o) h[t]=nil t=nil else n"server.lua: socket already closed" end if e then w[e]=nil A[e]=nil local t=e; e=nil if k then k(t,d or false); k=nil end end if b then b.remove() end n"server.lua: closed client handler and removed socket from list" return true end e.server=function() return b end e.ip=function() return R end e.serverport=function() return X end e.clientport=function() return D end e.port=e.clientport local b=function(i,a) if not e then return false end m=m+#a if m>x then A[e]="send buffer exceeded" e.write=V return false elseif t and not r[t]then o=l(r,t,o) end c=c+1 p[c]=a if e then w[e]=w[e]or d end return true end e.write=b e.bufferqueue=function(t) return p end e.socket=function(a) return t end e.set_mode=function(a,t) H=t or H return H end e.set_send=function(a,t) y=t or y return y end e.bufferlen=function(o,a,t) x=t or x _=a or _ return m,_,x end e.lock_read=function(n,o) if o==true then local o=a a=s(i,t,a) v[e]=nil if a~=o then j=true end elseif o==false then if j then j=false a=l(i,t,a) v[e]=d end end return j 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=V local a=o o=s(r,t,o) w[e]=nil if o~=a then E=true end elseif a==false then e.write=b if E then E=false b("") end end return j,E end local v=function() local a,t,o=T(t,H) if not t or(t=="wantread"or t=="timeout")then local o=a or o or"" local a=#o if a>_ then e:close("receive buffer exceeded") return false end local a=a*ue P=P+a M=M+a v[e]=d return G(e,o,t) else n("server.lua: client ",u(R),":",u(D)," read error: ",u(t)) z=e and e:force_close(t) return false end end local p=function() local f,a,h,i,l; if t then i=fe(p,"",1,c) f,a,h=y(t,i,1,m) l=(f or h or 0)*ue Y=Y+l F=F+l for e=c,1,-1 do p[e]=nil end else f,a,l=false,"unexpected close",0; end if f then c=0 m=0 o=s(r,t,o) w[e]=nil if W then W(e) end z=U and e:starttls(nil) z=Q and e:force_close() return true elseif h and(a=="timeout"or a=="wantwrite")then i=ye(i,h+1,m) p[1]=i c=1 m=m-h w[e]=d return true else n("server.lua: client ",u(R),":",u(D)," write error: ",u(a)) z=e and e:force_close(a) return false end end local d; function e.set_sslctx(w,t) q=t; local u,m d=we(function(h) local t for d=1,N do o=(m and s(r,h,o))or o a=(u and s(i,h,a))or a u,m=nil,nil d,t=h:dohandshake() if not t then n("server.lua: ssl handshake done") e.readbuffer=v e.sendbuffer=p d=B and B(e,"ssl-handshake-complete") if w.autostart_ssl and f.onconnect then f.onconnect(w); if c~=0 then o=l(r,h,o) end end a=l(i,h,a) return true else if t=="wantwrite"then o=l(r,h,o) m=true elseif t=="wantread"then a=l(i,h,a) u=true else break; end t=nil; pe() end end t="ssl handshake error: "..(t or"handshake too long"); n("server.lua: ",t); z=e and e:force_close(t) return false,t end ) end if I then e.starttls=function(f,m) if m then e:set_sslctx(m); end if c>0 then n"server.lua: we need to do tls, but delaying until send buffer empty" U=true return end n("server.lua: attempting to start tls on "..u(t)) local m,c=t t,c=ve(t,q) if not t then n("server.lua: error while starting tls on client: ",u(c or"unknown error")) return nil,c end t:settimeout(0) y=t.send T=t.receive g=O h[t]=e a=l(i,t,a) a=s(i,m,a) o=s(r,m,o) h[m]=nil e.starttls=nil U=nil K=true e.readbuffer=d e.sendbuffer=d return d(t) end end e.readbuffer=v e.sendbuffer=p y=t.send T=t.receive g=(K and O)or t.shutdown h[t]=e a=l(i,t,a) if q and I then n"server.lua: auto-starting ssl negotiation..." e.autostart_ssl=true; local t,e=e:starttls(q); if t==false then return nil,nil,e end end return e,t end O=function() end V=function() return false end l=function(t,a,e) if not t[a]then e=e+1 t[e]=a t[a]=e end return e; end s=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 J=function(e) o=s(r,e,o) a=s(i,e,a) h[e]=nil e:close() end local function z(e,a,o) local t; local i=a.sendbuffer; function a.sendbuffer() i(); if t and a.bufferlen()<o then e:lock_read(false); t=nil; end end local i=e.readbuffer; function e.readbuffer() i(); if not t and a.bufferlen()>=o then t=true; e:lock_read(true); end end e:set_mode("*a"); end ne=function(e,t,d,u,r) e=e or"*" local o if f(d)~="table"then o="invalid listener table" elseif f(e)~="string"then o="invalid address" elseif f(t)~="number"or not(t>=0 and t<=65535)then o="invalid port" elseif p[e..":"..t]then o="listeners on '["..e.."]:"..t.."' already exist" elseif r and not I then o="luasec not found" end if o then E("server.lua, [",e,"]:",t,": ",o) return nil,o end local o,s=se(e,t,j) if s then E("server.lua, [",e,"]:",t,": ",s) return nil,s end local s,d=te(d,o,e,t,u,r) if not s then o:close() return nil,d end o:settimeout(0) a=l(i,o,a) p[e..":"..t]=s h[o]=s n("server.lua: new "..(r and"ssl "or"").."server listener on '[",e,"]:",t,"'") return s end oe=function(e,t) return p[e..":"..t]; end he=function(e,t) local a=p[e..":"..t] if not a then return nil,"no server found on '["..e.."]:"..u(t).."'" end a:close() p[e..":"..t]=nil return true end B=function() for e,t in q(h)do t:close() h[e]=nil end a=0 o=0 g=0 p={} i={} r={} C={} h={} end ae=function() return{ select_timeout=T; tcp_backlog=j; max_send_buffer_size=S; max_receive_buffer_size=L; select_idle_check_interval=U; send_timeout=D; read_timeout=H; max_connections=_; max_ssl_handshake_roundtrips=N; highest_allowed_fd=x; accept_retry_interval=R; } end re=function(e) if f(e)~="table"then return nil,"invalid settings table" end T=y(e.select_timeout)or T S=y(e.max_send_buffer_size)or S L=y(e.max_receive_buffer_size)or L U=y(e.select_idle_check_interval)or U j=y(e.tcp_backlog)or j D=y(e.send_timeout)or D H=y(e.read_timeout)or H R=y(e.accept_retry_interval)or R _=e.max_connections or _ N=e.max_ssl_handshake_roundtrips or N x=e.highest_allowed_fd or x return true end X=function(e) if f(e)~="function"then return nil,"invalid listener function" end g=g+1 C[g]=e return true end local u do local o={}; local t={}; function u(e,a) local o=P(); e=e+o; if e>=o then de(t,{e,a}); else local e=a(o); if e and f(e)=="number"then return u(e,a); end end end X(function(a) if#t>0 then for a,e in q(t)do de(o,e); end t={}; end local e=ce; for s,t in q(o)do local i,n=t[1],t[2]; if i<=a then o[s]=nil; local t=n(a); if f(t)=="number"then u(t,n); e=Y(e,t); end else e=Y(e,i-a); end end return e; end); end ie=function() return M,F,a,o,g end local e; local function c(t) e=t; end Z=function(t) if e then return"quitting";end if t then e="once";end d=P() repeat local t=ce; for e=1,g do local e=C[e](d) if e then t=Y(t,e);end end local t,a,o=be(i,r,Y(T,t)) for t,e in me(t)do local t=h[e] if t then t.readbuffer() else J(e) n"server.lua: found no handler and closed socket (readlist)" end end for e,t in me(a)do local e=h[t] if e then e.sendbuffer() else J(t) n"server.lua: found no handler and closed socket (writelist)" end end for e,t in q(A)do e.disconnect()(e,t) e:force_close() A[e]=nil; end d=P() if d-Q>U then Q=d for e,t in q(w)do if d-t>D then e.disconnect()(e,"send timeout") e:force_close() end end for e,t in q(v)do if d-t>H then if not(e.onreadtimeout)or e:onreadtimeout()~=true then e.disconnect()(e,"read timeout") e:close() else v[e]=d end end end end for e,t in q(k)do if d-t>R then k[e]=nil; e.resume(); end end until e; if e=="once"then e=nil;return;end B(); return"quitting" end local function y() return Z(true); end local function g() return"select"; end local n=function(t,d,u,n,e,s) local e,t,d=ee(nil,n,t,d,u,"clientport",e,s) if not e then return nil,d end h[t]=e if not s then a=l(i,t,a) o=l(r,t,o) if n.onconnect then local t=e.sendbuffer; e.sendbuffer=function() e.sendbuffer=t; n.onconnect(e); return t(); end end end return e,t end local b=function(a,t,s,r,i,o) local e if f(s)~="table"then e="invalid listener table" elseif f(a)~="string"then e="invalid address" elseif f(t)~="number"or not(t>=0 and t<=65535)then e="invalid port" elseif i and not I then e="luasec not found" end if le and not o then local e,t=le(a) if not e then return nil,t end if e[1]and e[1].family=="inet6"then o="tcp6" end end local o=b[o or"tcp"] if f(o)~="function"then e="invalid socket type" end if e then E("server.lua, addclient: ",e) return nil,e end local o,e=o() if e then return nil,e end o:settimeout(0) local h,e=o:connect(a,t) if h or e=="timeout"or e=="Operation already in progress"then return n(o,a,t,s,r,i) else return nil,e end end local p=function(e) local e=e.conn; o=s(r,e,o) a=s(i,e,a) h[e]=nil end; local t=function(t,n,d) local e=t.conn h[e]=t if n~=nil then if n then a=l(i,e,a) else o=s(r,e,o) end end if d~=nil then if d then o=l(r,e,o) else a=s(i,e,a) end end end local a=function(e,a,i) local o=e if f(e)=="number"then o={getfd=function()return e;end} end local e={ conn=o; readbuffer=a or O; sendbuffer=i or O; close=p; setflags=t; }; t(e,a,i) return e end m"setmetatable"(h,{__mode="k"}) m"setmetatable"(v,{__mode="k"}) m"setmetatable"(w,{__mode="k"}) Q=P() local function t(e) local t=W; if e then W=e; end return t; end return{ _addtimer=X, add_task=u; addclient=b, wrapclient=n, watchfd=a, loop=Z, link=z, step=y, stats=ie, closeall=B, addserver=ne, getserver=oe, setlogger=t, getsettings=ae, setquitting=c, removeserver=he, get_backend=g, changesettings=re, } end) package.preload['util.xmppstream']=(function(...) local _ENV=_ENV; local function o(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local e=require"lxp"; local t=require"util.stanza"; local g=t.stanza_mt; local c=error; local t=tostring; local d=table.insert; local p=table.concat; local k=table.remove; local v=setmetatable; local q=pcall(e.new,{StartDoctypeDecl=false}); local x=pcall(e.new,{XmlDecl=false}); local a=not not e.new({}).getcurrentbytecount; local j=1024*1024*10; o"xmppstream" local b=e.new; local z={ ["http://www.w3.org/XML/1998/namespace\1lang"]="xml:lang"; ["http://www.w3.org/XML/1998/namespace\1space"]="xml:space"; ["http://www.w3.org/XML/1998/namespace\1base"]="xml:base"; ["http://www.w3.org/XML/1998/namespace\1id"]="xml:id"; }; local o="http://etherx.jabber.org/streams"; local r="\1"; local y="^([^"..r.."]*)"..r.."?(.*)$"; _M.ns_separator=r; _M.ns_pattern=y; local function h()end function new_sax_handlers(n,e,s) local i={}; local w=e.streamopened; local f=e.streamclosed; local l=e.error or function(o,a,e)c("XML stream error: "..t(a)..(e and": "..t(e)or""),2);end; local b=e.handlestanza; s=s or h; local t=e.stream_ns or o; local m=e.stream_tag or"stream"; if t~=""then m=t..r..m; end local j=t..r..(e.error_tag or"error"); local _=e.default_ns; local u={}; local h,e={}; local t=0; local r=0; function i:StartElement(c,o) if e and#h>0 then d(e,p(h)); h={}; end local h,i=c:match(y); if i==""then h,i="",h; end if h~=_ or r>0 then o.xmlns=h; r=r+1; end for t=1,#o do local e=o[t]; o[t]=nil; local t=z[e]; if t then o[t]=o[e]; o[e]=nil; end end if not e then if a then t=self:getcurrentbytecount(); end if n.notopen then if c==m then r=0; if w then if a then s(t); t=0; end w(n,o); end else l(n,"no-stream",c); end return; end if h=="jabber:client"and i~="iq"and i~="presence"and i~="message"then l(n,"invalid-top-level-element"); end e=v({name=i,attr=o,tags={}},g); else if a then t=t+self:getcurrentbytecount(); end d(u,e); local t=e; e=v({name=i,attr=o,tags={}},g); d(t,e); d(t.tags,e); end end if x then function i:XmlDecl(e,e,e) if a then s(self:getcurrentbytecount()); end end end function i:StartCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else s(self:getcurrentbytecount()); end end end function i:EndCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else s(self:getcurrentbytecount()); end end end function i:CharacterData(o) if e then if a then t=t+self:getcurrentbytecount(); end d(h,o); elseif a then s(self:getcurrentbytecount()); end end function i:EndElement(o) if a then t=t+self:getcurrentbytecount() end if r>0 then r=r-1; end if e then if#h>0 then d(e,p(h)); h={}; end if#u==0 then if a then s(t); end t=0; if o~=j then b(n,e); else l(n,"stream-error",e); end e=nil; else e=k(u); end else if f then f(n); end end end local function a(e) l(n,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1."); if not e.stop or not e:stop()then c("Failed to abort parsing"); end end if q then i.StartDoctypeDecl=a; end i.Comment=a; i.ProcessingInstruction=a; local function a() e,h,t=nil,{},0; u={}; end local function t(t,e) n=e; end return i,{reset=a,set_session=t}; end function new(i,n,t) local e=0; local o; if a then function o(a) e=e-a; end t=t or j; elseif t then c("Stanza size limits are not supported on this version of LuaExpat") end local i,s=new_sax_handlers(i,n,o); local o=b(i,r,false); local n=o.parse; return{ reset=function() o=b(i,r,false); n=o.parse; e=0; s.reset(); end, feed=function(s,i) if a then e=e+#i; end local o,i=n(o,i); if a and e>t then return nil,"stanza-too-large"; end return o,i; end, set_session=s.set_session; }; end return _M; end) package.preload['util.jid']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t,h=string.match,string.sub; local r=require"util.encodings".stringprep.nodeprep; local d=require"util.encodings".stringprep.nameprep; local l=require"util.encodings".stringprep.resourceprep; local n={ [" "]="\\20";['"']="\\22"; ["&"]="\\26";["'"]="\\27"; ["/"]="\\2f";[":"]="\\3a"; ["<"]="\\3c";[">"]="\\3e"; ["@"]="\\40";["\\"]="\\5c"; }; local s={}; for t,e in pairs(n)do s[e]=t;end a"jid" local function a(e) if not e then return;end local i,a=t(e,"^([^@/]+)@()"); local a,o=t(e,"^([^@/]+)()",a) if i and not a then return nil,nil,nil;end local t=t(e,"^/(.+)$",o); if(not a)or((not t)and#e>=o)then return nil,nil,nil;end return i,a,t; end split=a; function bare(e) local t,e=a(e); if t and e then return t.."@"..e; end return e; end local function o(e) local t,e,a=a(e); if e then if h(e,-1,-1)=="."then e=h(e,1,-2); end e=d(e); if not e then return;end if t then t=r(t); if not t then return;end end if a then a=l(a); if not a then return;end end return t,e,a; end end prepped_split=o; function prep(e) local t,e,a=o(e); if e then if t then e=t.."@"..e; end if a then e=e.."/"..a; end end return e; end function join(t,e,a) if t and e and a then return t.."@"..e.."/"..a; elseif t and e then return t.."@"..e; elseif e and a then return e.."/"..a; elseif e then return e; end return nil; end function compare(t,e) local n,o,i=a(t); local a,t,e=a(e); if((a~=nil and a==n)or a==nil)and ((t~=nil and t==o)or t==nil)and ((e~=nil and e==i)or e==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 _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=pairs; local o=table.insert; local n=table.sort; local s=setmetatable; local h=next; a"events" function new() local t={}; local e={}; local function r(s,a) local e=e[a]; if not e or h(e)==nil then return;end local t={}; for e in i(e)do o(t,e); end n(t,function(a,t)return e[a]>e[t];end); s[a]=t; return t; end; s(t,{__index=r}); local function s(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 n(o,i) local a=e[o]; if a then a[i]=nil; t[o]=nil; if h(a)==nil then e[o]=nil; end end end; local function h(e) for t,e in i(e)do s(t,e); end end; local function o(e) for e,t in i(e)do n(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=s; remove_handler=n; add_handlers=h; remove_handlers=o; fire_event=a; _handlers=t; _event_map=e; }; end return _M; end) package.preload['util.dataforms']=(function(...) local _ENV=_ENV; local function o(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=setmetatable; local e,i=pairs,ipairs; local n,s,c=tostring,type,next; local r=table.concat; local u=require"util.stanza"; local d=require"util.jid".prep; o"dataforms" local l='jabber:x:data'; local h={}; local t={__index=h}; function new(e) return a(e,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=r(t,"\n"); else a.value=t; end end end end return new(o); end function h.form(t,h,e) local e=u.stanza("x",{xmlns=l,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 a=o.type or"text-single"; e:tag("field",{type=a,var=o.name,label=o.label}); local t=(h and h[o.name])or o.value; if t then if a=="hidden"then if s(t)=="table"then e:tag("value") :add_child(t) :up(); else e:tag("value"):text(n(t)):up(); end elseif a=="boolean"then e:tag("value"):text((t and"1")or"0"):up(); elseif a=="fixed"then elseif a=="jid-multi"then for a,t in i(t)do e:tag("value"):text(t):up(); end elseif a=="jid-single"then e:tag("value"):text(t):up(); elseif a=="text-single"or a=="text-private"then e:tag("value"):text(t):up(); elseif a=="text-multi"then for t in t:gmatch("([^\r\n]+)\r?\n*")do e:tag("value"):text(t):up(); end elseif a=="list-single"then local a=false; for o,t in i(t)do if s(t)=="table"then e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up(); if t.default and(not a)then e:tag("value"):text(t.value):up(); a=true; end else e:tag("option",{label=t}):tag("value"):text(n(t)):up():up(); end end elseif a=="list-multi"then for a,t in i(t)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 h.data(t,n) local o={}; local a={}; for i,t in i(t)do local i; for e in n:childtags()do if t.name==e.attr.var then i=e; break; end end if not i then if t.required then a[t.name]="Required value missing"; end else local e=e[t.type]; if e then o[t.name],a[t.name]=e(i,t.required); end end end if c(a)then return o,a; end return o; 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 r(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=r(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 _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local d=require"util.encodings".base64.encode; local l=require"util.hashes".sha1; local n,s,h=table.insert,table.sort,table.concat; local r=ipairs; a"caps" function calculate_hash(e) local i,o,a={},{},{}; for t,e in r(e)do if e.name=="identity"then n(i,(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 s(a); if e.attr.var=="FORM_TYPE"then o=a[1]; elseif#a>0 then n(t,e.attr.var.."\0"..h(a,"<")); else n(t,e.attr.var); end end end s(t); t=h(t,"<"); if o then t=o.."\0"..t;end n(a,t); end end s(i); s(o); s(a); if#i>0 then i=h(i,"<"):gsub("%z","/").."<";else i="";end if#o>0 then o=h(o,"<").."<";else o="";end if#a>0 then a=h(a,"<"):gsub("%z","<").."<";else a="";end local e=i..o..a; local t=d(l(e)); return t,e; end return _M; end) package.preload['util.vcard']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"util.stanza"; local a,c=table.insert,table.concat; local h=type; local e,r,f=next,pairs,ipairs; local d,l,u,m; 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=n.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 o(t) local e=n.stanza("vCard",{xmlns="vcard-temp"}); for a=1,#t do e:add_child(p(t[a])); end return e; end function m(e) if not e[1]or e[1].name then return o(e) else local t=n.stanza("xCard",{xmlns="vcard-temp"}); for a=1,#e do t:add_child(o(e[a])); end return t; end end function d(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,o,i in t:gmatch("\30([^=]+)(=?)([^\30]*)")do e=e:upper(); local t={}; for e in i:gmatch("[^\31]+")do t[#t+1]=e t[e]=true; end if o=="="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 n(t) local e={}; for a=1,#t do e[a]=y(t[a]); end e=c(e,";"); local a=""; for t,e in r(t)do if h(t)=="string"and t~="name"then a=a..(";%s=%s"):format(t,h(e)=="table"and c(e,",")or e); end end return("%s%s:%s"):format(t.name,a,e) end local function o(t) local e={}; a(e,"BEGIN:VCARD") for o=1,#t do a(e,n(t[o])); end a(e,"END:VCARD") return c(e,w); end function l(e) if e[1]and e[1].name then return o(e) else local a={}; for t=1,#e do a[t]=o(e[t]); end return c(a,w); end end local function n(o) local t=o.name; local e=i[t]; local t={name=t}; if e=="text"then t[1]=o:get_text(); elseif h(e)=="table"then if e.value then t[1]=o:get_child_text(e.value)or""; elseif e.values then local e=e.values; if e.behaviour=="repeat-last"then for e=1,#o.tags do a(t,o.tags[e]:get_text()or""); end else for i=1,#e do a(t,o:get_child_text(e[i])or""); end end elseif e.names then local e=e.names; for a=1,#e do if o:get_child(e[a])then t[1]=e[a]; break; end end end if e.props_verbatim then for a,e in r(e.props_verbatim)do t[a]=e; end end if e.types then local e=e.types; t.TYPE={}; for i=1,#e do if o:get_child(e[i])then a(t.TYPE,e[i]:lower()); end end if#t.TYPE==0 then t.TYPE=nil; end end if e.props then local e=e.props; for i=1,#e do local e=e[i] local o=o:get_child_text(e); if o then t[e]=t[e]or{}; a(t[e],o); end end end else return nil end return t; 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 u(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=d; to_text=l; from_xep54=u; to_xep54=m; lua_to_text=l; lua_to_xep54=m; text_to_lua=d; text_to_xep54=function(...)return m(d(...));end; xep54_to_lua=u; xep54_to_text=function(...)return l(u(...))end; }; end) package.preload['util.logger']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local e=pcall; local e=string.find; local e,i,e=ipairs,pairs,setmetatable; local a={}; local e={}; local t; function a.init(e) local o=t(e,"debug"); local a=t(e,"info"); local i=t(e,"warn"); local n=t(e,"error"); return function(e,t,...) if e=="debug"then return o(t,...); elseif e=="info"then return a(t,...); elseif e=="warn"then return i(t,...); elseif e=="error"then return n(t,...); end end end function t(i,a) local t=e[a]; if not t then t={}; e[a]=t; end local e=function(o,...) for e=1,#t do t[e](i,a,o,...); end end return e; end function a.reset() for t,e in i(e)do for t=1,#e do e[t]=nil; end end end function a.add_level_sink(t,o) if not e[t]then e[t]={o}; else e[t][#e[t]+1]=o; end end a.new=t; return a; end) package.preload['util.datetime']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local e=os.date; local i=os.time; local u=os.difftime; local t=error; local r=tonumber; a"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(t) if t then local n,d,l,s,h,a,o; n,d,l,s,h,a,o=t: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 t=0; if o~=""and o~="Z"then local o,a,e=o:match("([+%-])(%d%d):?(%d*)"); if not o then return;end if#e~=2 then e="0";end a,e=r(a),r(e); t=a*60*60+e*60; if o=="-"then t=-t;end end a=(a+u)-t; return i({year=n,month=d,day=l,hour=s,min=h,sec=a,isdst=false}); end end end return _M; end) package.preload['util.json']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local y=type; local t,p,v,j=table.insert,table.concat,table.remove,table.sort; local s=string.char; local q,m=tostring,tonumber; local u,r=pairs,ipairs; local n=next; local e=error; local e,h,g=newproxy,getmetatable,setmetatable; local c=print; local a,o=pcall(require,"util.array"); local f=a and h(o())or{}; local a={}; local i=e and e(true)or{}; if h and h(i)then h(i).__tostring=function()return"null";end; end a.null=i; local w={ ["\""]="\\\"",["\\"]="\\\\",["\b"]="\\b", ["\f"]="\\f",["\n"]="\\n",["\r"]="\\r",["\t"]="\\t"}; local e={ ["\""]="\"",["\\"]="\\",["/"]="/", b="\b",f="\f",n="\n",r="\r",t="\t"}; for t=0,31 do local e=s(t); if not w[e]then w[e]=("\\u%.4X"):format(t);end end local function x(e) if e<128 then return s(e);end local t=e%64; if e<2048 then local e=(e-t)/64; return s(128+64+e,128+t); end local a=e%4096; local o=(a-t)/64; local e=(e-a)/4096; return s(128+64+32+e,128+o,128+t); end local k={ number=true, string=true, table=true, boolean=true }; local z={ __array=true; __hash=true; }; local o,b,d,l; function l(e,a) t(a,"\""..(e:gsub(".",w)).."\""); end function d(a,e) t(e,"["); if n(a)then for i,a in r(a)do o(a,e); t(e,","); end v(e); end t(e,"]"); end function b(c,e) local a={}; local h={}; local s={}; for e,t in r(c)do a[e]=t; end for e,t in u(c)do local o,n=y(e),y(t); if k[n]or t==i then if o=="string"and not z[e]then s[e]=t; elseif(k[o]or e==i)and a[e]==nil then h[e]=t; end end end if n(h)~=nil or n(s)~=nil or n(a)==nil then t(e,"{"); local c=#e; if e.ordered then local a={}; for e in u(s)do t(a,e); end j(a); for i,a in r(a)do l(a,e); t(e,":"); o(s[a],e); t(e,","); end else for i,a in u(s)do l(i,e); t(e,":"); o(a,e); t(e,","); end end if n(h)~=nil then t(e,"\"__hash\":["); for i,a in u(h)do o(i,e); t(e,","); o(a,e); t(e,","); end v(e); t(e,"]"); t(e,","); end if n(a)then t(e,"\"__array\":"); d(a,e); t(e,","); end if c~=#e then v(e);end t(e,"}"); else d(a,e); end end function o(e,a) local o=y(e); if o=="number"then t(a,q(e)); elseif o=="string"then l(e,a); elseif o=="table"then local t=h(e); if t==f then d(e,a); else b(e,a); end elseif o=="boolean"then t(a,(e and"true"or"false")); else t(a,"null"); end end function a.encode(t) local e={}; o(t,e); return p(e); end function a.encode_ordered(t) local e={ordered=true}; o(t,e); return p(e); end function a.encode_array(t) local e={}; d(t,e); return p(e); end local function o(t,e) return t:find("[^ \t\r\n]",e)or e; end local function d(e) local a=e.__array; if a then e.__array=nil; for o,a in r(a)do t(e,a); end end local a=e.__hash; if a then e.__hash=nil; local t; for o,a in r(a)do if t~=nil then e[t]=a;t=nil; else t=a; end end end return e; end local n,h; local function u(t,e) local s={}; while true do local a,i; e=o(t,e+1); if t:byte(e)~=34 then if t:byte(e)==125 then return s,e+1;end return nil,"key expected"; end a,e=h(t,e); if a==nil then return nil,e;end e=o(t,e); if t:byte(e)~=58 then return nil,"colon expected";end i,e=n(t,e+1); if i==nil then return nil,e;end s[a]=i; e=o(t,e); local t=t:byte(e); if t==125 then return d(s),e+1;end if t~=44 then return nil,"object eof";end end end local function l(i,e) local s={}; local h=e; while true do local a; a,e=n(i,e+1); if a==nil then if i:byte(h+1)==93 then return g(s,f),h+2;end return a,e; end t(s,a); e=o(i,e); local t=i:byte(e); if t==93 then return g(s,f),e+1;end if t~=44 then return nil,"array eof";end end end local t; local function e(e) local t,e=m(e:sub(3,6),16),m(e:sub(9,12),16); local e=t*1024+e-56613888; local o=e%64; e=(e-o)/64; local t=e%64; e=(e-t)/64; local a=e%64; e=(e-a)/64; return s(240+e,128+a,128+t,128+o); end local function s(e) e=e:match("%x%x%x%x",3); if e then return x(m(e,16)); end t=true; end function h(o,e) e=e+1; local a=o:find("\"",e,true); if a then local e=o:sub(e,a-1); t=nil; e=e:gsub("\\u.?.?.?.?",s); if t then return nil,"invalid escape";end return e,a+1; end return nil,"string eof"; end local function d(t,e) local t=t:match("[0-9%.%-eE%+]+",e); return m(t),e+#t; end local function s(t,e) local o,a,t=t:byte(e+1,e+3); if o==117 and a==108 and t==108 then return i,e+4; end return nil,"null parse failed"; end local function i(t,e) local t,a,o=t:byte(e+1,e+3); if t==114 and a==117 and o==101 then return true,e+4; end return nil,"true parse failed"; end local function r(t,e) local i,o,a,t=t:byte(e+1,e+4); if i==97 and o==108 and a==115 and t==101 then return false,e+5; end return nil,"false parse failed"; end function n(a,t) t=o(a,t); local e=a:byte(t); if e==123 then return u(a,t); elseif e==91 then return l(a,t); elseif e==34 then return h(a,t); elseif e~=nil and e>=48 and e<=57 or e==45 then return d(a,t); elseif e==110 then return s(a,t); elseif e==116 then return i(a,t); elseif e==102 then return r(a,t); else return nil,"value expected"; end end local t={ ["\\\""]="\\u0022"; ["\\\\"]="\\u005c"; ["\\/"]="\\u002f"; ["\\b"]="\\u0008"; ["\\f"]="\\u000C"; ["\\n"]="\\u000A"; ["\\r"]="\\u000D"; ["\\t"]="\\u0009"; ["\\u"]="\\u"; }; function a.decode(e) e=e:gsub("\\.",t) local t,a=n(e,1); if t==nil then return t,a;end if e:find("[^ \t\r\n]",a)then return nil,"garbage at eof";end return t; end function a.test(e) local e=a.encode(e); local t=a.decode(e); local t=a.encode(t); if e~=t then c("FAILED"); c("encoded:",e); c("recoded:",t); else c(e); end return e==t; end return a; end) package.preload['util.xml']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t=require"util.stanza"; local n=require"lxp"; a("xml") local e=(function() local s={ ["http://www.w3.org/XML/1998/namespace"]="xml"; }; local e="\1"; local i="^([^"..e.."]*)"..e.."?(.*)$"; return function(h) local o={}; local a=t.stanza("root"); function o:StartElement(t,e) local t,o=t:match(i); if o==""then t,o="",t; end if t~=""then e.xmlns=t; end for t=1,#e do local a=e[t]; e[t]=nil; local t,o=a:match(i); if o~=""then t=s[t]; if t then e[t..":"..o]=e[a]; e[a]=nil; end end end a:tag(o,e); end function o:CharacterData(e) a:text(e); end function o:EndElement(e) a:up(); end local n=n.new(o,"\1"); local e,i,o,t=n:parse(h); if e then e,i,o,t=n:parse();end if e then return a.tags[1]; else return e,i.." (line "..o..", col "..t..")"; end end; end)(); parse=e; return _M; end) package.preload['util.rsm']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"util.stanza".stanza; local a,o=tostring,tonumber; local s=type; local h=pairs; local i='http://jabber.org/protocol/rsm'; local t={}; do local e=t; local function t(e) return o((e:get_text())); end local function a(t) return t:get_text(); end e.after=a; e.before=function(e) local e=e:get_text(); return e==""or e; end; e.max=t; e.index=t; e.first=function(e) return{index=o(e.attr.index);e:get_text()}; end; e.last=a; e.count=t; end local r=setmetatable({ first=function(t,e) if s(e)=="table"then t:tag("first",{index=e.index}):text(e[1]):up(); else t:tag("first"):text(a(e)):up(); end end; before=function(e,t) if t==true then e:tag("before"):up(); else e:tag("before"):text(a(t)):up(); end end },{ __index=function(e,o) return function(t,e) t:tag(o):text(a(e)):up(); end end; }); local function o(e) local o={}; for a in e:childtags()do local e=a.name; local t=e and t[e]; if t then o[e]=t(a); end end return o; end local function s(e) local a=n("set",{xmlns=i}); for e,o in h(e)do if t[e]then r[e](a,o); end end return a; end local function t(e) local e=e:get_child("set",i); if e and#e.tags>0 then return o(e); end end return{parse=o,generate=s,get=t}; end) package.preload['util.random']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local e=io.open("/dev/urandom","r"); if e then return{ seed=function()end; bytes=function(t)return e:read(t);end }; end local e=require"crypto" return e.rand; end) package.preload['util.ip']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o={}; local i={__index=function(e,t)return(o[t])(e);end, __tostring=function(e)return e.addr;end, __eq=function(t,e)return t.addr==e.addr;end}; local n={["0"]="0000",["1"]="0001",["2"]="0010",["3"]="0011",["4"]="0100",["5"]="0101",["6"]="0110",["7"]="0111",["8"]="1000",["9"]="1001",["A"]="1010",["B"]="1011",["C"]="1100",["D"]="1101",["E"]="1110",["F"]="1111"}; local function e(t,e) if not e then local a=t:match("^%x+(.)"); if a==":"or(not(a)and t:sub(1,1)==":")then e="IPv6" elseif a=="."then e="IPv4" end if not e then return nil,"invalid address"; end elseif e~="IPv4"and e~="IPv6"then return nil,"invalid protocol"; end local a; if e=="IPv6"and t:find('%',1,true)then t,a=t:match("^(.-)%%(.*)"); end if e=="IPv6"and t:find('.',1,true)then local e; t,e=t:gsub(":(%d+)%.(%d+)%.(%d+)%.(%d+)$",function(t,e,o,a) return(":%04X:%04X"):format(t*256+e,o*256+a); end); if e~=1 then return nil,"invalid-address";end end return setmetatable({addr=t,proto=e,zone=a},i); end local function i(e) local t=""; local a={}; if e.proto=="IPv4"then e=e.toV4mapped; end e=(e.addr):upper(); e:gsub("([^:]*):?",function(e)a[#a+1]=e end); if not e:match(":$")then a[#a]=nil;end for o,e in ipairs(a)do if e:len()==0 and o~=1 and o~=#a then for e=1,16*(9-#a)do t=t.."0"; end else for e=1,4-e:len()do t=t.."0000"; end for a=1,e:len()do t=t..n[e:sub(a,a)]; end end end return t; end local function t(a,t) a,t=i(a),i(t); for e=1,128 do if a:sub(e,e)~=t:sub(e,e)then return e-1; end end return 128; end local function r(t) local e={}; t:gsub("([^.]*).?",function(t)e[#e+1]=tonumber(t)end); if e[1]==127 then return 2; elseif e[1]==169 and e[2]==254 then return 2; else return 14; end end local function h(e) if e:match("^[0:]*1$")then return 2; elseif e:match("^[Ff][Ee][89ABab]")then return 2; elseif e:match("^[Ff][Ee][CcDdEeFf]")then return 5; elseif e:match("^[Ff][Ff]")then return tonumber("0x"..e:sub(4,4)); else return 14; end end local function i(a) if t(a,e("::1","IPv6"))==128 then return 0; elseif t(a,e("2002::","IPv6"))>=16 then return 2; elseif t(a,e("2001::","IPv6"))>=32 then return 5; elseif t(a,e("fc00::","IPv6"))>=7 then return 13; elseif t(a,e("fec0::","IPv6"))>=10 then return 11; elseif t(a,e("3ffe::","IPv6"))>=16 then return 12; elseif t(a,e("::","IPv6"))>=96 then return 3; elseif t(a,e("::ffff:0:0","IPv6"))>=96 then return 4; else return 1; end end local function n(a) if t(a,e("::1","IPv6"))==128 then return 50; elseif t(a,e("2002::","IPv6"))>=16 then return 30; elseif t(a,e("2001::","IPv6"))>=32 then return 5; elseif t(a,e("fc00::","IPv6"))>=7 then return 3; elseif t(a,e("fec0::","IPv6"))>=10 then return 1; elseif t(a,e("3ffe::","IPv6"))>=16 then return 1; elseif t(a,e("::","IPv6"))>=96 then return 1; elseif t(a,e("::ffff:0:0","IPv6"))>=96 then return 35; else return 40; end end local function s(o) local a={}; local t="::ffff:"; o:gsub("([^.]*).?",function(e)a[#a+1]=tonumber(e)end); t=t..("%02x"):format(a[1]); t=t..("%02x"):format(a[2]); t=t..":" t=t..("%02x"):format(a[3]); t=t..("%02x"):format(a[4]); return e(t,"IPv6"); end function o:toV4mapped() if self.proto~="IPv4"then return nil,"No IPv4 address"end local e=s(self.addr); self.toV4mapped=e; return e; end function o:label() local e; if self.proto=="IPv4"then e=i(self.toV4mapped); else e=i(self); end self.label=e; return e; end function o:precedence() local e; if self.proto=="IPv4"then e=n(self.toV4mapped); else e=n(self); end self.precedence=e; return e; end function o:scope() local e; if self.proto=="IPv4"then e=r(self.addr); else e=h(self.addr); end self.scope=e; return e; end return{new_ip=e, commonPrefixLength=t}; end) package.preload['util.time']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end end) package.preload['util.sasl.scram']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n,m=require"mime".b64,require"mime".unb64; local e=require"util.hashes"; local a=require"bit"; local s=require"util.random"; local u=tonumber; local o,t=string.char,string.byte; local i=string.gsub; local a=a.bxor; local function c(n,e) return(i(n,"()(.)",function(n,i) return o(a(t(i),t(e,n))) end)); end local w,t=e.sha1,e.hmac_sha1; local function y(o,e,i) local e=t(o,e.."\0\0\0\1"); local a=e; for i=2,i do e=t(o,e); a=c(a,e); end return a; end local function f(e) return e; end local function a(e) return(i(e,"[,=]",{[","]="=2C",["="]="=3D"})); end local function d(e,o) local a="n="..a(e.username); local d=n(s.bytes(15)); local r="r="..d; local h=a..","..r; local i=""; local a=e.conn:ssl()and"y"or"n"; if o=="SCRAM-SHA-1-PLUS"then i=e.conn:socket():getfinished(); a="p=tls-unique"; end local s=a..",,"; local a=s..h; local a,l=coroutine.yield(a); if a~="challenge"then return false end local a,o,p=l:match("(r=[^,]+),s=([^,]*),i=(%d+)"); local u=u(p); o=m(o); if not a or not o or not u then return false,"Could not parse server_first_message"; elseif a:find(d,3,true)~=3 then return false,"nonce sent by server does not match our nonce"; elseif a==r then return false,"server did not append s-nonce to nonce"; end local i=s..i; local i="c="..n(i); local r=i..","..a; local a; local i; local s; if e.client_key and e.server_key then i=e.client_key; s=e.server_key; else if e.salted_password then a=e.salted_password; elseif e.password then a=y(f(e.password),o,u); end s=t(a,"Server Key"); i=t(a,"Client Key"); end local a=w(i); local e=h..","..l..","..r; local a=t(a,e); local a=c(i,a); local o=t(s,e); local e="p="..n(a); local e=r..","..e; local t,e=coroutine.yield(e); if t~="success"then return false,"success-expected"end local e=e:match("v=([^,]+)"); if m(e)~=o then return false,"server signature did not match"; end return true; end return function(e,t) if e.username and(e.password or(e.client_key or e.server_key))then if t=="SCRAM-SHA-1"then return d,99; elseif t=="SCRAM-SHA-1-PLUS"then local e=e.conn:ssl()and e.conn:socket(); if e and e.getfinished then return d,100; end end end end end) package.preload['util.sasl.plain']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end return function(e,t) if t=="PLAIN"and e.username and e.password then return function(e) return"success"==coroutine.yield((e.authzid or"").."\0"..e.username.."\0"..e.password); end,5; end end end) package.preload['util.sasl.anonymous']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end return function(t,e) if e=="ANONYMOUS"then return function() return coroutine.yield()=="success"; end,0; end end end) package.preload['verse.plugins.tls']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local t="urn:ietf:params:xml:ns:xmpp-tls"; function a.plugins.tls(e) local function i(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 library (LuaSec) not loaded, so TLS not available"); elseif not e.secure then e:debug("Server doesn't offer TLS :("); end end local function o(t) if t.name=="proceed"then e:debug("Server says proceed, handshake starting..."); e.conn:starttls(e.ssl or{mode="client",protocol="sslv23",options="no_sslv2",capath="/etc/ssl/certs"},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",i,400); e:hook("stream/"..t,o); e:hook("status",a,400); return true; end end) package.preload['verse.plugins.sasl']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"verse"; local s,h=require"mime".b64,require"mime".unb64; local a="urn:ietf:params:xml:ns:xmpp-sasl"; function n.plugins.sasl(e) local function r(t) if e.authenticated then return;end e:debug("Authenticating with SASL..."); local t=t:get_child("mechanisms",a); if not t then return end local o={}; local i={}; for t in t:childtags("mechanism")do t=t:get_text(); e:debug("Server offers %s",t); if not o[t]then local n=t:match("[^-]+"); local s,a=pcall(require,"util.sasl."..n:lower()); if s then e:debug("Loaded SASL %s module",n); o[t],i[t]=a(e,t); elseif not tostring(a):match("not found")then e:debug("Loading failed: %s",tostring(a)); end end end local t={}; for e in pairs(o)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(o[t]); i=e:sasl_mechanism(t); local t=n.stanza("auth",{xmlns=a,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,o=e.sasl_mechanism(t.name,h(t:get_text())); if not t then e:event("authentication-failure",{condition=o}); e:close(); return false; elseif t==true then e:event("authentication-success"); e.authenticated=true e:reopen(); else e:send(n.stanza("response",{xmlns=a}):text(s(t))); end return true; end e:hook("stream-features",r,300); e:hook("stream/"..a,i); return true; end end) package.preload['verse.plugins.bind']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end 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 t,a,o=t:get_error(); e:event("bind-failure",{error=a,text=o,type=t}); end end); end e:hook("stream-features",i,200); return true; end end) package.preload['verse.plugins.session']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local t="urn:ietf:params:xml:ns:xmpp-session"; function a.plugins.session(e) local function i(o) local o=o:get_child("session",t); if o and not o:get_child("optional")then local function o(o) e:debug("Establishing Session..."); e:send_iq(a.iq({type="set"}):tag("session",{xmlns=t}), function(t) if t.attr.type=="result"then e:event("session-success"); elseif t.attr.type=="error"then 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",o); end end e:hook("stream-features",i); return true; end end) package.preload['verse.plugins.legacy']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"verse"; local s=require"util.uuid".generate; local a="jabber:iq:auth"; function i.plugins.legacy(e) local function n(t) local o=t:get_child("query",a); if t.attr.type~="result"or not o then local t,a,o=t:get_error(); e:debug("warn","%s %s: %s",t,a,o); end local t={ username=e.username; password=e.password; resource=e.resource or s(); 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 local function a(t) if not t.version then e:send_iq(i.iq({type="get"}) :tag("query",{xmlns="jabber:iq:auth"}) :tag("username"):text(e.username), n); end end e:hook("opened",a); end end) package.preload['verse.plugins.compression']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t=require"verse"; local i=require"zlib"; local e="http://jabber.org/features/compress" local a="http://jabber.org/protocol/compress" local e="http://etherx.jabber.org/streams"; local o=9; local function s(e) local i,o=pcall(i.deflate,o); if i==false then local t=t.stanza("failure",{xmlns=a}):tag("setup-failed"); e:send(t); e:error("Failed to create zlib.deflate filter: %s",tostring(o)); return end return o end local function n(o) local i,e=pcall(i.inflate); if i==false then local t=t.stanza("failure",{xmlns=a}):tag("setup-failed"); o:send(t); o:error("Failed to create zlib.inflate filter: %s",tostring(e)); return end return e end local function i(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=t.stanza("failure",{xmlns=a}):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 n=e.data e.data=function(i,s) e:debug("Decompressing data..."); local s,o,h=pcall(o,s); if s==false then e:close({ condition="undefined-condition"; text=o; extra=t.stanza("failure",{xmlns=a}):tag("processing-failed"); }); stream:warn("%s",tostring(o)); return; end return n(i,o); end; end function t.plugins.compression(e) local function d(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(t.stanza("compress",{xmlns=a}):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 r(o) if o.name=="compressed"then e:debug("Activating compression...") local a=s(e); if not a then return end local t=n(e); if not t then return end i(e,a); h(e,t); e.compressed=true; e:reopen(); elseif o.name=="failure"then e:warn("Failed to establish compression"); end end e:hook("stream-features",d,250); e:hook("stream/"..a,r); end end) package.preload['verse.plugins.smacks']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"verse"; local h=require"socket".gettime; local s="urn:xmpp:sm:3"; function n.plugins.smacks(e) local t={}; local a=0; local r=h(); local o; local i=0; local function l(t) if t.attr.xmlns=="jabber:client"or not t.attr.xmlns then i=i+1; e:debug("Increasing handled stanzas to %d for %s",i,t:top_tag()); end end local function u(a) if a.name and not a.attr.xmlns then t[#t+1]=tostring(a); r=h(); if not o then o=true; e:debug("Waiting to send ack request..."); n.add_task(1,function() if#t==0 then o=false; return; end local a=h()-r; if a<1 and#t<10 then return 1-a; end e:debug("Time up, sending <r>..."); o=false; e:send(n.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; n.add_task(1,function() e:connect(e.connect_host or e.host,e.connect_port or 5222); end); return true; end end local function d() e.resumption_token=nil; e:unhook("disconnected",h); end local function r(o) if o.name=="r"then e:debug("Ack requested... acking %d handled stanzas",i); e:send(n.stanza("a",{xmlns=s,h=tostring(i)})); elseif o.name=="a"then local o=tonumber(o.attr.h); if o>a then local i=#t; for a=a+1,o do table.remove(t,1); end e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")"); a=o; else e:warn("Received bad ack for "..o.." when last ack was "..a); end elseif o.name=="enabled"then if o.attr.id then e.resumption_token=o.attr.id; e:hook("closed",d,100); e:hook("disconnected",h,100); end elseif o.name=="resumed"then local o=tonumber(o.attr.h); if o>a then local i=#t; for a=a+1,o do table.remove(t,1); end e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")"); a=o; 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.."/"..o.name); end end local function t() if not e.smacks then e:debug("smacks: sending enable"); e:send(n.stanza("enable",{xmlns=s,resume="true"})); e.smacks=true; e:hook("stanza",l); e:hook("outgoing",u); end end local function o(a) if a: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",i); e:send(n.stanza("resume",{xmlns=s, h=i,previd=e.resumption_token})); return true; else e:hook("bind-success",t,1); end end end e:hook("stream-features",o,250); e:hook("stream/"..s,r); end end) package.preload['verse.plugins.keepalive']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t=require"verse"; local e=require("mime").b64; local e=require("util.hashes").sha1; local s=require"util.caps".calculate_hash; local n="http://jabber.org/protocol/caps"; local e="http://jabber.org/protocol/disco"; local i=e.."#info"; local o=e.."#items"; function t.plugins.disco(e) e:add_plugin("presence"); local a={ __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 h={ __index=function(t,a) local e={}; t[a]=e; return e; end, }; e.disco={ cache={}, info=setmetatable({ [false]={ identities={ {category='client',type='pc',name='Verse'}, }, features={ [n]=true, [i]=true, [o]=true, }, }, },a); items=setmetatable({[false]={}},h); }; e.caps={} e.caps.node='http://code.matthewwild.co.uk/verse/' local function h(a) local o=e.disco.info[a or false]; if a and a==e.caps.node.."#"..e.caps.hash then o=e.disco.info[false]; end local n,o=o.identities,o.features local e=t.stanza("query",{ xmlns=i, node=a, }); for a,t in pairs(n)do e:tag('identity',t):up() end for t in pairs(o)do e:tag('feature',{var=t}):up() end return e; end setmetatable(e.caps,{ __call=function(...) local a=s(h()) e.caps.hash=a; return t.stanza('c',{ xmlns=n, hash='sha-1', node=e.caps.node, ver=a }) end }) function e:set_identity(a,t) self.disco.info[t or false].identities={a}; e:resend_presence(); end function e:add_identity(a,t) local t=self.disco.info[t or false].identities; t[#t+1]=a; e:resend_presence(); end function e:add_disco_feature(t,a) local t=t.var or t; self.disco.info[a or false].features[t]=true; e:resend_presence(); end function e:remove_disco_feature(t,a) local t=t.var or t; self.disco.info[a or false].features[t]=nil; e:resend_presence(); end function e:add_disco_item(t,e) local e=self.disco.items[e or false]; e[#e+1]=t; end function e:remove_disco_item(a,e) local e=self.disco.items[e or false]; for t=#e,1,-1 do if e[t]==a then table.remove(e,t); end end end function e:jid_has_identity(e,a,t) local o=self.disco.cache[e]; if not o then return nil,"no-cache"; end local e=self.disco.cache[e].identities; if t then return e[a.."/"..t]or false; end for e in pairs(e)do if e:match("^(.*)/")==a then return true; end end end function e:jid_supports(e,t) local e=self.disco.cache[e]; if not e or not e.features then return nil,"no-cache"; end return e.features[t]or false; end function e:get_local_services(o,a) local e=self.disco.cache[self.host]; if not(e)or not(e.items)then return nil,"no-cache"; end local t={}; for i,e in ipairs(e.items)do if self:jid_has_identity(e.jid,o,a)then table.insert(t,e.jid); end end return t; end function e:disco_local_services(a) self:disco_items(self.host,nil,function(t) if not t then return a({}); end local e=0; local function o() e=e-1; if e==0 then return a(t); end end for a,t in ipairs(t)do if t.jid then e=e+1; self:disco_info(t.jid,nil,o); end end if e==0 then return a(t); end end); end function e:disco_info(e,a,s) local t=t.iq({to=e,type="get"}) :tag("query",{xmlns=i,node=a}); self:send_iq(t,function(n) if n.attr.type=="error"then return s(nil,n:get_error()); end local o,t={},{}; for e in n:get_child("query",i):childtags()do if e.name=="identity"then o[e.attr.category.."/"..e.attr.type]=e.attr.name or true; elseif e.name=="feature"then t[e.attr.var]=true; end end if not self.disco.cache[e]then self.disco.cache[e]={nodes={}}; end if a then if not self.disco.cache[e].nodes[a]then self.disco.cache[e].nodes[a]={nodes={}}; end self.disco.cache[e].nodes[a].identities=o; self.disco.cache[e].nodes[a].features=t; else self.disco.cache[e].identities=o; self.disco.cache[e].features=t; end return s(self.disco.cache[e]); end); end function e:disco_items(a,i,n) local t=t.iq({to=a,type="get"}) :tag("query",{xmlns=o,node=i}); self:send_iq(t,function(e) if e.attr.type=="error"then return n(nil,e:get_error()); end local t={}; for e in e:get_child("query",o):childtags()do if e.name=="item"then table.insert(t,{ name=e.attr.name; jid=e.attr.jid; node=e.attr.node; }); end end if not self.disco.cache[a]then self.disco.cache[a]={nodes={}}; end if i then if not self.disco.cache[a].nodes[i]then self.disco.cache[a].nodes[i]={nodes={}}; end self.disco.cache[a].nodes[i].items=t; else self.disco.cache[a].items=t; end return n(t); end); end e:hook("iq/"..i,function(a) local o=a.tags[1]; if a.attr.type=='get'and o.name=="query"then local o=h(o.attr.node); local t=t.reply(a):add_child(o); e:send(t); return true end end); e:hook("iq/"..o,function(i) local a=i.tags[1]; if i.attr.type=='get'and a.name=="query"then local n=e.disco.items[a.attr.node or false]; local t=t.reply(i):tag('query',{ xmlns=o, node=a.attr.node }) for a=1,#n do t:tag('item',n[a]):up() end e:send(t); return true end end); local t; e:hook("ready",function() if t then return;end t=true; local function o(t) local a=e.disco.cache[t]; if a then for a in pairs(a.identities)do local o,a=a:match("^(.*)/(.*)$"); print(t,o,a) e:event("disco/service-discovered/"..o,{ type=a,jid=t; }); end end end e:disco_info(e.host,nil,function() o(e.host); end); e:disco_local_services(function(t) for a,t in ipairs(t)do o(t.jid); end e:event("ready"); end); return true; end,50); e:hook("presence-out",function(t) t:remove_children("c",n); t:reset():add_child(e:caps()):reset(); end,10); end end) package.preload['verse.plugins.version']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local a="jabber:iq:version"; local function i(t,e) t.name=e.name; t.version=e.version; t.platform=e.platform; end function o.plugins.version(e) e.version={set=i}; e:hook("iq/"..a,function(t) if t.attr.type~="get"then return;end local t=o.reply(t) :tag("query",{xmlns=a}); if e.version.name then t:tag("name"):text(tostring(e.version.name)):up(); end if e.version.version then t:tag("version"):text(tostring(e.version.version)):up() end if e.version.platform then t:tag("os"):text(e.version.platform); end e:send(t); return true; end); function e:query_version(e,t) t=t or function(e)return self:event("version/response",e);end self:send_iq(o.iq({type="get",to=e}) :tag("query",{xmlns=a}), function(o) if o.attr.type=="result"then local e=o:get_child("query",a); local o=e and e:get_child_text("name"); local a=e and e:get_child_text("version"); local e=e and e:get_child_text("os"); t({ name=o; version=a; platform=e; }); else local o,a,e=o:get_error(); t({ error=true; condition=a; text=e; type=o; }); end end); end return true; end end) package.preload['verse.plugins.ping']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local n=require"socket".gettime; local i="urn:xmpp:ping"; function a.plugins.ping(e) function e:ping(t,o) local s=n(); e:send_iq(a.iq{to=t,type="get"}:tag("ping",{xmlns=i}), function(e) if e.attr.type=="error"then local a,e,i=e:get_error(); if e~="service-unavailable"and e~="feature-not-implemented"then o(nil,t,{type=a,condition=e,text=i}); return; end end o(n()-s,t); end); end e:hook("iq/"..i,function(t) return e:send(a.reply(t)); end); return true; end end) package.preload['verse.plugins.uptime']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local t="jabber:iq:last"; local function a(e,t) e.starttime=t.starttime; end function o.plugins.uptime(e) e.uptime={set=a}; e:hook("iq/"..t,function(a) if a.attr.type~="get"then return;end local t=o.reply(a) :tag("query",{seconds=tostring(os.difftime(os.time(),e.uptime.starttime)),xmlns=t}); e:send(t); return true; end); function e:query_uptime(i,a) a=a or function(t)return e:event("uptime/response",t);end e:send_iq(o.iq({type="get",to=i}) :tag("query",{xmlns=t}), function(e) local t=e:get_child("query",t); if e.attr.type=="result"then local e=tonumber(t.attr.seconds); a({ seconds=e or nil; }); else local t,e,o=e:get_error(); a({ error=true; condition=e; text=o; type=t; }); end end); end return true; end end) package.preload['verse.plugins.blocking']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local o="urn:xmpp:blocking"; function a.plugins.blocking(e) e.blocking={}; function e.blocking:block_jid(i,t) e:send_iq(a.iq{type="set"} :tag("block",{xmlns=o}) :tag("item",{jid=i}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:unblock_jid(i,t) e:send_iq(a.iq{type="set"} :tag("unblock",{xmlns=o}) :tag("item",{jid=i}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:unblock_all_jids(t) e:send_iq(a.iq{type="set"} :tag("unblock",{xmlns=o}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:get_blocked_jids(t) e:send_iq(a.iq{type="get"} :tag("blocklist",{xmlns=o}) ,function(e) local a=e:get_child("blocklist",o); if not a then return t and t(false);end local e={}; for t in a:childtags()do e[#e+1]=t.attr.jid; end return t and t(e); end ,function(e)return t and t(false);end ); end end end) package.preload['verse.plugins.jingle']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local e=require"util.timer"; local n=require"util.uuid".generate; local i="urn:xmpp:jingle:1"; local h="urn:xmpp:jingle:errors:1"; local t={}; t.__index=t; local e={}; local e={}; function o.plugins.jingle(e) e:hook("ready",function() e:add_disco_feature(i); end,10); function e:jingle(a) return o.eventable(setmetatable(base or{ role="initiator"; peer=a; sid=n(); stream=e; },t)); end function e:register_jingle_transport(e) end function e:register_jingle_content_type(e) end local function u(n) local d=n:get_child("jingle",i); local a=d.attr.sid; local s=d.attr.action; local a=e:event("jingle/"..a,n); if a==true then e:send(o.reply(n)); return true; end if s~="session-initiate"then local t=o.error_reply(n,"cancel","item-not-found") :tag("unknown-session",{xmlns=h}):up(); e:send(t); return; end local l=d.attr.sid; local a=o.eventable{ role="receiver"; peer=n.attr.from; sid=l; stream=e; }; setmetatable(a,t); local r; local h,s; for t in d:childtags()do if t.name=="content"and t.attr.xmlns==i then local o=t:child_with_name("description"); local i=o.attr.xmlns; if i then local e=e:event("jingle/content/"..i,a,o); if e then h=e; end end local o=t:child_with_name("transport"); local i=o.attr.xmlns; s=e:event("jingle/transport/"..i,a,o); if h and s then r=t; break; end end end if not h then e:send(o.error_reply(n,"cancel","feature-not-implemented","The specified content is not supported")); return true; end if not s then e:send(o.error_reply(n,"cancel","feature-not-implemented","The specified transport is not supported")); return true; end e:send(o.reply(n)); a.content_tag=r; a.creator,a.name=r.attr.creator,r.attr.name; a.content,a.transport=h,s; function a:decline() end e:hook("jingle/"..l,function(e) if e.attr.from~=a.peer then return false; end local e=e:get_child("jingle",i); return a:handle_command(e); end); e:event("jingle",a); return true; end function t:handle_command(a) local t=a.attr.action; e:debug("Handling Jingle command: %s",t); if t=="session-terminate"then self:destroy(); elseif t=="session-accept"then self:handle_accepted(a); elseif t=="transport-info"then e:debug("Handling transport-info"); self.transport:info_received(a); elseif t=="transport-replace"then e:error("Peer wanted to swap transport, not implemented"); else e:warn("Unhandled Jingle command: %s",t); return nil; end return true; end function t:send_command(e,a,t) local e=o.iq({to=self.peer,type="set"}) :tag("jingle",{ xmlns=i, sid=self.sid, action=e, initiator=self.role=="initiator"and self.stream.jid or nil, responder=self.role=="responder"and self.jid or nil, }):add_child(a); if not t then self.stream:send(e); else self.stream:send_iq(e,t); end end function t:accept(a) local t=o.iq({to=self.peer,type="set"}) :tag("jingle",{ xmlns=i, sid=self.sid, action="session-accept", responder=e.jid, }) :tag("content",{creator=self.creator,name=self.name}); local o=self.content:generate_accept(self.content_tag:child_with_name("description"),a); t:add_child(o); local a=self.transport:generate_accept(self.content_tag:child_with_name("transport"),a); t:add_child(a); local a=self; e:send_iq(t,function(t) if t.attr.type=="error"then local a,t,a=t:get_error(); e:error("session-accept rejected: %s",t); return false; end a.transport:connect(function(t) e:warn("CONNECTED (receiver)!!!"); a.state="active"; a:event("connected",t); end); end); end e:hook("iq/"..i,u); return true; end function t:offer(t,a) local e=o.iq({to=self.peer,type="set"}) :tag("jingle",{xmlns=i,action="session-initiate", initiator=self.stream.jid,sid=self.sid}); e:tag("content",{creator=self.role,name=t}); local t=self.stream:event("jingle/describe/"..t,a); if not t then return false,"Unknown content type"; end e:add_child(t); local t=self.stream:event("jingle/transport/".."urn:xmpp:jingle:transports:s5b:1",self); self.transport=t; e:add_child(t:generate_initiate()); self.stream:debug("Hooking %s","jingle/"..self.sid); self.stream:hook("jingle/"..self.sid,function(e) if e.attr.from~=self.peer then return false; end local e=e:get_child("jingle",i); return self:handle_command(e) end); self.stream:send_iq(e,function(e) if e.attr.type=="error"then self.state="terminated"; local t,a,e=e:get_error(); return self:event("error",{type=t,condition=a,text=e}); end end); self.state="pending"; end function t:terminate(e) local e=o.stanza("reason"):tag(e or"success"); self:send_command("session-terminate",e,function(e) self.state="terminated"; self.transport:disconnect(); self:destroy(); end); end function t:destroy() self:event("terminated"); self.stream:unhook("jingle/"..self.sid,self.handle_command); end function t:handle_accepted(e) local e=e:child_with_name("transport"); self.transport:handle_accepted(e); self.transport:connect(function(e) self.stream:debug("CONNECTED (initiator)!") self.state="active"; self:event("connected",e); end); end function t:set_source(a,o) local function t() local e,i=a(); if e and e~=""then self.transport.conn:send(e); elseif e==""then return t(); elseif e==nil then if o then self:terminate(); end self.transport.conn:unhook("drained",t); a=nil; end end self.transport.conn:hook("drained",t); t(); end function t:set_sink(t) self.transport.conn:hook("incoming-raw",t); self.transport.conn:hook("disconnected",function(e) self.stream:debug("Closing sink..."); local e=e.reason; if e=="closed"then e=nil;end t(nil,e); end); end end) package.preload['verse.plugins.jingle_ft']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"verse"; local o=require"ltn12"; local s=package.config:sub(1,1); local t="urn:xmpp:jingle:apps:file-transfer:4"; function i.plugins.jingle_ft(e) e:hook("ready",function() e:add_disco_feature(t); end,10); local a={type="file"}; function a:generate_accept(t,e) if e and e.save_file then self.jingle:hook("connected",function() local e=o.sink.file(io.open(e.save_file,"w+")); self.jingle:set_sink(e); end); end return t; end local a={__index=a}; e:hook("jingle/content/"..t,function(t,e) local e=e:get_child("file"); local e={ name=e:get_child_text("name"); size=tonumber(e:get_child_text("size")); desc=e:get_child_text("desc"); date=e:get_child_text("date"); }; return setmetatable({jingle=t,file=e},a); end); e:hook("jingle/describe/file",function(e) local a; if e.timestamp then a=os.date("!%Y-%m-%dT%H:%M:%SZ",e.timestamp); end return i.stanza("description",{xmlns=t}) :tag("file") :tag("name"):text(e.filename):up() :tag("size"):text(tostring(e.size)):up() :tag("date"):text(a):up() :tag("desc"):text(e.description):up() :up(); end); function e:send_file(n,t) local e,a=io.open(t); if not e then return e,a;end local i=e:seek("end",0); e:seek("set",0); local a=o.source.file(e); local e=self:jingle(n); e:offer("file",{ filename=t:match("[^"..s.."]+$"); size=i; }); e:hook("connected",function() e:set_source(a,true); end); return e; end end end) package.preload['verse.plugins.jingle_s5b']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local o="urn:xmpp:jingle:transports:s5b:1"; local r="http://jabber.org/protocol/bytestreams"; local h=require"util.hashes".sha1; local s=require"util.uuid".generate; local function d(e,n) local function i() e:unhook("connected",i); 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 a(t) e:unhook("incoming-raw",a); if t~="\005\000"then local a="version-mismatch"; if t:sub(1,1)=="\005"then a="authentication-failure"; end return e:event("error",a); 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",i,200); e:hook("incoming-raw",a,100); e:send("\005\001\000"); end local function n(o,e,i) local e=a.new(nil,{ streamhosts=e, current_host=0; }); local function t(a) if a then return o(nil,a.reason); end if e.current_host<#e.streamhosts then e.current_host=e.current_host+1; e:debug("Attempting to connect to "..e.streamhosts[e.current_host].host..":"..e.streamhosts[e.current_host].port.."..."); local a,t=e:connect( e.streamhosts[e.current_host].host, e.streamhosts[e.current_host].port ); if not a then e:debug("Error connecting to proxy (%s:%s): %s", e.streamhosts[e.current_host].host, e.streamhosts[e.current_host].port, t ); else e:debug("Connecting..."); end d(e,i); return true; end e:unhook("disconnected",t); return o(nil); end e:hook("disconnected",t,100); e:hook("connected",function() e:unhook("disconnected",t); o(e.streamhosts[e.current_host],e); end,100); t(); return e; end function a.plugins.jingle_s5b(e) e:hook("ready",function() e:add_disco_feature(o); end,10); local t={}; function t:generate_initiate() self.s5b_sid=s(); local i=a.stanza("transport",{xmlns=o, mode="tcp",sid=self.s5b_sid}); local t=0; for a,o in pairs(e.proxy65.available_streamhosts)do t=t+1; i:tag("candidate",{jid=a,host=o.host, port=o.port,cid=a,priority=t,type="proxy"}):up(); end e:debug("Have %d proxies",t) return i; end function t:generate_accept(e) local t={}; self.s5b_peer_candidates=t; self.s5b_mode=e.attr.mode or"tcp"; self.s5b_sid=e.attr.sid or self.jingle.sid; for e in e:childtags()do t[e.attr.cid]={ type=e.attr.type; jid=e.attr.jid; host=e.attr.host; port=tonumber(e.attr.port)or 0; priority=tonumber(e.attr.priority)or 0; cid=e.attr.cid; }; end local e=a.stanza("transport",{xmlns=o}); return e; end function t:connect(i) e:warn("Connecting!"); local t={}; for a,e in pairs(self.s5b_peer_candidates or{})do t[#t+1]=e; end if#t>0 then self.connecting_peer_candidates=true; local function s(e,t) self.jingle:send_command("transport-info",a.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=t; end local e=h(self.s5b_sid..self.peer..e.jid,true); n(s,t,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 t:info_received(t) e:warn("Info received"); local s=t:child_with_name("content"); local i=s:child_with_name("transport"); if i:get_child("candidate-used")and not self.connecting_peer_candidates then local t=i:child_with_name("candidate-used"); if t then local function i(i,e) if self.jingle.role=="initiator"then self.jingle.stream:send_iq(a.iq({to=i.jid,type="set"}) :tag("query",{xmlns=r,sid=self.s5b_sid}) :tag("activate"):text(self.jingle.peer),function(i) if i.attr.type=="result"then self.jingle:send_command("transport-info",a.stanza("content",s.attr) :tag("transport",{xmlns=o,sid=self.s5b_sid}) :tag("activated",{cid=t.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[t.attr.cid]); local t={ self.jingle.stream.proxy65.available_streamhosts[t.attr.cid]; }; local e=h(self.s5b_sid..e.jid..self.peer,true); n(i,t,e); end elseif i:get_child("activated")then self.onconnect_callback(self.conn); end end function t:disconnect() if self.conn then self.conn:close(); end end function t:handle_accepted(e) end local t={__index=t}; 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local r=require"util.uuid"; local d=require"util.hashes".sha1; local n={}; n.__index=n; local i="http://jabber.org/protocol/bytestreams"; local s; function a.plugins.proxy65(t) t.proxy65=setmetatable({stream=t},n); t.proxy65.available_streamhosts={}; local e=0; t:hook("disco/service-discovered/proxy",function(o) if o.type=="bytestreams"then e=e+1; t:send_iq(a.iq({to=o.jid,type="get"}) :tag("query",{xmlns=i}),function(a) e=e-1; if a.attr.type=="result"then local e=a:get_child("query",i) :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/"..i,function(o) local e=a.new(nil,{ initiator_jid=o.attr.from, streamhosts={}, current_host=0; }); for t in o.tags[1]:childtags()do if t.name=="streamhost"then table.insert(e.streamhosts,t.attr); end end local function i() 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 ); s(t,e,o.tags[1].attr.sid,o.attr.from,t.jid); return true; end e:unhook("disconnected",i); t:send(a.error_reply(o,"cancel","item-not-found")); end function e:accept() e:hook("disconnected",i,100); e:hook("connected",function() e:unhook("disconnected",i); local e=a.reply(o) :tag("query",o.tags[1].attr) :tag("streamhost-used",{jid=e.streamhosts[e.current_host].jid}); t:send(e); end,100); i(); end function e:refuse() end t:event("proxy65/request",e); end); end function n:new(t,h) local e=a.new(nil,{ target_jid=t; bytestream_sid=r.generate(); }); local o=a.iq{type="set",to=t} :tag("query",{xmlns=i,mode="tcp",sid=e.bytestream_sid}); for t,e in ipairs(h or self.proxies)do o:tag("streamhost",e):up(); end self.stream:send_iq(o,function(o) if o.attr.type=="error"then local t,o,a=o:get_error(); e:event("connection-failed",{conn=e,type=t,condition=o,text=a}); else local o=o.tags[1]:get_child("streamhost-used"); e.streamhost_jid=o.attr.jid; local o,n; for a,t in ipairs(h or self.proxies)do if t.jid==e.streamhost_jid then o,n=t.host,t.port; break; end end e:connect(o,n); local function o() e:unhook("connected",o); local t=a.iq{to=e.streamhost_jid,type="set"} :tag("query",{xmlns=i,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); end end); return true; end e:hook("connected",o,100); s(self.stream,e,e.bytestream_sid,self.stream.jid,t); end end); return e; end function s(i,e,a,t,o) local t=d(a..t..o); local function a() e:unhook("connected",a); 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,#t)..t.."\0\0"); e:hook("incoming-raw",o,100); return true; end e:hook("connected",a,200); e:hook("incoming-raw",i,100); e:send("\005\001\000"); end end) package.preload['verse.plugins.jingle_ibb']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local e=require"verse"; local i=require"util.encodings".base64; local s=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 t={__index=a}; local function h(a) local t=setmetatable({stream=a},t) 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 incoming 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=h(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=s(); 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local s=table.insert; local i="http://jabber.org/protocol/pubsub"; local h="http://jabber.org/protocol/pubsub#owner"; local r="http://jabber.org/protocol/pubsub#event"; local e={}; local n={__index=e}; function o.plugins.pubsub(e) e.pubsub=setmetatable({stream=e},n); 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(a,o,t,e) return self:service(a):node(o):subscribe(t,nil,e); end function e:publish(i,o,a,t,e) return self:service(i):node(o):publish(a,nil,t,e); end local a={}; local t={__index=a}; function e:service(e) return setmetatable({stream=self.stream,service=e},t) end local function t(t,n,s,a,r,h,e) local t=o.iq{type=t or"get",to=n} :tag("pubsub",{xmlns=s or i}) if a then t:tag(a,{node=r,jid=h});end if e then t:tag("item",{id=e~=true and e or nil});end return t; end function a:subscriptions(o) self.stream:send_iq(t(nil,self.service,nil,"subscriptions") ,o and function(a) if a.attr.type=="result"then local e=a:get_child("pubsub",i); local e=e and e:get_child("subscriptions"); local a={}; 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; s(a,e); end end o(a); else o(false,a:get_error()); end end or nil); end function a:affiliations(e) self.stream:send_iq(t(nil,self.service,nil,"affiliations") ,e and function(t) if t.attr.type=="result"then local t=t:get_child("pubsub",i); local a=t and t:get_child("affiliations")or{}; local t={}; if a then for a in a:childtags("affiliation")do local e=self:node(a.attr.node) e.affiliation=a; s(t,e); end end e(t); else e(false,t: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 n:__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(a,e) if a~=nil then error("Not implemented yet."); else self.stream:send_iq(t("set",self.service,nil,"create",self.node),e); 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(i,a,o,e) 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,i or true) :add_child(o) ,e); 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) ,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(e,a) self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,e) ,a); 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(a,e) assert(not a,"Not implemented yet."); self.stream:send_iq(t("set",self.service,h,"purge",self.node) ,e); end function e:delete(a,e) assert(not a,"Not implemented yet."); self.stream:send_iq(t("set",self.service,h,"delete",self.node) ,e); end end) package.preload['verse.plugins.pep']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end 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,o,i) 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,o,i); 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,o) return e.pubsub:service(nil):node(a or t.attr.xmlns):publish(o or"current",nil,t) end end end) package.preload['verse.plugins.adhoc']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end 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,s,h) i[a]=n.new(o,a,s,h); 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(a) local e=o.iq({to=self.jid,type="set"}) :tag("command",{ xmlns=t, node=self.command, sessionid=self.sessionid }); if a then e:add_child(a);end self.stream:send_iq(e,function(e) self:_process_response(e); end); end end) package.preload['verse.plugins.presence']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; function a.plugins.presence(t) t.last_presence=nil; t:hook("presence-out",function(e) if not e.attr.to then t.last_presence=e; end end,1); function t:resend_presence() if self.last_presence then t:send(self.last_presence); end end function t:set_status(e) local a=a.presence(); if type(e)=="table"then if e.show then a:tag("show"):text(e.show):up(); end if e.priority or e.prio then a:tag("priority"):text(tostring(e.priority or e.prio)):up(); end if e.status or e.msg then a:tag("status"):text(e.status or e.msg):up(); end elseif type(e)=="string"then a:tag("status"):text(e):up(); end t:send(a); end end end) package.preload['verse.plugins.private']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t=require"verse"; local a="jabber:iq:private"; function t.plugins.private(o) function o:private_set(o,i,e,n) local t=t.iq({type="set"}) :tag("query",{xmlns=a}); 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 o:private_get(e,o,i) self:send_iq(t.iq({type="get"}) :tag("query",{xmlns=a}) :tag(e,{xmlns=o}), function(t) if t.attr.type=="result"then local t=t:get_child("query",a); local e=t:get_child(e,o); i(e); end end); end end end) package.preload['verse.plugins.roster']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"verse"; local d=require"util.jid".bare; local a="jabber:iq:roster"; local o="urn:xmpp:features:rosterver"; local n=table.insert; function i.plugins.roster(t) local s=false; local e={ items={}; ver=""; }; t.roster=e; t:hook("stream-features",function(e) if e:get_child("ver",o)then s=true; end end); local function h(e) local t=i.stanza("item",{xmlns=a}); for a,e in pairs(e)do if a~="groups"then t.attr[a]=e; else for a=1,#e do t:tag("group"):text(e[a]):up(); end end end return t; end local function r(o) local e={}; local a={}; e.groups=a; for t,a in pairs(o.attr)do if t~="xmlns"then e[t]=a end end for e in o:childtags("group")do n(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(o,n,s,e) local o={jid=o,name=n,groups=s}; local a=i.iq({type="set"}) :tag("query",{xmlns=a}) :add_child(h(o)); t:send_iq(a,function(t) if not e then return end if t.attr.type=="result"then e(true); else e(nil,t); end end); end function e:delete_contact(o,n) o=(type(o)=="table"and o.jid)or o; local s={jid=o,subscription="remove"} if not e.items[o]then return false,"item-not-found";end t:send_iq(i.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 n(nil,e); 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(n) t:send_iq(i.iq({type="get"}):tag("query",{xmlns=a,ver=s and e.ver or nil}), function(o) if o.attr.type=="result"then local t=o: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 n(e); else n(nil,o); end end); end t:hook("iq/"..a,function(n) local s,o=n.attr.type,n.attr.from; if s=="set"and(not o or o==d(t.jid))then local s=n:get_child("query",a); local o=s and s:get_child("item"); if o then local i,a; local n=o.attr.jid; if o.attr.subscription=="remove"then i="removed" a=r(n); else i=e.items[n]and"changed"or"added"; h(o) a=e.items[n]; end e.ver=s.attr.ver; if a then t:event("roster/item-"..i,a); end end t:send(i.reply(n)) return true; end end); end end) package.preload['verse.plugins.register']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end 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 a,t,o=t:get_error(); e:debug("Registration failed: %s",t); e:event("registration-failure",{type=a,condition=t,text=o}); 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"verse"; local e=require"util.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,r) 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(); if r then t:get_child("x",s):tag("password"):text(r):reset(); end 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(o,a,t,e) self:send(i.iq({type="set"}) :query(s.."#admin") :tag("item",{nick=o,[a]=t}) :tag("reason"):text(e or"")); end function a:set_role(e,a,t) self:admin_set(e,"role",a,t); end function a:set_affiliation(a,t,e) self:admin_set(a,"affiliation",t,e); 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"verse"; local o=require"util.vcard"; local e="vcard-temp"; function i.plugins.vcard(a) function a:get_vcard(n,t) a:send_iq(i.iq({to=n,type="get"}) :tag("vCard",{xmlns=e}),t and function(a) local e=a:get_child("vCard",e); if a.attr.type=="result"and e then e=o.from_xep54(e) t(e) else t(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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local n=require"verse"; local i="vcard-temp:x:update"; local s=require("util.hashes").sha1; 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; local function r(a) local o; for e=1,#a do if a[e].name=="PHOTO"then o=a[e][1]; break end end if o then local a=s(h(o),true); t=n.stanza("x",{xmlns=i}) :tag("photo"):text(a); e:resend_presence() else t=nil; end end local a; e:hook("ready",function() if a then return;end a=true; e:get_vcard(nil,function(t) if t then r(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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local a=require"verse"; local o="urn:xmpp:carbons:2"; local s="urn:xmpp:forward:0"; local n=os.time; local h=require"util.datetime".parse; local r=require"util.jid".bare; function a.plugins.carbons(e) local t={}; t.enabled=false; e.carbons=t; function t:enable(i) e:send_iq(a.iq{type="set"} :tag("enable",{xmlns=o}) ,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(a.iq{type="set"} :tag("disable",{xmlns=o}) ,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(a) local t=a:get_child(nil,o); if a.attr.from==i and t then local o=t.name; local t=t:get_child("forwarded",s); 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 h(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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local t=require"util.stanza"; local a="urn:xmpp:mam:2" local l="urn:xmpp:forward:0"; local d="urn:xmpp:delay"; local i=require"util.uuid".generate; local u=require"util.datetime".parse; local h=require"util.datetime".datetime; local e=require"util.dataforms".new; local r=require"util.rsm"; local c={}; local m=e{ {name="FORM_TYPE";type="hidden";value=a;}; {name="with";type="jid-single";}; {name="start";type="text-single"}; {name="end";type="text-single";}; }; function o.plugins.archive(o) function o:query_archive(o,e,n) local i=i(); local o=t.iq{type="set",to=o} :tag("query",{xmlns=a,queryid=i}); local t,s=tonumber(e["start"]),tonumber(e["end"]); e["start"]=t and h(t); e["end"]=s and h(s); o:add_child(m:form(e,"submit")); o:add_child(r.generate(e)); local t={}; local function s(e) local e=e:get_child("result",a); if e and e.attr.queryid==i then local a=e:get_child("forwarded",l); local o=e.attr.id; local e=a:get_child("delay",d); local e=e and u(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",s,1); self:send_iq(o,function(e) self:unhook("message",s); if e.attr.type=="error"then self:warn(table.concat({e:get_error()}," ")) n(false,e:get_error()) return true; end local e=e:get_child("fin",a) if e then local e=r.get(e); for a,e in pairs(e or c)do t[a]=e;end end n(t); return true end); end local i={ always=true,[true]="always", never=false,[false]="never", roster="roster", } local function s(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 n(o) local e e,o[false]=o[false],nil; if e~=nil then e=i[e]; end local a=t.stanza("prefs",{xmlns=a,default=e}) local e=t.stanza("always"); local t=t.stanza("never"); for o,a in pairs(o)do (a and e or t):tag("jid"):text(o):up(); end return a:add_child(e):add_child(t); end function o:archive_prefs_get(o) self:send_iq(t.iq{type="get"}:tag("prefs",{xmlns=a}), function(e) if e and e.attr.type=="result"and e.tags[1]then local t=s(e.tags[1]); o(t,e); else o(nil,e); end end); end function o:archive_prefs_set(e,a) self:send_iq(t.iq{type="set"}:add_child(n(e)),a); end end end) package.preload['util.http']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t,s=string.format,string.char; local d,n,o=pairs,ipairs,tonumber; local i,h=table.insert,table.concat; local function r(e) return e and(e:gsub("[^a-zA-Z0-9.~_-]",function(e)return t("%%%02x",e:byte());end)); end local function a(e) return e and(e:gsub("%%(%x%x)",function(e)return s(o(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 local function s(t) local a={}; if t[1]then for o,t in n(t)do i(a,e(t.name).."="..e(t.value)); end else for o,t in d(t)do i(a,e(o).."="..e(t)); end end return h(a,"&"); end local function n(e) if not e:match("=")then return a(e);end local o={}; for t,e in e:gmatch("([^=&]*)=([^&]*)")do t,e=t:gsub("%+","%%20"),e:gsub("%+","%%20"); t,e=a(t),a(e); i(o,{name=t,value=e}); o[t]=e; end return o; end local function t(e,t) e=","..e:gsub("[ \t]",""):lower()..","; return e:find(","..t:lower()..",",1,true)~=nil; end return{ urlencode=r,urldecode=a; formencode=s,formdecode=n; contains_token=t; }; end) package.preload['net.http.parser']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local w=tonumber; local a=assert; local b=require"socket.url".parse; local t=require"util.http".urldecode; local function g(e) e=t((e:gsub("//+","/"))); if e:sub(1,1)~="/"then e="/"..e; end local t=0; for e in e:gmatch("([^/]+)/")do if e==".."then t=t-1; elseif e~="."then t=t+1; end if t<0 then return nil; end end return e; end local y={}; function y.new(u,h,e,p) local d=true; if not e or e=="server"then d=false;else a(e=="client","Invalid parser type");end local e=""; local y,a,r; local s=nil; local t; local o; local c; local n; return{ feed=function(l,i) if n then return nil,"parse has failed";end if not i then if s and d and not o then t.body=e; u(t); elseif e~=""then n=true;return h(); end return; end e=e..i; while#e>0 do if s==nil then local m=e:find("\r\n\r\n",nil,true); if not m then return;end local u,r,l,i,v; local f; local a={}; for t in e:sub(1,m+1):gmatch("([^\r\n]+)\r\n")do if f then local e,t=t:match("^([^%s:]+): *(.*)$"); if not e then n=true;return h("invalid-header-line");end e=e:lower(); a[e]=a[e]and a[e]..","..t or t; else f=t; if d then l,i,v=t:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); i=w(i); if not i then n=true;return h("invalid-status-line");end c=not ((p and p().method=="HEAD") or(i==204 or i==304 or i==301) or(i>=100 and i<200)); else u,r,l=t:match("^(%w+) (%S+) HTTP/(1%.[01])$"); if not u then n=true;return h("invalid-status-line");end end end end if not f then n=true;return h("invalid-status-line");end y=c and a["transfer-encoding"]=="chunked"; o=w(a["content-length"]); if d then if not c then o=0;end t={ code=i; httpversion=l; headers=a; body=c and""or nil; responseversion=l; responseheaders=a; }; else local e; if r:byte()==47 then local a,t=r:match("([^?]*).?(.*)"); if t==""then t=nil;end e={path=a,query=t}; else e=b(r); if not(e and e.path)then n=true;return h("invalid-url");end end r=g(e.path); a.host=e.host or a.host; o=o or 0; t={ method=u; url=e; path=r; httpversion=l; headers=a; body=nil; }; end e=e:sub(m+4); s=true; end if s then if d then if y then if not e:find("\r\n",nil,true)then return; end if not a then a,r=e:match("^(%x+)[^\r\n]*\r\n()"); a=a and w(a,16); if not a then n=true;return h("invalid-chunk-size");end end if a==0 and e:find("\r\n\r\n",r-2,true)then s,a=nil,nil; e=e:gsub("^.-\r\n\r\n",""); u(t); elseif#e-r-2>=a then t.body=t.body..e:sub(r,r+(a-1)); e=e:sub(r+a+2); a,r=nil,nil; else break; end elseif o and#e>=o then if t.code==101 then t.body,e=e,""; else t.body,e=e:sub(1,o),e:sub(o+1); end s=nil;u(t); else break; end elseif#e>=o then t.body,e=e:sub(1,o),e:sub(o+1); s=nil;u(t); else break; end end end end; }; end return y; end) package.preload['net.http']=(function(...) local _ENV=_ENV; local function a(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local y=require"socket" local q=require"util.encodings".base64.encode; local r=require"socket.url" local d=require"net.http.parser".new; local h=require"util.http"; local x=pcall(require,"ssl"); local j=require"net.server" local l,o=table.insert,table.concat; local m=pairs; local v,c,p,b,s= tonumber,tostring,xpcall,select,debug.traceback; local g,k=assert,error local u=require"util.logger".init("http"); a"http" local i={}; local n={default_port=80,default_mode="*a"}; function n.onconnect(t) local e=i[t]; local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"}; if e.query then l(a,4,"?"..e.query); end t:write(o(a)); local a={[2]=": ",[4]="\r\n"}; for e,i in m(e.headers)do a[1],a[3]=e,i; t:write(o(a)); end t:write("\r\n"); if e.body then t:write(e.body); end end function n.onincoming(a,t) local e=i[a]; if not e then u("warn","Received response from connection %s with no request attached!",c(a)); return; end if t and e.reader then e:reader(t); end end function n.ondisconnect(t,a) local e=i[t]; if e and e.conn then e:reader(nil,a); end i[t]=nil; end function n.ondetach(e) i[e]=nil; end local function f(e,o,t) if not e.parser then local function a(t) if e.callback then e.callback(t or"connection-closed",0,e); e.callback=nil; end destroy_request(e); end if not o then a(t); return; end local function o(t) if e.callback then e.callback(t.body,t.code,t,e); e.callback=nil; end destroy_request(e); end local function t() return e; end e.parser=d(o,a,"client",t); end e.parser:feed(o); end local function w(e)u("error","Traceback[http]: %s",s(c(e),2));end function request(e,t,l) local e=r.parse(e); if not(e and e.host)then l(nil,0,e); return nil,"invalid-url"; end if not e.path then e.path="/"; end local d,o,s; local r,a=e.host,e.port; local h=r; if(a=="80"and e.scheme=="http") or(a=="443"and e.scheme=="https")then a=nil; elseif a then h=h..":"..a; end o={ ["Host"]=h; ["User-Agent"]="Prosody XMPP Server"; }; if e.userinfo then o["Authorization"]="Basic "..q(e.userinfo); end if t then e.onlystatus=t.onlystatus; s=t.body; if s then d="POST"; o["Content-Length"]=c(#s); o["Content-Type"]="application/x-www-form-urlencoded"; end if t.method then d=t.method;end if t.headers then for t,e in m(t.headers)do o[t]=e; end end end e.method,e.headers,e.body=d,o,s; local o=e.scheme=="https"; if o and not x then k("SSL not available, unable to contact https URL"); end local s=a and v(a)or(o and 443 or 80); local a=y.tcp(); a:settimeout(10); local d,h=a:connect(r,s); if not d and h~="timeout"then l(nil,0,e); return nil,h; end local h=false; if o then h=t and t.sslctx or{mode="client",protocol="sslv23",options={"no_sslv2","no_sslv3"}}; end e.handler,e.conn=g(j.wrapclient(a,r,s,n,"*a",h)); e.write=function(...)return e.handler:write(...);end e.callback=function(a,t,o,i)u("debug","Calling callback, status %s",t or"---");return b(2,p(function()return l(a,t,o,i)end,w));end e.reader=f; e.state="status"; i[e.handler]=e; return e; end function destroy_request(e) if e.conn then e.conn=nil; e.handler:close() end end local e,a=h.urlencode,h.urldecode; local t,o=h.formencode,h.formdecode; _M.urlencode,_M.urldecode=e,a; _M.formencode,_M.formdecode=t,o; return _M; end) package.preload['util.x509']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local i=require"util.encodings".stringprep.nameprep; local l=require"util.encodings".idna.to_ascii; local d=require"util.encodings".base64; local e=require"util.logger".init("x509"); local c=string.format; local _ENV=nil; local f="2.5.4.3"; local r="2.5.29.17"; local s="1.3.6.1.5.5.7.8.5"; local h="1.3.6.1.5.5.7.8.7"; local function n(a,o) local t=l(a) if t==nil then e("info","Host %s failed IDNA ToASCII operation",a) return false end t=t:lower() local i=t:gsub("^[^.]+%.","") for a=1,#o do local a=o[a] if t==a:lower()then e("debug","Cert dNSName %s matched hostname",a); return true end if a:match("^%*%.")then local t=a:gsub("^[^.]+%.","") if i==t:lower()then e("debug","Cert dNSName %s matched hostname",a); return true end end end return false end local function m(a,t) local o=i(a) for a=1,#t do local t=t[a] if t:match("[@/]")then e("debug","Ignoring xmppAddr %s because it's not a bare domain",t) else local a=i(t) if a==nil then e("info","Ignoring xmppAddr %s, failed nameprep!",t) else if o==a then e("debug","Cert xmppAddr %s matched hostname",t) return true end end end end return false end local function u(t,o,i) local a=l(t) if a==nil then e("info","Host %s failed IDNA ToASCII operation",t); return false end if o:match("^_")==nil then o="_"..o end a=a:lower(); local n=a:gsub("^[^.]+%.","") for t=1,#i do local i,t=i[t]:match("^(_[^.]+)%.(.*)"); if o==i then if a==t:lower()then e("debug","Cert SRVName %s matched hostname",t); return true; end if t:match("^%*%.")then local a=t:gsub("^[^.]+%.","") if n==a:lower()then e("debug","Cert SRVName %s matched hostname",t) return true end end if a==t:lower()then e("debug","Cert SRVName %s matched hostname",t); return true end end end return false end local function l(a,i,o) if o.setencode then o:setencode("utf8"); end local t=o:extensions() if t[r]then local e=t[r]; local t=false if e[s]then t=true if i=="_xmpp-client"or i=="_xmpp-server"then if m(a,e[s])then return true end end end if e[h]then t=true if i and u(a,i,e[h])then return true end end if e["dNSName"]then t=true if n(a,e["dNSName"])then return true end end if e["uniformResourceIdentifier"]then t=true end if t then return false end end local o=o:subject() local t=nil for a=1,#o do local a=o[a] if a["oid"]==f then if t then e("info","Certificate has multiple common names") return false end t=a["value"]; end end if t then return n(a,{t}) end return false end local e="%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. "([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-"; local function a(t) local t,e=t:match(e); if t and e then return d.decode(e),t; end end local o=('.'):rep(64); local n="-----BEGIN %s-----\n%s\n-----END %s-----\n" local function i(t,e) e=e and e:upper()or"CERTIFICATE"; t=d.encode(t); return c(n,e,t:gsub(o,'%0\n',(#t-1)/64),e); end return{ verify_identity=l; pem2der=a; der2pem=i; }; end) package.preload['verse.bosh']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local h=require"util.xmppstream".new; local i=require"util.stanza"; require"net.httpclient_listener"; local t=require"net.http"; local e=setmetatable({},{__index=verse.stream_mt}); e.__index=e; local s="http://etherx.jabber.org/streams"; local n="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]=i.clone(e); self:flush(); end function e:flush() if self.connected and#self.bosh_waiting_requests<self.bosh_max_requests and(#self.bosh_waiting_requests==0 or#self.bosh_outgoing_buffer>0 or self.bosh_need_restart)then self:debug("Flushing..."); local e=self:_make_body(); local t=self.bosh_outgoing_buffer; for o,a in ipairs(t)do e:add_child(a); t[o]=nil; end self:_make_request(e); else self:debug("Decided not to flush."); end end function e:_make_request(a) local e,t=t.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 e,a in ipairs(self.bosh_waiting_requests)do if a==t then table.remove(self.bosh_waiting_requests,e); 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'; t.request(self.bosh_url,{body=tostring(e)},function(t,e) if e==0 then return self:_disconnected(); end local e=self:_parse_response(t) 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(t,a,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(t); 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==s 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(e,t)e.payload:add_child(t);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=h(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=n; 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 _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local t=require"verse"; local o=t.stream_mt; local h=require"util.jid".split; local s=require"net.adns"; 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 d=require"util.xmppstream".new; local n="http://etherx.jabber.org/streams"; local function r(t,e) return t.priority<e.priority or(t.priority==e.priority and t.weight>e.weight); end local i={ stream_ns=n, stream_tag="stream", default_ns="jabber:client"}; function i.streamopened(e,t) e.stream_id=t.id; if not e:event("opened",t)then e.notopen=nil; end return true; end function i.streamclosed(e) e.notopen=true; if not e.closed then e:send("</stream:stream>"); e.closed=true; end e:event("closed"); return e:close("stream closed") end function i.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 i.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 o:reset() if self.stream then self.stream:reset(); else self.stream=d(self,i); end self.notopen=true; return true; end function o:connect_client(e,a) self.jid,self.password=e,a; self.username,self.host,self.resource=h(e); self:add_plugin("tls"); self:add_plugin("sasl"); self:add_plugin("bind"); self:add_plugin("session"); function self.data(t,e) local t,a=self.stream:feed(e); if t then return;end self:debug("Received invalid XML (%s) %d bytes: %s",tostring(a),#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 e=self.close; function self:close(t) self.close=e; if not self.closed then self:send("</stream:stream>"); self.closed=true; else return self:close(t); end end local function t() self:connect(self.connect_host or self.host,self.connect_port or 5222); end if not(self.connect_host or self.connect_port)then s.lookup(function(a) if a then local e={}; self.srv_hosts=e; for a,t in ipairs(a)do table.insert(e,t.srv); end table.sort(e,r); local a=e[1]; self.srv_choice=1; if a then self.connect_host,self.connect_port=a.target,a.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; t(); return true; end end,1e3); self:hook("connected",function() self.srv_hosts=nil; end,1e3); end t(); end,"_xmpp-client._tcp."..(self.host)..".","SRV"); else t(); end end function o: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 o:send_iq(e,a) local t=self:new_id(); self.tracked_iqs[t]=a; e.attr.id=t; self:send(e); end function o:new_id() self.curr_id=self.curr_id+1; return tostring(self.curr_id); end end) package.preload['verse.component']=(function(...) local _ENV=_ENV; local function e(t,...) local e=package.loaded[t]or _ENV[t]or{_NAME=t}; package.loaded[t]=e; for t=1,select("#",...)do (select(t,...))(e); end _ENV=e; _M=e; return e; end local o=require"verse"; local t=o.stream_mt; local d=require"util.jid".split; local e=require"lxp"; local a=require"util.stanza"; local h=require"util.hashes".sha1; o.message,o.presence,o.iq,o.stanza,o.reply,o.error_reply= a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply; local r=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 t:reset() if self.stream then self.stream:reset(); else self.stream=r(self,n); end self.notopen=true; return true; end function t:connect_component(e,n) self.jid,self.password=e,n; self.username,self.host,self.resource=d(e); function self.data(a,e) local o,a=self.stream:feed(e); if o then return;end t:debug("Received invalid XML (%s) %d bytes: %s",tostring(a),#e,e:sub(1,300):gsub("[\r\n]+"," ")); t: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 t; 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 a=e.tags[1]and e.tags[1].attr.xmlns; if a then t=self:event("iq/"..a,e); if not t then t=self:event("iq",e); end end if t==nil then self:send(o.error_reply(e,"cancel","service-unavailable")); return true; end else t=self:event(e.name,e); end end return t; end,-1); self:hook("opened",function(e) print(self.jid,self.stream_id,e.id); local e=h(self.stream_id..n,true); self:send(a.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 t:reopen() self:reset(); self:send(a.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams', xmlns=i,version="1.0"}):top_tag()); end function t:close(e) if not self.notopen then self:send("</stream:stream>"); end local t=self.conn.disconnect(); self.conn:close(); t(conn,e); end function t:send_iq(t,a) local e=self:new_id(); self.tracked_iqs[e]=a; t.attr.id=e; self:send(t); end function t:new_id() self.curr_id=self.curr_id+1; return tostring(self.curr_id); end end) pcall(require,"luarocks.require"); local n=require"socket"; pcall(require,"ssl"); local a=require"net.server"; local s=require"util.events"; local o=require"util.logger"; local e={}; e.server=a; local t={}; t.__index=t; e.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,...)..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=s.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(t,...) local e,a,o=0,{...},select('#',...); return(t:gsub("%%(.)",function(t)if e<=o then e=e+1;return tostring(a[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(e,a,o) t:write(e,"\t",a,"\t",o,"\n"); end end if e then local function n(o,a,t,...) return e(o,a,i(t,...)); end for t,e in ipairs(t)do o.add_level_sink(e,n); end end end function e._default_log_handler(t,a,o) return io.stderr:write(t,"\t",a,"\t",o,"\n"); end e.set_log_handler(e._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("once"); end function t:listen(o,t) o=o or"localhost"; t=t or 0; local e,a=a.addserver(o,t,e.new_listener(self,"server"),"*a"); if e then self:debug("Bound to %s:%s",o,t); self.server=e; end return e,a; end function t:connect(i,o) i=i or"localhost"; o=tonumber(o)or 5222; local n=n.tcp() n:settimeout(0); n:setoption("keepalive",true); local s,t=n:connect(i,o); if not s and t~="timeout"then self:warn("connect() to %s:%d failed: %s",i,o,t); return self:event("disconnected",{reason=t})or false,t; end local e=a.wrapclient(n,i,o,e.new_listener(self),"*a"); if not e then self:warn("connection initialisation failed: %s",t); return self:event("disconnected",{reason=t})or false,t; end self:set_conn(e); 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(e,t) return self.events.remove_handler(e,t); end function e.eventable(e) e.events=s.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 e,a=e.plugins[t](self); if e~=false then self:debug("Loaded %s plugin",t); self.plugins[t]=true; else self:warn("Failed to load %s plugin: %s",t,a); end end return self; end function e.new_listener(t) local a={}; function a.onconnect(o) if t.server then local a=e.new(); o:setlistener(e.new_listener(a)); a:set_conn(o); t:event("connected",{client=a}); 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;