verse.lua

Thu, 23 Mar 2023 18:28:20 +0000

author
Matthew Wild <mwild1@gmail.com>
date
Thu, 23 Mar 2023 18:28:20 +0000
changeset 181
3a9b9c98304a
parent 180
249197af6c01
permissions
-rw-r--r--

Add support for component connections

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 t(t,e)
error("Hash method "..e.." not available",2);
end
local e=setmetatable({},{__index=t});
local function t(t,o)
local e,a;
if t=="util.sha1"then
e,a=true,require(t)
else
e,a=pcall(require,t);
end
if e then o(a);end
end
local function i(e)
return(e:gsub(".",function(e)
return("%02x"):format(e:byte());
end));
end
t("util.sha1",function(t)
e.sha1=t.sha1;
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);
t("openssl.digest",function(o)
local function a(t)
return function(e,a)
local e=o.new(t):final(e);
if a then
return i(e);
end
return e;
end;
end
e.sha1=a("sha1");
end);
return e;
end)
package.preload['lib.adhoc']=(function(...)
local _ENV=_ENV;
local function r(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.id".short;
local e="http://jabber.org/protocol/commands";
local n={}
local h={};
local function o(i,o,t,a)
local e=s.stanza("command",{xmlns=e,node=i.node,status=o});
if t then e.attr.sessionid=t;end
if a then e.attr.action=a;end
return e;
end
function h.new(e,a,i,t)
return{name=e,node=a,handler=i,cmdtag=o,permission=(t or"user")};
end
function h.handle_cmd(o,h,e)
local t=e.tags[1].attr.sessionid or i();
local a={};
a.to=e.attr.to;
a.from=e.attr.from;
a.action=e.tags[1].attr.action or"execute";
a.form=e.tags[1]:child_with_ns("jabber:x:data");
local a,i=o:handler(a,n[t]);
n[t]=i;
local i=s.reply(e);
local e;
if a.status=="completed"then
n[t]=nil;
e=o:cmdtag("completed",t);
elseif a.status=="canceled"then
n[t]=nil;
e=o:cmdtag("canceled",t);
elseif a.status=="error"then
n[t]=nil;
i=s.error_reply(i,a.error.type,a.error.condition,a.error.message);
h.send(i);
return true;
else
e=o:cmdtag("executing",t);
end
for a,t in pairs(a)do
if a=="info"then
e:tag("note",{type="info"}):text(t):up();
elseif a=="warn"then
e:tag("note",{type="warn"}):text(t):up();
elseif a=="error"then
e:tag("note",{type="error"}):text(t.message):up();
elseif a=="actions"then
local a=s.stanza("actions");
for i,t in ipairs(t)do
if(t=="prev")or(t=="next")or(t=="complete")then
a:tag(t):up();
else
r:log("error",'Command "'..o.name..
'" at node "'..o.node..'" provided an invalid action "'..t..'"');
end
end
e:add_child(a);
elseif a=="form"then
e:add_child((t.layout or t):form(t.values));
elseif a=="result"then
e:add_child((t.layout or t):form(t.values,"result"));
elseif a=="other"then
e:add_child(t);
end
end
i:add_child(e);
h.send(i);
return true;
end
return h;
end)
package.preload['util.table']=(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{
pack=function(...)return{n=select("#",...);...}end;
create=function()return{}end;
move=function(o,t,i,a,e)
e=e or o;
if i>=t then
local n=(i-t)+1;
if a>i or a<=t or o~=e then
for i=0,n-1 do
e[a+i]=o[t+i];
end
else
for i=n-1,0,-1 do
e[a+i]=o[t+i];
end
end
end
return e;
end;
}
end)
package.preload['util.sha1']=(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=string.len
local a=string.char
local j=string.byte
local k=string.sub
local c=math.floor
local t=require"util.bit"
local q=t.bnot
local e=t.band
local y=t.bor
local n=t.bxor
local i=t.lshift
local o=t.rshift
local l,u,d,h,s
local function p(t,e)
return i(t,e)+o(t,32-e)
end
local function r(i)
local t,o
local t=""
for n=1,8 do
o=e(i,15)
if(o<10)then
t=a(o+48)..t
else
t=a(o+87)..t
end
i=c(i/16)
end
return t
end
local function b(t)
local i,o
local n=""
i=m(t)*8
t=t..a(128)
o=56-e(m(t),63)
if(o<0)then
o=o+64
end
for e=1,o do
t=t..a(0)
end
for t=1,8 do
n=a(e(i,255))..n
i=c(i/256)
end
return t..n
end
local function g(f)
local m,t,o,a,w,r,c,v
local i,i
local i={}
while(f~="")do
for e=0,15 do
i[e]=0
for t=1,4 do
i[e]=i[e]*256+j(f,e*4+t)
end
end
for e=16,79 do
i[e]=p(n(n(i[e-3],i[e-8]),n(i[e-14],i[e-16])),1)
end
m=l
t=u
o=d
a=h
w=s
for s=0,79 do
if(s<20)then
r=y(e(t,o),e(q(t),a))
c=1518500249
elseif(s<40)then
r=n(n(t,o),a)
c=1859775393
elseif(s<60)then
r=y(y(e(t,o),e(t,a)),e(o,a))
c=2400959708
else
r=n(n(t,o),a)
c=3395469782
end
v=p(m,5)+r+w+c+i[s]
w=a
a=o
o=p(t,30)
t=m
m=v
end
l=e(l+m,4294967295)
u=e(u+t,4294967295)
d=e(d+o,4294967295)
h=e(h+a,4294967295)
s=e(s+w,4294967295)
f=k(f,65)
end
end
local function t(e,t)
e=b(e)
l=1732584193
u=4023233417
d=2562383102
h=271733878
s=3285377520
g(e)
local e=r(l)..r(u)..r(d)
..r(h)..r(s);
if t then
return e;
else
return(e:gsub("..",function(e)
return string.char(tonumber(e,16));
end));
end
end
return{
sha1=t;
};
end)
package.preload['util.bit']=(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=type;
local h=tonumber;
local n=setmetatable;
local u=error;
local l=tostring;
local c={[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=0;[18]=3;[19]=2;[20]=5;[21]=4;[22]=7;[23]=6;[24]=9;[25]=8;[26]=11;[27]=10;[28]=13;[29]=12;[30]=15;[31]=14;[32]=2;[33]=3;[34]=0;[35]=1;[36]=6;[37]=7;[38]=4;[39]=5;[40]=10;[41]=11;[42]=8;[43]=9;[44]=14;[45]=15;[46]=12;[47]=13;[48]=3;[49]=2;[50]=1;[51]=0;[52]=7;[53]=6;[54]=5;[55]=4;[56]=11;[57]=10;[58]=9;[59]=8;[60]=15;[61]=14;[62]=13;[63]=12;[64]=4;[65]=5;[66]=6;[67]=7;[68]=0;[69]=1;[70]=2;[71]=3;[72]=12;[73]=13;[74]=14;[75]=15;[76]=8;[77]=9;[78]=10;[79]=11;[80]=5;[81]=4;[82]=7;[83]=6;[84]=1;[85]=0;[86]=3;[87]=2;[88]=13;[89]=12;[90]=15;[91]=14;[92]=9;[93]=8;[94]=11;[95]=10;[96]=6;[97]=7;[98]=4;[99]=5;[100]=2;[101]=3;[102]=0;[103]=1;[104]=14;[105]=15;[106]=12;[107]=13;[108]=10;[109]=11;[110]=8;[111]=9;[112]=7;[113]=6;[114]=5;[115]=4;[116]=3;[117]=2;[118]=1;[119]=0;[120]=15;[121]=14;[122]=13;[123]=12;[124]=11;[125]=10;[126]=9;[127]=8;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=0;[137]=1;[138]=2;[139]=3;[140]=4;[141]=5;[142]=6;[143]=7;[144]=9;[145]=8;[146]=11;[147]=10;[148]=13;[149]=12;[150]=15;[151]=14;[152]=1;[153]=0;[154]=3;[155]=2;[156]=5;[157]=4;[158]=7;[159]=6;[160]=10;[161]=11;[162]=8;[163]=9;[164]=14;[165]=15;[166]=12;[167]=13;[168]=2;[169]=3;[170]=0;[171]=1;[172]=6;[173]=7;[174]=4;[175]=5;[176]=11;[177]=10;[178]=9;[179]=8;[180]=15;[181]=14;[182]=13;[183]=12;[184]=3;[185]=2;[186]=1;[187]=0;[188]=7;[189]=6;[190]=5;[191]=4;[192]=12;[193]=13;[194]=14;[195]=15;[196]=8;[197]=9;[198]=10;[199]=11;[200]=4;[201]=5;[202]=6;[203]=7;[204]=0;[205]=1;[206]=2;[207]=3;[208]=13;[209]=12;[210]=15;[211]=14;[212]=9;[213]=8;[214]=11;[215]=10;[216]=5;[217]=4;[218]=7;[219]=6;[220]=1;[221]=0;[222]=3;[223]=2;[224]=14;[225]=15;[226]=12;[227]=13;[228]=10;[229]=11;[230]=8;[231]=9;[232]=6;[233]=7;[234]=4;[235]=5;[236]=2;[237]=3;[238]=0;[239]=1;[240]=15;[241]=14;[242]=13;[243]=12;[244]=11;[245]=10;[246]=9;[247]=8;[248]=7;[249]=6;[250]=5;[251]=4;[252]=3;[253]=2;[254]=1;[255]=0;};
local w={[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=1;[18]=3;[19]=3;[20]=5;[21]=5;[22]=7;[23]=7;[24]=9;[25]=9;[26]=11;[27]=11;[28]=13;[29]=13;[30]=15;[31]=15;[32]=2;[33]=3;[34]=2;[35]=3;[36]=6;[37]=7;[38]=6;[39]=7;[40]=10;[41]=11;[42]=10;[43]=11;[44]=14;[45]=15;[46]=14;[47]=15;[48]=3;[49]=3;[50]=3;[51]=3;[52]=7;[53]=7;[54]=7;[55]=7;[56]=11;[57]=11;[58]=11;[59]=11;[60]=15;[61]=15;[62]=15;[63]=15;[64]=4;[65]=5;[66]=6;[67]=7;[68]=4;[69]=5;[70]=6;[71]=7;[72]=12;[73]=13;[74]=14;[75]=15;[76]=12;[77]=13;[78]=14;[79]=15;[80]=5;[81]=5;[82]=7;[83]=7;[84]=5;[85]=5;[86]=7;[87]=7;[88]=13;[89]=13;[90]=15;[91]=15;[92]=13;[93]=13;[94]=15;[95]=15;[96]=6;[97]=7;[98]=6;[99]=7;[100]=6;[101]=7;[102]=6;[103]=7;[104]=14;[105]=15;[106]=14;[107]=15;[108]=14;[109]=15;[110]=14;[111]=15;[112]=7;[113]=7;[114]=7;[115]=7;[116]=7;[117]=7;[118]=7;[119]=7;[120]=15;[121]=15;[122]=15;[123]=15;[124]=15;[125]=15;[126]=15;[127]=15;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=8;[137]=9;[138]=10;[139]=11;[140]=12;[141]=13;[142]=14;[143]=15;[144]=9;[145]=9;[146]=11;[147]=11;[148]=13;[149]=13;[150]=15;[151]=15;[152]=9;[153]=9;[154]=11;[155]=11;[156]=13;[157]=13;[158]=15;[159]=15;[160]=10;[161]=11;[162]=10;[163]=11;[164]=14;[165]=15;[166]=14;[167]=15;[168]=10;[169]=11;[170]=10;[171]=11;[172]=14;[173]=15;[174]=14;[175]=15;[176]=11;[177]=11;[178]=11;[179]=11;[180]=15;[181]=15;[182]=15;[183]=15;[184]=11;[185]=11;[186]=11;[187]=11;[188]=15;[189]=15;[190]=15;[191]=15;[192]=12;[193]=13;[194]=14;[195]=15;[196]=12;[197]=13;[198]=14;[199]=15;[200]=12;[201]=13;[202]=14;[203]=15;[204]=12;[205]=13;[206]=14;[207]=15;[208]=13;[209]=13;[210]=15;[211]=15;[212]=13;[213]=13;[214]=15;[215]=15;[216]=13;[217]=13;[218]=15;[219]=15;[220]=13;[221]=13;[222]=15;[223]=15;[224]=14;[225]=15;[226]=14;[227]=15;[228]=14;[229]=15;[230]=14;[231]=15;[232]=14;[233]=15;[234]=14;[235]=15;[236]=14;[237]=15;[238]=14;[239]=15;[240]=15;[241]=15;[242]=15;[243]=15;[244]=15;[245]=15;[246]=15;[247]=15;[248]=15;[249]=15;[250]=15;[251]=15;[252]=15;[253]=15;[254]=15;[255]=15;};
local p={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=0;[9]=0;[10]=0;[11]=0;[12]=0;[13]=0;[14]=0;[15]=0;[16]=0;[17]=1;[18]=0;[19]=1;[20]=0;[21]=1;[22]=0;[23]=1;[24]=0;[25]=1;[26]=0;[27]=1;[28]=0;[29]=1;[30]=0;[31]=1;[32]=0;[33]=0;[34]=2;[35]=2;[36]=0;[37]=0;[38]=2;[39]=2;[40]=0;[41]=0;[42]=2;[43]=2;[44]=0;[45]=0;[46]=2;[47]=2;[48]=0;[49]=1;[50]=2;[51]=3;[52]=0;[53]=1;[54]=2;[55]=3;[56]=0;[57]=1;[58]=2;[59]=3;[60]=0;[61]=1;[62]=2;[63]=3;[64]=0;[65]=0;[66]=0;[67]=0;[68]=4;[69]=4;[70]=4;[71]=4;[72]=0;[73]=0;[74]=0;[75]=0;[76]=4;[77]=4;[78]=4;[79]=4;[80]=0;[81]=1;[82]=0;[83]=1;[84]=4;[85]=5;[86]=4;[87]=5;[88]=0;[89]=1;[90]=0;[91]=1;[92]=4;[93]=5;[94]=4;[95]=5;[96]=0;[97]=0;[98]=2;[99]=2;[100]=4;[101]=4;[102]=6;[103]=6;[104]=0;[105]=0;[106]=2;[107]=2;[108]=4;[109]=4;[110]=6;[111]=6;[112]=0;[113]=1;[114]=2;[115]=3;[116]=4;[117]=5;[118]=6;[119]=7;[120]=0;[121]=1;[122]=2;[123]=3;[124]=4;[125]=5;[126]=6;[127]=7;[128]=0;[129]=0;[130]=0;[131]=0;[132]=0;[133]=0;[134]=0;[135]=0;[136]=8;[137]=8;[138]=8;[139]=8;[140]=8;[141]=8;[142]=8;[143]=8;[144]=0;[145]=1;[146]=0;[147]=1;[148]=0;[149]=1;[150]=0;[151]=1;[152]=8;[153]=9;[154]=8;[155]=9;[156]=8;[157]=9;[158]=8;[159]=9;[160]=0;[161]=0;[162]=2;[163]=2;[164]=0;[165]=0;[166]=2;[167]=2;[168]=8;[169]=8;[170]=10;[171]=10;[172]=8;[173]=8;[174]=10;[175]=10;[176]=0;[177]=1;[178]=2;[179]=3;[180]=0;[181]=1;[182]=2;[183]=3;[184]=8;[185]=9;[186]=10;[187]=11;[188]=8;[189]=9;[190]=10;[191]=11;[192]=0;[193]=0;[194]=0;[195]=0;[196]=4;[197]=4;[198]=4;[199]=4;[200]=8;[201]=8;[202]=8;[203]=8;[204]=12;[205]=12;[206]=12;[207]=12;[208]=0;[209]=1;[210]=0;[211]=1;[212]=4;[213]=5;[214]=4;[215]=5;[216]=8;[217]=9;[218]=8;[219]=9;[220]=12;[221]=13;[222]=12;[223]=13;[224]=0;[225]=0;[226]=2;[227]=2;[228]=4;[229]=4;[230]=6;[231]=6;[232]=8;[233]=8;[234]=10;[235]=10;[236]=12;[237]=12;[238]=14;[239]=14;[240]=0;[241]=1;[242]=2;[243]=3;[244]=4;[245]=5;[246]=6;[247]=7;[248]=8;[249]=9;[250]=10;[251]=11;[252]=12;[253]=13;[254]=14;[255]=15;}
local y={[0]=15;[1]=14;[2]=13;[3]=12;[4]=11;[5]=10;[6]=9;[7]=8;[8]=7;[9]=6;[10]=5;[11]=4;[12]=3;[13]=2;[14]=1;[15]=0;};
local d={[0]=0;[1]=0;[2]=1;[3]=1;[4]=2;[5]=2;[6]=3;[7]=3;[8]=4;[9]=4;[10]=5;[11]=5;[12]=6;[13]=6;[14]=7;[15]=7;};
local r={[0]=0;[1]=8;[2]=0;[3]=8;[4]=0;[5]=8;[6]=0;[7]=8;[8]=0;[9]=8;[10]=0;[11]=8;[12]=0;[13]=8;[14]=0;[15]=8;};
local b={[0]=0;[1]=2;[2]=4;[3]=6;[4]=8;[5]=10;[6]=12;[7]=14;[8]=0;[9]=2;[10]=4;[11]=6;[12]=8;[13]=10;[14]=12;[15]=14;};
local v={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=1;[9]=1;[10]=1;[11]=1;[12]=1;[13]=1;[14]=1;[15]=1;};
local m={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=8;[9]=8;[10]=8;[11]=8;[12]=8;[13]=8;[14]=8;[15]=8;};
local i={__tostring=function(e)return("%x%x%x%x%x%x%x%x"):format(e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]);end};
local function s(a,t,e)
return n({
e[a[1]*16+t[1]];
e[a[2]*16+t[2]];
e[a[3]*16+t[3]];
e[a[4]*16+t[4]];
e[a[5]*16+t[5]];
e[a[6]*16+t[6]];
e[a[7]*16+t[7]];
e[a[8]*16+t[8]];
},i);
end
local function a(e,t)
return n({
t[e[1]];
t[e[2]];
t[e[3]];
t[e[4]];
t[e[5]];
t[e[6]];
t[e[7]];
t[e[8]];
},i);
end
local function f(t,e)return s(t,e,c);end
local function c(e,t)return s(e,t,w);end
local function w(e,t)return s(e,t,p);end
local function p(e)return a(e,y);end
local function s(t)
local a=0;
for e=1,8 do
local o=d[t[e]]+a;
a=r[t[e]];
t[e]=o;
end
end
local function y(e,t)
local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]};
for t=1,t do s(e);end
return n(e,i);
end
local function s(e)
local a=m[e[1]];
for t=1,8 do
local o=d[e[t]]+a;
a=r[e[t]];
e[t]=o;
end
end
local function r(e,t)
local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]};
for t=1,t do s(e);end
return n(e,i);
end
local function s(t)
local a=0;
for e=8,1,-1 do
local o=b[t[e]]+a;
a=v[t[e]];
t[e]=o;
end
end
local function d(e,t)
local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]};
for t=1,t do s(e);end
return n(e,i);
end
local function a(e)
if o(e)=="number"then e=("%x"):format(e);
elseif o(e)=="table"then return e;
elseif o(e)~="string"then u("string expected, got "..o(e),2);end
local a={0,0,0,0,0,0,0,0};
e="00000000"..e;
e=e:sub(-8);
for t=1,8 do
a[t]=h(e:sub(t,t),16)or u("Number format error",2);
end
return n(a,i);
end
local function t(t)
return function(e,...)
if o(e)~="table"then e=a(e);end
e=t(e,...);
e=h(l(e),16);
if e>2147483647 then e=e-1-4294967295;end
return e;
end;
end
local function i(i)
return function(e,t,...)
if o(e)~="table"then e=a(e);end
if o(t)~="table"then t=a(t);end
e=i(e,t,...);
e=h(l(e),16);
if e>2147483647 then e=e-1-4294967295;end
return e;
end;
end
return{
bits=32;
bxor=i(f);
bor=i(c);
band=i(w);
bnot=t(p);
lshift=t(d);
rshift=t(y);
arshift=t(r);
cast=t(a);
};
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 a=error;
local o=table.insert;
local l=table.remove;
local y=table.concat;
local d=string.match;
local p=tostring;
local m=setmetatable;
local q=getmetatable;
local h=pairs;
local s=ipairs;
local t=type;
local x=string.gsub;
local u=string.sub;
local r=string.find;
local c=table.move or require"util.table".move;
local w=require"util.table".create;
local i=require"util.encodings".utf8.valid;
local j,f=pcall(require,"util.termcolours");
local b="urn:ietf:params:xml:ns:xmpp-stanzas";
local v={xmlns=b};
local _ENV=nil;
local e={__name="stanza"};
e.__index=e;
local function n(e,t)
return not r(e,t and"[^\1\9\10\13\20-\255]"or"[^\9\10\13\20-\255]");
end
local function k(o,e)
if t(o)~="string"then
a("invalid "..e.." name: expected string, got "..t(o));
elseif#o==0 then
a("invalid "..e.." name: empty string");
elseif r(o,"[<>& '\"]")then
a("invalid "..e.." name: contains invalid characters");
elseif not n(o,e=="attribute")then
a("invalid "..e.." name: contains control characters");
elseif not i(o)then
a("invalid "..e.." name: contains invalid utf8");
end
end
local function g(e,o)
if t(e)~="string"then
a("invalid "..o.." value: expected string, got "..t(e));
elseif not n(e,false)then
a("invalid "..o.." value: contains control characters");
elseif not i(e)then
a("invalid "..o.." value: contains invalid utf8");
end
end
local function n(e)
if e~=nil then
if t(e)~="table"then
a("invalid attributes: expected table, got "..t(e));
end
for e,t in h(e)do
k(e,"attribute");
g(t,"attribute");
end
end
end
local function i(t,a,o)
k(t,"tag");
n(a);
local t={name=t,attr=a or{},namespaces=o,tags={}};
return m(t,e);
end
local function n(t)
return q(t)==e;
end
function e:query(e)
return self:tag("query",{xmlns=e});
end
function e:body(t,e)
return self:text_tag("body",t,e);
end
function e:text_tag(a,o,t,e)
return self:tag(a,t,e):text(o):up();
end
function e:tag(e,t,a)
local t=i(e,t,a);
local e=self.last_add;
if not e then e={};self.last_add=e;end
(e[#e]or self):add_direct_child(t);
o(e,t);
return self;
end
function e:text(e)
if e~=nil and e~=""then
local t=self.last_add;
(t and t[#t]or self):add_direct_child(e);
end
return self;
end
function e:up()
local e=self.last_add;
if e then l(e);end
return self;
end
function e:at_top()
return self.last_add==nil or#self.last_add==0
end
function e:reset()
self.last_add=nil;
return self;
end
function e:add_direct_child(e)
if n(e)then
o(self.tags,e);
o(self,e);
else
g(e,"text");
o(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,t)
t=t or self.attr.xmlns;
return self:maptags(function(e)
if(not a or e.name==a)and e.attr.xmlns==t then
return nil;
end
return e;
end);
end
function e:get_child(t,a)
for o,e in s(self.tags)do
if(not t or e.name==t)
and((not a and self.attr.xmlns==e.attr.xmlns)
or e.attr.xmlns==a)then
return e;
end
end
return nil;
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:get_child_attr(a,e,t)
local e=self:get_child(a,e);
if e then
return e.attr[t];
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
return nil;
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
return nil;
end
function e:get_child_with_attr(e,o,a,i,t)
for e in self:childtags(e,o)do
if(t and t(e.attr[a])or e.attr[a])==i then
return e;
end
end
return nil;
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 i,o=1,#e;
return function()
for o=i,o do
local e=e[o];
if(not t or e.name==t)
and((not a and self.attr.xmlns==e.attr.xmlns)
or e.attr.xmlns==a)then
i=o+1;
return e;
end
end
end;
end
function e:maptags(h)
local i,t=self.tags,1;
local n,o=#self,#i;
local s=n+1;
local e=1;
while t<=o and o>0 do
if self[e]==i[t]then
local a=h(self[e]);
if a==nil then
l(self,e);
l(i,t);
n=n-1;
o=o-1;
e=e-1;
t=t-1;
else
self[e]=a;
i[t]=a;
end
t=t+1;
end
e=e+1;
if e>s then
a("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=u(a,e,e);
if n=="@"then
return self.attr[u(a,e+1)];
elseif n=="{"then
o,e=d(a,"^([^}]+)}()",e+1);
end
t,i,e=d(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 function u(t,s)
local n={};
for t,e in h(t.attr)do n[t]=e;end
local a,i=t.namespaces;
if a then
i={};
for e,t in h(a)do i[e]=t;end
end
local o,a;
if s then
o={};
a={name=t.name,attr=n,namespaces=i,tags=o};
else
o=w(#t.tags,0);
a=w(#t,4);
a.name=t.name;
a.attr=n;
a.namespaces=i;
a.tags=o;
end
m(a,e);
if not s then
c(t,1,#t,1,a);
c(t.tags,1,#t.tags,1,o);
a:maptags(u);
end
return a;
end
local function l(e,o)
if not n(e)then
a("bad argument to clone: expected stanza, got "..t(e));
end
return u(e,o);
end
local u={["'"]="&apos;",["\""]="&quot;",["<"]="&lt;",[">"]="&gt;",["&"]="&amp;"};
local function m(e)return(x(e,"['&<>\"]",u));end
local function u(a,e,l,t,u)
local i=0;
local s=a.name
o(e,"<"..s);
for a,n in h(a.attr)do
if r(a,"\1",1,true)then
local s,a=d(a,"^([^\1]*)\1?(.*)$");
i=i+1;
o(e," xmlns:ns"..i.."='"..t(s).."' ".."ns"..i..":"..a.."='"..t(n).."'");
elseif not(a=="xmlns"and n==u)then
o(e," "..a.."='"..t(n).."'");
end
end
local i=#a;
if i==0 then
o(e,"/>");
else
o(e,">");
for i=1,i do
local i=a[i];
if i.name then
l(i,e,l,t,a.attr.xmlns);
else
o(e,t(i));
end
end
o(e,"</"..s..">");
end
end
function e.__tostring(t)
local e={};
u(t,e,u,m,nil);
return y(e);
end
function e.top_tag(e)
local e=l(e,true);
return p(e):sub(1,-3)..">";
end
function e.get_text(e)
if#e.tags==0 then
return y(e);
end
return nil;
end
function e.get_error(e)
local i,t,a,o;
local e=e:get_child("error");
if not e then
return nil,nil,nil,nil;
end
i=e.attr.type;
for i,e in s(e.tags)do
if e.attr.xmlns==b then
if not a and e.name=="text"then
a=e:get_text();
elseif not t then
t=e.name;
end
else
o=e;
end
if t and a and o then
break;
end
end
return i,t or"undefined-condition",a,o;
end
function e.add_error(o,a,s,h,i)
local e;
if t(a)=="table"then
if t(a.extra)=="table"then
e=a.extra;
end
if t(a.context)=="table"and t(a.context.by)=="string"then i=a.context.by;end
a,s,h=a.type,a.condition,a.text;
end
if o.attr.from==i then
i=nil;
end
o:tag("error",{type=a,by=i})
:tag(s,v);
if e and s=="gone"and t(e.uri)=="string"then
o:text(e.uri);
end
o:up();
if h then o:text_tag("text",h,v);end
if e and n(e.tag)then
o:add_child(e.tag);
elseif e and e.namespace and e.condition then
o:tag(e.condition,{xmlns=e.namespace}):up();
end
return o:up();
end
local function u(i)
local a={name=i.name,attr=i.attr};
for i,e in s(i)do
if t(e)=="table"then
o(a,u(e));
else
o(a,e);
end
end
return a;
end
e.__freeze=u;
local function c(a)
if a then
local e=a.attr;
local o={};
for e,a in h(e)do
if t(e)=="string"then
if r(e,"|",1,true)and not r(e,"\1",1,true)then
local e,t=d(e,"^([^|]+)|(.+)$");
o[e.."\1"..t]=a;
else
o[e]=a;
end
end
end
local o=i(a.name,o);
for a,e in s(a)do
if t(e)=="table"then
o:add_direct_child(c(e));
elseif t(e)=="string"then
o:add_direct_child(e);
end
end
return o;
end
end
local function v(t,e)
if not e then
return i("message",t);
else
return i("message",t):text_tag("body",e);
end
end
local function w(e)
if not e then
a("iq stanzas require id and type attributes");
end
if not e.id then
a("iq stanzas require an id attribute");
end
if not e.type then
a("iq stanzas require a type attribute");
end
return i("iq",e);
end
local function h(e)
if not n(e)then
a("bad argument to reply: expected stanza, got "..t(e));
end
return i(e.name,
{
to=e.attr.from,
from=e.attr.to,
id=e.attr.id,
type=((e.name=="iq"and"result")or e.attr.type)
});
end
local function y(e,r,o,i,s)
if not n(e)then
a("bad argument to error_reply: expected stanza, got "..t(e));
elseif e.attr.type=="error"then
a("bad argument to error_reply: got stanza of type error which must not be replied to");
end
local e=h(e);
e.attr.type="error";
e:add_error(r,o,i,s);
e.last_add={e[1]};
return e;
end
local function d(e)
return i("presence",e);
end
local s;
if j then
local a,t=f.getstyle,f.getstring;
local h=a("1b3967");
local r=a("13b5ea");
local o=a("439639");
local n=a("a0ce67");
local i=a("d9541e");
local a=a("e96d1f");
local a=(
t(n,"%1")..
t(o,"%2")..
t(i,"%3")..
t(a,"%4")..
t(i,"%5")
);
local o=(
t(o,"%1")..
t(n,"%2")..
t(o,"%3")
);
function s(e)
return(e:gsub("(<[?/]?)([^ >/?]*)(.-)([?/]?>)([^<]*)",function(e,i,s,n,d)
return t(h,e)..t(r,i)..
s:gsub("([^=]+)(=)([\"'])(.-)([\"'])",a)..
t(h,n)..
d:gsub("(&#?)(%w+)(;)",o);
end,100));
end
function e.pretty_print(e)
return s(p(e));
end
function e.pretty_top_tag(e)
return s(e:top_tag());
end
else
e.pretty_print=e.__tostring;
e.pretty_top_tag=e.top_tag;
end
function e.indent(i,e,a)
if#i==0 or(#i==1 and t(i[1])=="string")then
return i;
end
a=a or"\t";
e=e or 1;
local o=l(i,true);
for i in i:children()do
if t(i)=="string"then
if i:find("%S")then
o:text("\n"..a:rep(e));
o:text(i);
end
elseif n(i)then
o:text("\n"..a:rep(e));
o:add_direct_child(i:indent(e+1,a));
end
end
o:text("\n"..a:rep((e-1)));
return o;
end
return{
stanza_mt=e;
stanza=i;
is_stanza=n;
preserialize=u;
deserialize=c;
clone=l;
message=v;
iq=w;
reply=h;
error_reply=y;
presence=d;
xml_escape=m;
pretty_print=s;
};
end)
package.preload['util.timer']=(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"util.indexedbheap";
local t=require"util.logger".init("timer");
local e=require"net.server";
local d=require"util.time".now
local m=type;
local i=debug.traceback;
local r=tostring;
local f=require"util.xpcall".xpcall;
local c=math.max;
local u=pairs;
if e.timer then
return e.timer;
end
local _ENV=nil;
local h=e.add_task;
local o;
local s=0;
local a=a.create();
local n={};
local e=nil;
local function l(e)t("error","Traceback[timer]: %s",i(r(e),2));end
local function r(o)
local t;
local i;
while true do
t=a:peek();
if t==nil or t>o then break;end
local a,t,e=a:pop();
local s=n[e];
n[e]=nil;
local h,a=f(t,l,o,e,s);
if h and m(a)=="number"then
if i then
i[e]={t,a+o};
else
i={[e]={t,a+o}};
end
n[e]=s;
end
end
if i then
for o,e in u(i)do
a:insert(e[1],e[2],o);
end
t=a:peek();
end
if t~=nil and s>1 and t==e then
t=nil;
else
e=t;
end
if t then
return t-o;
end
s=s-1;
end
local function l(t,l,u)
local i=d();
local t=i+t;
local a=a:insert(l,t);
n[a]=u;
if e==nil or t<e then
e=t;
if o then
o:close();
o=nil;
else
s=s+1;
end
o=h(e-i,r);
end
return a;
end
local function u(t)
n[t]=nil;
local s,n,i=a:remove(t);
local t=a:peek();
if t~=e and o then
e=t;
o:close();
if e~=nil then
o=h(c(e-d(),0),r);
end
end
return s,n,i;
end
local function s(o,i)
local n=d();
local t=n+i;
a:reprioritize(o,i);
if e==nil or t<e then
e=t;
h(e-n,r);
end
return o;
end
return{
add_task=l;
stop=u;
reschedule=s;
};
end)
package.preload['util.termcolours']=(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 r,h=table.concat,table.insert;
local t,s=string.char,string.format;
local o=tonumber;
local c=ipairs;
local m=io.write;
local i=math.floor;
local u=type;
local d=setmetatable;
local l=pairs;
local a;
if os.getenv("WINDIR")then
a=require"util.windows";
end
local n=a and a.get_consolecolor and a.get_consolecolor();
local _ENV=nil;
local e={
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 y={
["0"]=n,
["1"]=7+8,
["1;33"]=2+4+8,
["1;31"]=4+8
}
local f={
[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 w=t(27).."[%sm%s"..t(27).."[0m";
local function p(t,e)
if t then
return s(w,t,e);
else
return e;
end
end
local function w(e)
return i(e*3/32)+232;
end
local function v(e,t,a)
if e==t and t==a then
return w(e);
end
e=i(e*3/128);
t=i(t*3/128);
a=i(a*3/128);
return 16+(e*36)+(t*6)+(a);
end
local function t(e)
local t=o(e:sub(1,2),16);
local a=o(e:sub(3,4),16);
local e=o(e:sub(5,6),16);
return t,a,e;
end
d(e,{__index=function(a,e)
if u(e)=="string"and e:find("%x%x%x%x%x%x")==1 then
local a=e:sub(7)==" background"and"48;5;"or"38;5;";
return s("%s%d",a,v(t(e)));
end
end});
local t={
red="ff0000";fuchsia="ff00ff";green="008000";white="ffffff";
lime="00ff00";yellow="ffff00";purple="800080";blue="0000ff";
aqua="00ffff";olive="808000";black="000000";navy="000080";
teal="008080";silver="c0c0c0";maroon="800000";gray="808080";
}
for t,a in l(t)do
e[t]=e[t]or e[a];
t,a=t.." background",a.." background"
e[t]=e[t]or e[a];
end
local function s(...)
local t,a={...},{};
for o,t in c(t)do
t=e[t];
if t then
h(a,t);
end
end
return r(a,";");
end
local t="0";
local function i(e)
e=e or"0";
if e~=t then
m("\27["..e.."m");
t=e;
end
end
if a then
function i(e)
e=e or"0";
if e~=t then
a.set_consolecolor(y[e]or n);
t=e;
end
end
if not n then
function i()end
end
end
local function a(t)
if t=="0"then return"</span>";end
local e={};
for t in t:gmatch("[^;]+")do
h(e,f[o(t)]);
end
return"</span><span style='"..r(e,";").."'>";
end
local function t(e)
return e:gsub("\027%[(.-)m",a);
end
return{
getstring=p;
getstyle=s;
setstyle=i;
tohtml=t;
};
end)
package.preload['util.uuid']=(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"util.random";
local a=t.bytes;
local o=require"util.hex".encode;
local i=math.ceil;
local function e(e)
return o(a(i(e/2))):sub(1,e);
end
local function o()
return("%x"):format(a(1):byte()%4+8);
end
local function a()
return e(8).."-"..e(4).."-4"..e(3).."-"..(o())..e(3).."-"..e(12);
end
return{
get_nibbles=e;
generate=a;
seed=t.seed;
};
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
local a=require"socket".gettime;
local e;
local t,o=pcall(require,"system");
if t then
e=o.monotime;
end
return{
now=a;
monotonic=e;
}
end)
package.preload['util.envload']=(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=load;
local n=io.open;
local function s(o,a,e)
return t(o,a,nil,e);
end
local function i(a,i)
local e,n,o=n(a);
if not e then return e,n,o;end
local a,t=t(e:lines(2048),"@"..a,nil,i);
e:close();
return a,t;
end
return{envload=s,envloadfile=i};
end)
package.preload['util.id']=(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=string.gsub;
local t=require"util.random".bytes;
local a=require"util.encodings".base64.encode;
local i={["+"]="-",["/"]="_",["="]=""};
local function e(e)
return(o(a(t(e)),"[+/=]",i));
end
return{
tiny=function()return e(3);end;
short=function()return e(9);end;
medium=function()return e(18);end;
long=function()return e(27);end;
custom=function(t)
return function()return e(t);end;
end;
}
end)
package.preload['util.serialization']=(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 u=getmetatable;
local e,o=next,type;
local n=string.format;
local p=string.gsub;
local y=string.rep;
local a=string.char;
local g=string.match;
local H=table.concat;
local S=require"util.hex".to;
local N=pcall;
local v=require"util.envload".envload;
if not math.type then
require"util.mathcompat"
end
local z,A=math.huge,-math.huge;
local j=math.type;
local function l(t)
return e,t,nil;
end
local function h(t,e)
error("Can't serialize "..o(t)..(e and": "..e or""));
end
local function t(t,e)
return n("{__type=%q,__error=%q}",o(t),e or"fail");
end
local r={
['\a']=[[\a]];['\b']=[[\b]];
['\f']=[[\f]];['\n']=[[\n]];
['\r']=[[\r]];['\t']=[[\t]];
['\v']=[[\v]];['\\']=[[\\]];
['\"']=[[\"]];['\'']=[[\']];
}
for t=0,255 do
local e=a(t);
if not r[e]then
r[e]=n("\\%03d",t);
end
end
local a={
["do"]=true;["and"]=true;["else"]=true;["break"]=true;
["if"]=true;["end"]=true;["goto"]=true;["false"]=true;
["in"]=true;["for"]=true;["then"]=true;["local"]=true;
["or"]=true;["nil"]=true;["true"]=true;["until"]=true;
["elseif"]=true;["function"]=true;["not"]=true;
["repeat"]=true;["return"]=true;["while"]=true;
};
local function s(e)
if o(e)~="table"then
e={preset=e};
end
local i={
table=true;
string=true;
number=true;
boolean=true;
["nil"]=true;
};
if e.preset=="debug"then
e.preset="oneline";
e.freeze=true;
e.fatal=false;
e.fallback=t;
e.unquoted=true;
end
if e.preset=="oneline"then
e.indentwith=e.indentwith or"";
e.itemstart=e.itemstart or" ";
e.itemlast=e.itemlast or"";
e.tend=e.tend or" }";
elseif e.preset=="compact"then
e.indentwith=e.indentwith or"";
e.itemstart=e.itemstart or"";
e.itemlast=e.itemlast or"";
e.equals=e.equals or"=";
e.unquoted=true;
end
local s=e.fallback or e.fatal==false and t or h;
local function h(e)
return(i[o(e)]or s)(e);
end
local q=e.keywords or a;
local f=e.indentwith or"\t";
local b=e.itemstart or"\n";
local v=e.itemsep or";";
local I=e.itemlast or";\n";
local w=e.tstart or"{";
local N=e.tend or"}";
local k=e.kstart or"[";
local x=e.kend or"]";
local c=e.equals or" = ";
local m=e.unquoted==true and"^[%a_][%w_]*$"or e.unquoted;
local d=e.hex;
local T=e.freeze;
local E=e.maxdepth or 127;
local _=e.multiref;
local O=e.table_iterator or l;
local function l(a,t,e,i)
if t[a]then
t[e],e=s(a,"table has multiple references"),e+1;
return e;
elseif i>E then
t[e],e=s(a,"max table depth reached"),e+1;
return e;
end
local d=a;
t[a]=true;
t[d]=true;
if T==true then
local i=u(a);
if o(i)=="table"then
local n=i.__name;
local i=i.__freeze;
if o(i)=="function"then
a=i(a);
if o(a)=="string"then
t[e],e=a,e+1;
return e;
end
if o(n)=="string"then
t[e],e=n,e+1;
end
end
end
end
t[e],e=w,e+1;
local p=y(f,i);
local r=1;
local n,u;
local w=false;
for a,s in O(a)do
w=true;
t[e],e=b,e+1;
t[e],e=p,e+1;
n,u=o(a),o(s);
if a==r then
r=r+1;
elseif m and n=="string"and
not q[a]and g(a,m)then
t[e],e=a,e+1;
t[e],e=c,e+1;
else
t[e],e=k,e+1;
if n=="table"then
e=l(a,t,e,i+1);
else
t[e],e=h(a),e+1;
end
t[e],t[e+1],e=x,c,e+2;
end
if u=="table"then
e=l(s,t,e,i+1);
else
t[e],e=h(s),e+1;
end
t[e],e=v,e+1;
end
if w then
t[e-1]=I;
t[e],e=y(f,i-1),e+1;
end
t[e],e=N,e+1;
if _ then
t[a]=nil;
t[d]=nil;
end
return e;
end
function i.table(t)
local e={};
l(t,e,1,1);
return H(e);
end
local function a(e)
return'"'..p(e,"[%z\1-\31\"\'\\\127-\255]",r)..'"';
end
if o(d)=="string"then
function i.string(e)
local t=a(e);
if#t>(#e*2+2+#d)then
return d..'"'..S(e)..'"';
end
return t;
end
else
i.string=a;
end
function i.number(e)
if j(e)=="integer"then
return n("%d",e);
elseif e==z then
return"(1/0)";
elseif e==A then
return"(-1/0)";
elseif e~=e then
return"(0/0)";
end
return n("%.18g",e);
end
i["nil"]=function()
return"nil";
end
function i.boolean(e)
return e and"true"or"false";
end
return h;
end
local function i(e)
if o(e)~="string"then return nil;end
e="return "..e;
local e,t=v(e,"=serialized data",{});
if not e then return nil,t;end
local t,e=N(e);
if not t then return nil,e;end
return e;
end
local a=s();
return{
new=s;
serialize=function(e,t)
if t==nil then
return a(e);
else
return s(t)(e);
end
end;
deserialize=i;
};
end)
package.preload['util.indexedbheap']=(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 d=setmetatable;
local h=math.floor;
local i=table.remove;
local function l(t,i,o,n,s)
local e=#t+1;
while true do
local a=h(e/2);
if a==0 or i>t[a]then break;end
t[e]=t[a];
o[e]=o[a];
s[o[e]]=e;
e=a;
end
t[e]=i;
o[e]=n;
s[n]=e;
end
local function r(t,e,a,n)
local i=t[e];
local s=a[e];
while e~=1 do
local o=h(e/2);
if i>=t[o]then break;end
t[e]=t[o];
a[e]=a[o];
n[a[e]]=e;
e=o;
end
t[e]=i;
a[e]=s;
n[s]=e;
return e;
end
local function n(a,e,o,h)
local s=a[e];
local n=o[e];
local i=#a;
local t=2*e;
while 2*e<=i do
if t~=i and a[t]>a[t+1]then
t=t+1;
end
if s>a[t]then
a[e]=a[t];
o[e]=o[t];
h[o[e]]=e;
else
break;
end
e=t;
t=2*e;
end
a[e]=s;
o[e]=n;
h[n]=e;
return e;
end
local function u(e,t,a)
local h=#e;
if h==0 then return nil;end
local s=e[1];
local o=t[1];
a[o]=nil;
if h==1 then
e[1]=nil;
t[1]=nil;
return s,o;
end
e[1]=i(e);
t[1]=i(t);
a[t[1]]=1;
n(e,1,t,a);
return s,o;
end
local t={};
function t:insert(a,t,e)
if e==nil then
e=self.current_id;
self.current_id=e+1;
end
self.items[e]=a;
l(self.priorities,t,self.ids,e,self.index);
return e;
end
function t:pop()
local a,e=u(self.priorities,self.ids,self.index);
if e then
local t=self.items[e];
self.items[e]=nil;
return a,t,e;
end
end
function t:peek()
return self.priorities[1];
end
function t:reprioritize(e,t)
local e=self.index[e];
if e==nil then return;end
self.priorities[e]=t;
e=r(self.priorities,e,self.ids,self.index);
n(self.priorities,e,self.ids,self.index);
end
function t:remove_index(e)
local o=self.priorities[e];
if o==nil then return;end
local t=self.ids[e];
local s=self.items[t];
local a=#self.priorities;
self.priorities[e]=self.priorities[a];
self.ids[e]=self.ids[a];
self.index[self.ids[e]]=e;
i(self.priorities);
i(self.ids);
self.index[t]=nil;
self.items[t]=nil;
if a>e then
e=r(self.priorities,e,self.ids,self.index);
n(self.priorities,e,self.ids,self.index);
end
return o,s,t;
end
function t:remove(e)
return self:remove_index(self.index[e]);
end
local e={__index=t};
local e={
create=function()
return d({
ids={};
items={};
priorities={};
index={};
current_id=1.5
},e);
end
};
return e;
end)
package.preload['util.xpcall']=(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=xpcall;
if select(2,e(function(e)return e end,function()end,"test"))~="test"then
e=require"util.compat".xpcall;
end
return{
xpcall=e;
};
end)
package.preload['util.array']=(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 d,l,u,m
=table.insert,table.sort,table.remove,table.concat;
local s=require"util.table".move;
local h=setmetatable;
local a=getmetatable;
local r=math.random;
local y=math.floor;
local p,w=pairs,ipairs;
local f=tostring;
local c=type;
local i={};
local t={};
local e={};
local o={
__index=e;
__name="array";
__tostring=function(e)return"{"..e:concat(", ").."}";end;
};
function o:__freeze()return self;end
local function n(t,e,a,i)
if c(e)=="function"then
e=t.collect(e,a,i);
end
return h(e or{},o);
end
function o.__add(a,e)
local t=n();
return t:append(a):append(e);
end
function o.__eq(e,t)
if a(e)~=o or a(t)~=o then
return false;
end
if#e==#t then
for a=1,#e do
if e[a]~=t[a]then
return false;
end
end
else
return false;
end
return true;
end
function o.__div(a,i)
local t=n();
local e=0;
for o=1,#a do
local a=i(a[o]);
if a~=nil then
e=e+1;
t[e]=a;
end
end
return t;
end
h(i,{__call=n});
function e:random()
return self[r(1,#self)];
end
function e:random_other(t)
local e=#self;
return self[((math.random(1,e-1)+(t-1))%e)+1];
end
function t.map(e,t,o)
for t,a in w(t)do
e[t]=o(a);
end
return e;
end
function t.filter(t,a,n)
local i,o=a==t,#a;
local e=1;
for o=1,o do
local a=a[o];
if n(a)then
t[e]=a;
e=e+1;
end
end
if i and e<=o then
for e=e,o do
t[e]=nil;
end
end
return t;
end
function t.slice(o,t,a,e)
if e==nil then
e=-1;
end
if e<0 then
e=#t+(e+1);
end
if a<0 then
a=#t+(a+1);
end
if a<1 then
a=1;
end
if e>#t then
e=#t;
end
if a>e then
for e=1,#o do
o[e]=nil;
end
return o;
end
s(t,a,e,1,o);
if t==o then
s(t,#o+1,#o*2,2+e-a,t);
end
return o;
end
function t.sort(e,t,...)
if t~=e then
e:append(t);
end
l(e,...);
return e;
end
function t.unique(o,e)
local a={};
return t.filter(o,e,function(e)
if a[e]then
return false;
else
a[e]=true;
return true;
end
end);
end
function t.pluck(t,e,i,o)
for a=1,#e do
local e=e[a][i];
if e==nil then
e=o;
end
t[a]=e;
end
return t;
end
function t.reverse(e,i)
local t=#i;
if i==e then
local a=y(t/2);
t=t+1;
local o;
for a=1,a do
o=t-a;
e[a],e[o]=e[o],e[a];
end
else
local a=t+1;
for t=1,t do
e[t]=i[a-t];
end
end
return e;
end
function e:shuffle()
local t=#self;
for e=1,#self do
local t=r(e,t);
self[e],self[t]=self[t],self[e];
end
return self;
end
function e:append(e)
s(e,1,#e,#self+1,self);
return self;
end
function e:push(t)
d(self,t);
return self;
end
e.pop=u;
function e:concat(e)
return m(i.map(self,f),e);
end
function e:length()
return#self;
end
function i.collect(i,a,e)
local t={};
while true do
e=i(a,e);
if e==nil then break;end
d(t,e);
end
return h(t,o);
end
for t,a in p(t)do
local a=a;
i[t]=function(o,...)
local t=n();
return a(t,o,...);
end
e[t]=function(e,...)
return a(e,e,...);
end
end
return i;
end)
package.preload['util.format']=(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=tostring;
local p=table.unpack;
local e=table.pack;
local y=require"util.encodings".utf8.valid;
local i=type;
local w=require"util.serialization".new("debug");
local d=math.type;
local u={c=true,d=true,i=true,o=true,u=true,X=true,x=true,};
local l={o=true;u=true;x=true;X=true};
local r={
["\000"]="\226\144\128",["\001"]="\226\144\129",["\002"]="\226\144\130",
["\003"]="\226\144\131",["\004"]="\226\144\132",["\005"]="\226\144\133",
["\006"]="\226\144\134",["\007"]="\226\144\135",["\008"]="\226\144\136",
["\009"]="\226\144\137",["\010"]="\226\144\138",["\011"]="\226\144\139",
["\012"]="\226\144\140",["\013"]="\226\144\141",["\014"]="\226\144\142",
["\015"]="\226\144\143",["\016"]="\226\144\144",["\017"]="\226\144\145",
["\018"]="\226\144\146",["\019"]="\226\144\147",["\020"]="\226\144\148",
["\021"]="\226\144\149",["\022"]="\226\144\150",["\023"]="\226\144\151",
["\024"]="\226\144\152",["\025"]="\226\144\153",["\026"]="\226\144\154",
["\027"]="\226\144\155",["\028"]="\226\144\156",["\029"]="\226\144\157",
["\030"]="\226\144\158",["\031"]="\226\144\159",["\127"]="\226\144\161",
};
local c=pcall(string.format,"%p","");
local function f(h,...)
local o=e(...);
local m=o.n;
local a=0;
h=h:gsub("%%[^cdiouxXaAeEfgGpqs%%]*[cdiouxXaAeEfgGpqs%%]",function(s)
if s=="%%"then return end
a=a+1;
local e=o[a];
if e==nil then
o[a]="nil";
return"(%s)";
end
local t=s:sub(-1);
local i=i(e);
if t=="s"and i=="string"and not e:find("[%z\1-\31\128-\255]")then
return
elseif i=="number"then
if t=="g"or(t=="d"and d(e)=="integer")then return end
elseif t=="s"and i~="string"then
e=n(e);
i="string";
end
if t~="s"and t~="q"and t~="p"then
if i~="number"then
e=n(e);
t="s";
s="[%s]";
i="string";
elseif u[t]and d(e)~="integer"then
o[a]=n(e);
return"[%s]";
elseif l[t]and e<0 then
o[a]=n(e);
return"[%s]";
else
return
end
end
if t=="p"and not c then
e=n(e);
t="s";
s="[%s]";
i="string";
end
if i=="string"and t~="p"then
if not y(e)then
t="q";
elseif t~="q"then
o[a]=e:gsub("[%z\1-\8\11-\31\127]",r):gsub("\n\t?","\n\t");
return s;
end
end
if t=="q"then
o[a]=w(e);
return"%s";
end
if t=="p"and(i=="boolean"or i=="number")then
o[a]=n(e);
return"[%s]";
end
end);
while a<m do
a=a+1;
local e=o[a];
if e==nil then
o[a]="(nil)";
else
o[a]=n(e):gsub("[%z\1-\8\11-\31\127]",r):gsub("\n\t?","\n\t");
end
h=h.." [%s]"
end
return h:format(p(o));
end
return{
format=f;
};
end)
package.preload['util.promise']=(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={};
local n={__name="promise",__index=i};
local s=require"util.xpcall".xpcall;
local l=table.unpack;
function n:__tostring()
return"promise ("..(self._state or"invalid")..")";
end
local function o(e)
local e=getmetatable(e);
return e==n;
end
local function e(e,a,o,t)
if not e then
return t;
end
return function(t)
local t,e=s(e,debug.traceback,t);
if t then
a(e);
else
o(e);
end
return true;
end;
end
local function u(o,i,n,a,t)
table.insert(o._pending_on_fulfilled,e(i,a,t,a));
table.insert(o._pending_on_rejected,e(n,a,t,t));
end
local function d(i,o,n,t,a)
e(o,t,a,t)(i.value);
end
local function r(i,n,o,a,t)
e(o,a,t,t)(i.reason);
end
local function a(e,t,a,o,i)
if e._state~="pending"then
return;
end
e._state=t;
e._next=a;
for a,t in ipairs(o)do
t(i);
end
e._pending_on_fulfilled=nil;
e._pending_on_rejected=nil;
return true;
end
local function h(e)
local function i(t)
if o(t)then
t:next(h(e));
elseif a(e,"fulfilled",d,e._pending_on_fulfilled,t)then
e.value=t;
end
end
local function o(t)
if a(e,"rejected",r,e._pending_on_rejected,t)then
e.reason=t;
end
end
return i,o;
end
local d=function(e)
e();
end
local function e(a)
local e=setmetatable({_state="pending",_next=u,_pending_on_fulfilled={},_pending_on_rejected={}},n);
if a then
d(function()
local o,t=h(e);
local a,o=s(a,debug.traceback,o,t);
if not a and e._state=="pending"then
t(o);
end
end);
end
return e;
end
local function r(i)
return e(function(n,r)
local e,a,h=0,{},false;
local t=0;
for s,i in pairs(i)do
if o(i)then
t=t+1;
i:next(function(o)
a[s]=o;
e=e+1;
if e==t and h then
n(a);
end
end,r);
else
a[s]=i;
end
end
h=true;
if e==t then
n(a);
end
end);
end
local function u(n)
return e(function(s)
local e,t,i=0,{},false;
local a=0;
for n,h in pairs(n)do
if o(h)then
a=a+1;
h:next(function(o)
t[n]={status="fulfilled",value=o};
e=e+1;
if e==a and i then
s(t);
end
end,function(o)
t[n]={status="rejected",reason=o};
e=e+1;
if e==a and i then
s(t);
end
end);
else
t[n]=h;
end
end
i=true;
if e==a then
s(t);
end
end);
end
local function s(t,...)
local e,a={...},select("#",...);
return r(e):next(function(e)
return t(l(e,1,a));
end);
end
local function n(t)
return e(function(o,a)
for e=1,#t do
t[e]:next(o,a);
end
end);
end
local function a(t)
return e(function(e)
e(t);
end);
end
local function t(t)
return e(function(a,e)
e(t);
end);
end
local function h(e)
return a():next(function()return e();end);
end
function i:next(t,a)
return e(function(e,o)
self:_next(t,a,e,o);
end);
end
function i:catch(e)
return self:next(nil,e);
end
function i:finally(e)
local function i(t)e();return t;end
local function o(a)e();return t(a);end
return self:next(i,o);
end
return{
new=e;
resolve=a;
join=s;
reject=t;
all=r;
all_settled=u;
race=n;
try=h;
is_promise=o;
set_nexttick=function(e)d=e;end;
}
end)
package.preload['net.adns']=(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=setmetatable;
local f=tostring;
local y=table.concat;
local n=string.format;
local m=string.lower;
local u=string.upper;
local i=function()end;
local w=require"util.logger";
local o=w.init("unbound");
local k=require"net.server";
local s=require"lunbound";
local g=require"util.promise";
local b=require"util.id".short;
local p=require"socket".gettime;
local e=require"util.dns";
local h,r,l=e.classes,e.types,e.errors;
local v=e.parsers;
local t={hoststxt=false}
local function d(e)
e=e or{};
for t,a in pairs(t)do
if e[t]==nil then
e[t]=a;
end
end
for t,a in pairs(s.config)do
if e[t]==nil then
e[t]=a;
end
end
return e;
end
local t;
if prosody then
local e=require"core.configmanager";
t=d(e.get("*","unbound"));
prosody.events.add_handler("config-reloaded",function()
t=d(e.get("*","unbound"));
end);
end
local function q(e,t)
o("debug","Setting up net.server event handling for %s",e);
return t.watchfd(e,function()
o("debug","Processing queries for %s",e);
e:process()
end);
end
local o,c;
local function d()
o=s.new(t);
c=q(o,k);
end
if prosody then
prosody.events.add_handler("server-started",d);
end
local s={
__tostring=function(e)
if e._string then return e._string end
local t=n("Status: %s",l[e.status]);
if e.secure then
t=t..", Secure";
elseif e.bogus then
t=t..n(", Bogus: %s",e.bogus);
end
local a={t};
for t=1,#e do
a[t+1]=e.qname.."\t"..h[e.qclass].."\t"..r[e.qtype].."\t"..f(e[t]);
end
local t=y(a,"\n");
e._string=t;
return t;
end;
};
local n={};
local function y(e)
if not e then return end
local i=l[e.rcode];
local o=h[e.qclass];
local t=r[e.qtype];
e.status,e.class,e.type=i,o,t;
local o=m(t);
local i={__index=e,__tostring=function(e)return f(e[o])end};
local n=v[t];
for t=1,#e do
if e.bogus then
e[t]=nil;
else
e[t]=a({[o]=n(e[t])},i);
end
end
return a(e,s);
end
local function m(c,l,a,t)
if not o then d();end
a=a and u(a)or"A";
t=t and u(t)or"IN";
local u,d=r[a],h[t];
local h=p();
local s;
local i=w.init("unbound.query"..b());
local function r(e,o)
local r=p();
n[s]=nil;
if e then
y(e);
i("debug","Results for %s %s %s: %s (%s, %f sec)",l,t,a,e.rcode==0 and(#e.." items")or e.status,
e.secure and"Secure"or e.bogus or"Insecure",r-h);
else
i("error","Results for %s %s %s: %s",l,t,a,f(o));
end
local e,t=pcall(c,e,o);
if not e then i("error","Error in callback: %s",t);end
end
i("debug","Resolve %s %s %s",l,t,a);
local e;
s,e=o:resolve_async(r,l,u,d);
if s then
n[s]=c;
else
i("error","Resolver error: %s",e);
end
return s,e;
end
local function s(a,e,t)
if not o then d();end
e=e and u(e)or"A";
t=t and u(t)or"IN";
local t,e=r[e],h[t];
local e,t=o:resolve(a,t,e);
if not e then return e,t;end
return y(e);
end
local function e(e)
local t=n[e];
o:cancel(e);
if t then
t(nil,"canceled");
n[e]=nil;
end
return true;
end
local function a()
for t in pairs(n)do e(t);end
if c then c:close();end
d();
return true;
end
local function t()
error"not implemented";
end
local a={
lookup=m;
cancel=e;
new_async_socket=t;
dns={
lookup=s;
cancel=e;
cache=i;
socket_wrapper_set=i;
settimeout=i;
query=i;
purge=a;
random=i;
peek=i;
types=r;
classes=h;
};
};
local function h(e,s,n,i)
return g.new(function(o,t)
local function a(a,e)
if e then
return t(e);
else
return o(a);
end
end
local e,a=m(a,s,n,i)
if not e then t(a);end
end);
end
local e={
lookup=function(i,e,t,a,o)
return m(e,t,a,o)
end;
lookup_promise=h;
_resolver={
settimeout=function()end;
closeall=function()end;
};
}
function a.resolver()return e;end
return a;
end)
package.preload['util.dns']=(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=setmetatable;
local e=table;
local s=e.concat;
local n=e.insert;
local o=string.byte;
local i=string.format;
local h=string.sub;
local c=require"util.dnsregistry";
local m=require"util.hex".encode;
local r=require"util.net".ntop;
local e={};
local function a(i,t)
if o(i,t)==0 then return".",t+1;end
local d,r,a=#i,{};
t=t or 1;
repeat
a=o(i,t)or 0;
n(r,h(i,t+1,t+a));
t=t+a+1;
until a==0 or t>=d;
return s(r,"."),t;
end
e.CNAME=a;
e.NS=a
e.PTR=a;
local p={
__tostring=function(e)
return i("%s %s %d %d %d %d %d",e.mname,e.rname,e.serial,e.refresh,e.retry,e.expire,e.minimum);
end;
};
function e.SOA(n)
local h,s,i;
h,i=a(n,1);
s,i=a(n,i);
local
m,f,w,y,
v,g,b,j,
x,q,k,c,
u,i,n,a,
o,r,l,d
=o(n,i,i+19);
return t({
mname=h;
rname=s;
serial=m*16777216+f*65536+w*256+y;
refresh=v*16777216+g*65536+b*256+j;
retry=x*16777216+q*65536+k*256+c;
expire=u*16777216+i*65536+n*256+a;
minimum=o*16777216+r*65536+l*256+d;
},p);
end
e.A=r;
e.AAAA=r;
local r={
__tostring=function(e)
return i("%d %s",e.pref,e.mx)
end
};
function e.MX(e)
local i=a(e,3);
local a,e=o(e,1,2);
return t({
pref=a*256+e;
mx=i;
},r);
end
local r={
__tostring=function(e)
return i("%d %d %d %s",e.priority,e.weight,e.port,e.target);
end
};
function e.SRV(e)
local a=a(e,7);
local e,o,h,n,i,s=o(e,1,6);
return t({
priority=e*256+o;
weight=h*256+n;
port=i*256+s;
target=a;
},r);
end
local d={__tostring=s};
function e.TXT(s)
local l=#s;
local r,a,i={},1;
repeat
i=o(s,a)or 0;
n(r,h(s,a+1,a+i));
a=a+i+1;
until a>=l;
return t(r,d);
end
e.SPF=e.TXT;
local r={
[0]="PKIX-CA";
[1]="PKIX-EE";
[2]="DANE-TA";
[3]="DANE-EE";
[255]="PrivCert";
};
local d={
[0]="Cert",
[1]="SPKI",
[255]="PrivSel",
};
local l={
[0]="Full",
[1]="SHA2-256",
[2]="SHA2-512",
[255]="PrivMatch",
};
local u={
__tostring=function(e)
return i("%s %s %s %s",
r[e.use]or e.use,
d[e.select]or e.select,
l[e.match]or e.match,
m(e.data));
end;
__index={
getUsage=function(e)return r[e.use]end;
getSelector=function(e)return d[e.select]end;
getMatchType=function(e)return l[e.match]end;
}
};
function e.TLSA(e)
local i,o,a=o(e,1,3);
return t({
use=i;
select=o;
match=a;
data=h(e,4);
},u);
end
local o={"alpn";"no-default-alpn";"port";"ipv4hint";"ech";"ipv6hint"};
t(o,{__index=function(t,e)return"key"..tostring(e);end});
local f={
__tostring=function(e)
local t={};
for a=1,#e.fields do
n(t,i("%s=%q",o[e.fields[a].key],tostring(e.fields[a].value)));
end
return i("%d %s %s",e.prio,e.name,s(t," "));
end;
};
local u={__tostring=function(e)return s(e,", ");end}
function e.SVCB(i)
local o,s=i:byte(1,2);
local l=o*256+s;
local d,o=a(i,3);
local r={};
while#i>o do
local a,s=i:byte(o+0,o+1);
local h,d=i:byte(o+2,o+3);
local s=a*256+s;
local h=h*256+d;
local a=i:sub(o+4,o+4-1+h)
if s==1 then
a=t(e.TXT(a),u);
elseif s==3 then
local t,e=a:byte(1,2);
local e=t*256+e;
a=e;
elseif s==4 then
local o={};
for t=1,#a,4 do
n(o,e.A(a:sub(t,t+3)));
end
a=t(o,u);
elseif s==6 then
local o={};
for t=1,#a,16 do
n(o,e.AAAA(a:sub(t,t+15)));
end
a=t(o,u);
end
n(r,{key=s,value=a,len=h});
o=o+h+4;
end
return t({
prio=l,name=d,fields=r,
},f);
end
e.HTTPS=e.SVCB;
local o={
TLSA={
use=r;
select=d;
match=l;
};
};
local a={
__tostring=function(e)
return i([[\# %d %s]],#e.raw,m(e.raw));
end;
};
local function i(e)
return t({raw=e},a);
end
t(e,{__index=function()return i end});
return{
parsers=e;
classes=c.classes;
types=c.types;
errors=c.errors;
params=o;
};
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 w=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 q=function(...)return W("warn",e{...});end
local ie=1
local f=w"type"
local j=w"pairs"
local ne=w"ipairs"
local p=w"tonumber"
local u=w"tostring"
local e=w"table"
local a=w"string"
local t=w"coroutine"
local V=math.min
local se=math.huge
local ye=e.concat
local he=e.insert
local pe=a.sub
local ve=t.wrap
local be=t.yield
local T=w"socket"or require"socket"
local P=T.gettime
local e=require"util.net";
local ke=e.pton;
local ge=require"util.sslconfig";
local Y,je=pcall(require,"net.tls_luasec");
local me=T.bind
local xe=T.select
local H
local Z
local ce
local qe
local ee
local d
local we
local te
local ae
local re
local de
local ue
local oe
local r
local le
local B
local fe
local v
local i
local F
local h
local s
local R
local b
local y
local g
local z
local a
local o
local k
local M
local C
local U
local E
local D
local X
local l
local L
local S
local I
local O
local N
local _
local x
local A
v={}
i={}
h={}
F={}
s={}
b={}
y={}
R={}
g={}
a=0
o=0
k=0
M=0
C=0
U=1
E=128
D=10
L=51e3*1024
S=25e3*1024
I=30
O=6e4
N=14*60
local e=package.config:sub(1,1)=="\\"
x=(e and math.huge)or T._SETSIZE or 1024
_=T._SETSIZE or 1024
A=30
de=function(w,t,m,c,j,y,p)
if t:getfd()>=x then
q("server.lua: Disallowed FD number: "..t:getfd())
t:close()
return nil,"fd-too-large"
end
local f=0
local b,e=w.onconnect,w.ondisconnect
local k=t.accept
local e={}
e.shutdown=function()end
e.ssl=function()
return y~=nil
end
e.sslctx=function()
return y
end
e.hosts={}
e.remove=function()
f=f-1
if e then
e.resume()
end
end
e.close=function()
t:close()
o=r(h,t,o)
a=r(i,t,a)
v[m..":"..c]=nil;
s[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=r(i,t,a)
if o then
s[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=me(m,c,E);
t:settimeout(0)
end
a=d(i,t,a)
s[t]=e
g[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()
g[e]=l
n("server.lua: refused new client connection: server full")
return false
end
local t,i=k(t)
if t then
local a,o=t:getpeername()
local t,i,e=B(e,w,t,a,c,o,j,y,p)
if e then
return false
end
f=f+1
n("server.lua: accepted new client connection from ",u(a),":",u(o)," to ",u(c))
if b and not p then
return b(t);
end
return;
elseif i then
n("server.lua: error with new client connection: ",u(i))
e.pause()
g[e]=l
return false
end
end
return e
end
B=function(k,c,t,N,te,I,D,p,Z,Q)
if t:getfd()>=x then
q("server.lua: Disallowed FD number: "..t:getfd())
t:close()
if k then
g[k]=l
k.pause()
end
return nil,nil,"fd-too-large"
end
t:settimeout(0)
local v
local E
local _
local J
local j
local K=c.onincoming
local G=c.onstatus
local x=c.ondisconnect
local P=c.onpredrain
local W=c.ondrain
local ee=c.onreadtimeout;
local U=c.ondetach
local g={}
local m=0
local X
local F
local f=0
local w=false
local T=false
local V,B=0,0
local O=L
local S=S
local e=g
e.extra=Q
if Q then
e.servername=Q.servername
end
e.dispatch=function()
return K
end
e.disconnect=function()
return x
end
e.onreadtimeout=ee;
e.setlistener=function(a,t,o)
if U then
U(a)
end
K=t.onincoming
x=t.ondisconnect
G=t.onstatus
P=t.onpredrain
W=t.ondrain
e.onreadtimeout=t.onreadtimeout
U=t.ondetach
if t.onattach then
t.onattach(a,o)
end
end
e._setpending=function()
j=true
end
e.getstats=function()
return B,V
end
e.ssl=function()
return J
end
e.sslctx=function()
return p
end
e.ssl_info=function()
return t.info and t:info()
end
e.ssl_peercertificate=function()
if not t.getpeercertificate then return nil,"not-implemented";end
return t:getpeercertificate()
end
e.ssl_peerverification=function()
if not t.getpeerverification then return nil,{{"Chain verification not supported"}};end
return t:getpeerverification();
end
e.ssl_peerfinished=function()
if not t.getpeerfinished then return nil,"not-implemented";end
return t:getpeerfinished();
end
e.send=function(n,i,o,a)
return v(t,i,o,a)
end
e.receive=function(a,o)
return E(t,a,o)
end
e.shutdown=function(a)
return _(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(a,t)
if m~=0 then
n("server.lua: discarding unwritten data for ",u(N),":",u(I))
m=0;
end
return a:close(t);
end
e.close=function(l,d)
if not e then return true;end
a=r(i,t,a)
b[e]=nil
if m~=0 then
e:sendbuffer()
if m~=0 then
if e then
e.write=nil
end
X=true
return false
end
end
if t then
z=_ and _(t)
t:close()
o=r(h,t,o)
s[t]=nil
t=nil
else
n"server.lua: socket already closed"
end
if e then
y[e]=nil
R[e]=nil
local t=e;
e=nil
if x then
x(t,d or false);
x=nil
end
end
if k then
k.remove()
end
n"server.lua: closed client handler and removed socket from list"
return true
end
e.server=function()
return k
end
e.ip=function()
return N
end
e.serverport=function()
return te
end
e.clientport=function()
return I
end
e.port=e.clientport
local k=function(i,a)
if not e then return false end
f=f+#a
if f>O then
R[e]="send buffer exceeded"
return false
elseif not T and t and not h[t]then
o=d(h,t,o)
end
m=m+1
g[m]=a
if e then
y[e]=y[e]or l
end
return true
end
e.write=k
e.bufferqueue=function(t)
return g
end
e.socket=function(a)
return t
end
e.set_mode=function(a,t)
D=t or D
return D
end
e.set_send=function(a,t)
v=t or v
return v
end
e.bufferlen=function(o,t,a)
O=a or O
S=t or S
return f,S,O
end
e.lock_read=function(t,a)
q("server.lua, lock_read() is deprecated, use pause() and resume()")
if a==true then
return t:pause()
elseif a==false then
return t:resume()
end
return w
end
e.pause=function(o)
local o=a
a=r(i,t,a)
b[e]=nil
if a~=o then
w=true
end
return w;
end
e.resume=function(o)
if w then
w=false
a=d(i,t,a)
b[e]=l
end
return w;
end
e.lock=function(t,a)
q("server.lua, lock() is deprecated")
e.lock_read(t,a)
if a==true then
e.pause_writes(t)
elseif a==false then
e.resume_writes(t)
end
return w,T
end
e.pause_writes=function(a)
local a=o
o=r(h,t,o)
y[e]=nil
T=true
end
e.resume_writes=function(e)
T=false
if f>0 and t then
o=d(h,t,o)
end
end
local b=function()
local o,t,a=E(t,D)
if not t or(t=="wantread"or t=="timeout")then
local a=o or a or""
local o=#a
if o>S then
e:close("receive buffer exceeded")
return false
end
local o=o*ie
B=B+o
C=C+o
b[e]=l
if j then
j=nil
if c.onconnect then
c.onconnect(e)
end
end
return K(e,a,t)
else
n("server.lua: client ",u(N),":",u(I)," read error: ",u(t))
z=e and e:force_close(t)
return false
end
end
local w=function()
local w,a,i,s,d;
if t then
if j then
j=nil
if c.onconnect then
c.onconnect(e);
end
end
if P then
P(e);
end
s=ye(g,"",1,m)
w,a,i=v(t,s,1,f)
d=(w or i or 0)*ie
V=V+d
M=M+d
for e=m,1,-1 do
g[e]=nil
end
else
w,a,d=false,"unexpected close",0;
end
if w then
m=0
f=0
o=r(h,t,o)
y[e]=nil
if W then
W(e)
end
z=F and e:starttls(nil)
z=X and e:force_close()
return true
elseif i and(a=="timeout"or a=="wantwrite")then
s=pe(s,i+1,f)
g[1]=s
m=1
f=f-i
y[e]=l
return true
else
n("server.lua: client ",u(N),":",u(I)," write error: ",u(a))
z=e and e:force_close(a)
return false
end
end
local l;
function e.set_sslctx(y,t)
p=t;
local u,f
l=ve(function(s)
local t
for l=1,A do
o=(f and r(h,s,o))or o
a=(u and r(i,s,a))or a
u,f=nil,nil
l,t=s:dohandshake()
if not t then
n("server.lua: ssl handshake done")
e.readbuffer=b
e.sendbuffer=w
l=G and G(e,"ssl-handshake-complete")
if y.autostart_ssl and c.onconnect then
c.onconnect(y);
if m~=0 then
o=d(h,s,o)
end
end
a=d(i,s,a)
return true
else
if t=="wantwrite"then
o=d(h,s,o)
f=true
elseif t=="wantread"then
a=d(i,s,a)
u=true
else
break;
end
t=nil;
be()
end
end
t=(t or"handshake too long");
n("server.lua: ",t);
z=e and e:force_close(t)
return false,t
end
)
end
if Y then
e.starttls=function(f,c)
if c then
e:set_sslctx(c);
end
if m>0 then
n"server.lua: we need to do tls, but delaying until send buffer empty"
F=true
return
end
n("server.lua: attempting to start tls on "..u(t))
local c,m=t
t,m=p:wrap(t)
if not t then
n("server.lua: error while starting tls on client: ",u(m or"unknown error"))
return nil,m
end
if t.sni then
if f.servername then
t:sni(f.servername);
elseif next(p._sni_contexts)~=nil then
t:sni(p._sni_contexts,true);
end
end
t:settimeout(0)
v=t.send
E=t.receive
_=H
s[t]=e
a=d(i,t,a)
a=r(i,c,a)
o=r(h,c,o)
s[c]=nil
e.starttls=nil
F=nil
J=true
e.readbuffer=l
e.sendbuffer=l
return l(t)
end
end
e.readbuffer=b
e.sendbuffer=w
v=t.send
E=t.receive
_=(J and H)or t.shutdown
s[t]=e
a=d(i,t,a)
if p and Z and Y then
n"server.lua: auto-starting ssl negotiation..."
e.autostart_ssl=true;
local e,t=e:starttls(p);
if e==false then
return nil,nil,t
end
end
return e,t
end
H=function()
end
qe=function()
return false
end
d=function(t,a,e)
if not t[a]then
e=e+1
t[e]=a
t[a]=e
end
return e;
end
r=function(e,i,t)
local o=e[i]
if o then
e[i]=nil
local a=e[t]
e[t]=nil
if a~=i then
e[a]=o
e[o]=a
end
return t-1
end
return t
end
oe=function(e)
o=r(h,e,o)
a=r(i,e,a)
s[e]=nil
e:close()
end
local function m(e,t,o)
local a;
local i=t.sendbuffer;
function t.sendbuffer()
i(t);
if a and t.bufferlen()<o then
e:lock_read(false);
a=nil;
end
end
local i=e.readbuffer;
function e.readbuffer()
i();
if not a and t.bufferlen()>=o then
a=true;
e:lock_read(true);
end
end
e:set_mode("*a");
end
te=function(t,e,l,h)
t=t or"*"
h=h or{}
local o
local r=h.tls_ctx;
local c=h.tls_direct;
local u=h.read_size;
if f(l)~="table"then
o="invalid listener table"
elseif f(t)~="string"then
o="invalid address"
elseif f(e)~="number"or not(e>=0 and e<=65535)then
o="invalid port"
elseif v[t..":"..e]then
o="listeners on '["..t.."]:"..e.."' already exist"
elseif r and not Y then
o="luasec not found"
end
if o then
q("server.lua, [",t,"]:",e,": ",o)
return nil,o
end
local o,h=me(t,e,E)
if h then
q("server.lua, [",t,"]:",e,": ",h)
return nil,h
end
local h,l=de(l,o,t,e,u,r,c)
if not h then
o:close()
return nil,l
end
o:settimeout(0)
a=d(i,o,a)
v[t..":"..e]=h
s[o]=h
n("server.lua: new "..(r and"ssl "or"").."server listener on '[",t,"]:",e,"'")
return h
end
we=function(i,o,a,t,e)
return te(i,o,a,{
read_size=t;
tls_ctx=e;
tls_direct=e and true or false;
});
end
re=function(t,e)
return v[t..":"..e];
end
le=function(e,t)
local a=v[e..":"..t]
if not a then
return nil,"no server found on '["..e.."]:"..u(t).."'"
end
a:close()
v[e..":"..t]=nil
return true
end
ee=function()
for e,t in j(s)do
t:close()
s[e]=nil
end
a=0
o=0
k=0
v={}
i={}
h={}
F={}
s={}
end
ue=function()
return{
select_timeout=U;
tcp_backlog=E;
max_send_buffer_size=L;
max_receive_buffer_size=S;
select_idle_check_interval=I;
send_timeout=O;
read_timeout=N;
max_connections=_;
max_ssl_handshake_roundtrips=A;
highest_allowed_fd=x;
accept_retry_interval=D;
}
end
fe=function(e)
if f(e)~="table"then
return nil,"invalid settings table"
end
U=p(e.select_timeout)or U
L=p(e.max_send_buffer_size)or L
S=p(e.max_receive_buffer_size)or S
I=p(e.select_idle_check_interval)or I
E=p(e.tcp_backlog)or E
O=p(e.send_timeout)or O
N=p(e.read_timeout)or N
D=p(e.accept_retry_interval)or D
_=e.max_connections or _
A=e.max_ssl_handshake_roundtrips or A
x=e.highest_allowed_fd or x
return true
end
ae=function(e)
if f(e)~="function"then
return nil,"invalid listener function"
end
k=k+1
F[k]=e
return true
end
local u do
local i={};
local t={};
function u(e,o)
local a=P();
e=e+a;
if e>=a then
he(t,{e,o});
else
local e=o(a);
if e and f(e)=="number"then
return u(e,o);
end
end
end
ae(function(a)
if#t>0 then
for a,e in j(t)do
he(i,e);
end
t={};
end
local e=se;
for s,t in j(i)do
local n,o=t[1],t[2];
if n<=a then
i[s]=nil;
local t=o(a);
if f(t)=="number"then
u(t,o);
e=V(e,t);
end
else
e=V(e,n-a);
end
end
return e;
end);
end
ce=function()
return C,M,a,o,k
end
local t;
local function c(e)
t=e;
end
Z=function(e)
if t then return"quitting";end
if e then t="once";end
l=P()
repeat
local e=se;
for t=1,k do
local t=F[t](l)
if t then e=V(e,t);end
end
local e,a,o=xe(i,h,V(U,e))
for t,e in ne(e)do
local t=s[e]
if t then
t:readbuffer()
else
oe(e)
n"server.lua: found no handler and closed socket (readlist)"
end
end
for e,t in ne(a)do
local e=s[t]
if e then
e:sendbuffer()
else
oe(t)
n"server.lua: found no handler and closed socket (writelist)"
end
end
for e,t in j(R)do
e.disconnect()(e,t)
e:force_close()
R[e]=nil;
end
l=P()
if l-X>I then
X=l
for e,t in j(y)do
if l-t>O then
e.disconnect()(e,"send timeout")
e:force_close()
end
end
for e,t in j(b)do
if l-t>N then
if not(e.onreadtimeout)or e:onreadtimeout()~=true then
e.disconnect()(e,"read timeout")
e:close()
else
b[e]=l
end
end
end
end
for e,t in j(g)do
if l-t>D then
g[e]=nil;
e.resume();
end
end
until t;
if t=="once"then t=nil;return;end
ee();
return"quitting"
end
local function g()
return Z(true);
end
local function k()
return"select";
end
local n=function(c,u,l,t,e,n,r)
local e,t,r=B(nil,t,c,u,l,"clientport",e,n,n,r)
if not e then return nil,r end
s[t]=e
if not n then
e._setpending()
a=d(i,t,a)
o=d(h,t,o)
end
return e,t
end
local v=function(o,t,i,h,s,a,d)
local e
if f(i)~="table"then
e="invalid listener table"
elseif f(o)~="string"then
e="invalid address"
elseif f(t)~="number"or not(t>=0 and t<=65535)then
e="invalid port"
elseif s and not Y then
e="luasec not found"
end
if not a then
local e=ke(o);
if not e then return nil,"invalid-ip";end
if#e==16 then
a="tcp6";
elseif#e==4 then
a="tcp4";
end
end
local a=T[a];
if f(a)~="function"then
e="invalid socket type"
end
if e then
q("server.lua, addclient: ",e)
return nil,e
end
local e,a=a()
if a then
return nil,a
end
e:settimeout(0)
local r,a=e:setpeername(o,t)
if r or a=="timeout"or a=="Operation already in progress"then
return n(e,o,t,i,h,s,d)
else
return nil,a
end
end
local p=function(e)
local e=e.conn;
o=r(h,e,o)
a=r(i,e,a)
s[e]=nil
end;
local a=function(n,t,l)
local e=n.conn
s[e]=n
if t~=nil then
if t then
a=d(i,e,a)
else
o=r(h,e,o)
end
end
if l~=nil then
if l then
o=d(h,e,o)
else
a=r(i,e,a)
end
end
end
local a=function(e,i,o)
local t=e
if f(e)=="number"then
t={getfd=function()return e;end}
end
local e={
conn=t;
readbuffer=i or H;
sendbuffer=o or H;
close=p;
setflags=a;
};
a(e,i,o)
return e
end
w"setmetatable"(s,{__mode="k"})
w"setmetatable"(b,{__mode="k"})
w"setmetatable"(y,{__mode="k"})
X=P()
local function o(e)
local t=W;
if e then
W=e;
end
return t;
end
return{
_addtimer=ae,
add_task=u;
addclient=v,
wrapclient=n,
watchfd=a,
loop=Z,
link=m,
step=g,
stats=ce,
closeall=ee,
addserver=we,
listen=te,
getserver=re,
setlogger=o,
getsettings=ue,
setquitting=c,
removeserver=le,
get_backend=k,
changesettings=fe,
tls_builder=function(e)
return ge._new(je.new_context,e)
end,
}
end)
package.preload['util.xmppstream']=(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"lxp";
local x=require"util.stanza";
local k=x.stanza_mt;
local w=error;
local t=tostring;
local d=table.insert;
local g=table.concat;
local z=table.remove;
local b=setmetatable;
local T=pcall(e.new,{StartDoctypeDecl=false});
local A=pcall(e.new,{XmlDecl=false});
local a=not not e.new({}).getcurrentbytecount;
local _=1024*1024*1;
local _ENV=nil;
local j=e.new;
local E={
["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 h="http://etherx.jabber.org/streams";
local r="\1";
local q="^([^"..r.."]*)"..r.."?(.*)$";
local function o()end
local function v(n,e,s)
local i={};
local p=e.streamopened;
local y=e.streamclosed;
local u=e.error or
function(o,a,e)
w("XML stream error: "..t(a)..(e and": "..t(e)or""),2);
end;
local v=e.handlestanza;
s=s or o;
local t=e.stream_ns or h;
local c=e.stream_tag or"stream";
if t~=""then
c=t..r..c;
end
local x=t..r..(e.error_tag or"error");
local j=e.default_ns;
local m="en";
local l={};
local h,e={};
local t=0;
local r=0;
function i:StartElement(f,o)
if e and#h>0 then
d(e,g(h));
h={};
end
local h,i=f:match(q);
if i==""then
h,i="",h;
end
if h~=j 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=E[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 f==c then
r=0;
m=o["xml:lang"]or m;
if p then
if a then
s(t);
t=0;
end
p(n,o);
end
else
u(n,"no-stream",f);
end
return;
end
if h=="jabber:client"and i~="iq"and i~="presence"and i~="message"then
u(n,"invalid-top-level-element");
end
e=b({name=i,attr=o,tags={}},k);
else
if a then
t=t+self:getcurrentbytecount();
end
d(l,e);
local t=e;
e=b({name=i,attr=o,tags={}},k);
d(t,e);
d(t.tags,e);
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,g(h));
h={};
end
if#l==0 then
if a then
s(t);
end
t=0;
if e.attr["xml:lang"]==nil then
e.attr["xml:lang"]=m;
end
if o~=x then
v(n,e);
else
u(n,"stream-error",e);
end
e=nil;
else
e=z(l);
end
else
if a then
s(t);
end
if y then
y(n);
end
end
end
local function o(e)
u(n,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1.");
if not e.stop or not e:stop()then
w("Failed to abort parsing");
end
end
if A then
function i:XmlDecl(e,t,i)
if a then
s(self:getcurrentbytecount());
end
if(t and t:lower()~="utf-8")
or(i=="no")
or(e and e~="1.0")then
return o(self);
end
end
end
if T then
i.StartDoctypeDecl=o;
end
i.Comment=o;
i.ProcessingInstruction=o;
local function a()
e,h,t=nil,{},0;
l={};
end
local function t(t,e)
n=e;
end
return i,{reset=a,set_session=t};
end
local function u(l,d,o)
local t=0;
local e;
if a then
function e(e)
t=t-e;
end
o=o or _;
elseif o then
w("Stanza size limits are not supported on this version of LuaExpat")
end
local n,s=v(l,d,e);
local i=j(n,r,false);
local h=i.parse;
function l.open_stream(e,t,a)
local i=e.sends2s or e.send;
local o={
["xmlns:stream"]="http://etherx.jabber.org/streams",
["xml:lang"]="en",
xmlns=d.default_ns,
version=e.version and(e.version>0 and"1.0"or nil),
id=e.streamid or"",
from=t or e.host,to=a,
};
if e.stream_attrs then
e:stream_attrs(t,a,o)
end
i("<?xml version='1.0'?>"..x.stanza("stream:stream",o):top_tag());
return true;
end
return{
reset=function()
i=j(n,r,false);
h=i.parse;
t=0;
s.reset();
end,
feed=function(e,n)
if a then
t=t+#n;
end
local e=i;
local n,s=h(e,n);
if a and t>o then
return nil,"stanza-too-large";
end
if i~=e then
e:parse();
e:close();
end
return n,s;
end,
set_session=s.set_session;
set_stanza_size_limit=function(t,e)
o=e;
end;
};
end
return{
ns_separator=r;
ns_pattern=q;
new_sax_handlers=v;
new=u;
};
end)
package.preload['util.jid']=(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=select;
local a,n=string.match,string.sub;
local d=require"util.encodings".stringprep.nodeprep;
local l=require"util.encodings".stringprep.nameprep;
local u=require"util.encodings".stringprep.resourceprep;
local i={
[" "]="\\20";['"']="\\22";
["&"]="\\26";["'"]="\\27";
["/"]="\\2f";[":"]="\\3a";
["<"]="\\3c";[">"]="\\3e";
["@"]="\\40";["\\"]="\\5c";
};
local s={};
local h={};
for t,e in pairs(i)do
s[e]=t;
h[e]=e:gsub("\\",i)
end
local _ENV=nil;
local function t(e)
if e==nil then return;end
local i,t=a(e,"^([^@/]+)@()");
local t,o=a(e,"^([^@/]+)()",t);
local a=t and a(e,"^/(.+)$",o);
if(t==nil)or((a==nil)and#e>=o)then return nil,nil,nil;end
return i,t,a;
end
local function m(e)
local t,e=t(e);
if t~=nil and e~=nil then
return t.."@"..e;
end
return e;
end
local function r(e,o)
local t,e,a=t(e);
if e~=nil and e~="."then
if n(e,-1,-1)=="."then
e=n(e,1,-2);
end
e=l(e,o);
if e==nil then return;end
if t~=nil then
t=d(t,o);
if t==nil then return;end
end
if a~=nil then
a=u(a,o);
if a==nil then return;end
end
return t,e,a;
end
end
local function n(t,e,a)
if e==nil then return end
if t~=nil and a~=nil then
return t.."@"..e.."/"..a;
elseif t~=nil then
return t.."@"..e;
elseif a~=nil then
return e.."/"..a;
end
return e;
end
local function l(t,e)
local a,t,e=r(t,e);
return n(a,t,e);
end
local function d(e,a)
local o,n,i=t(e);
local e,t,a=t(a);
if(e==nil or e==o)and
(t==nil or t==n)and
(a==nil or a==i)then
return true
end
return false
end
local function c(e)
return(o(1,t(e)));
end
local function a(e)
return(o(2,t(e)));
end
local function u(e)
return(o(3,t(e)));
end
local function o(e)return e and(e:gsub("\\%x%x",h):gsub("[\"&'/:<>@ ]",i));end
local function i(e)return e and(e:gsub("\\%x%x",s));end
return{
split=t;
bare=m;
prepped_split=r;
join=n;
prep=l;
compare=d;
node=c;
host=a;
resource=u;
escape=o;
unescape=i;
};
end)
package.preload['util.events']=(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=pairs;
local l=table.insert;
local m=table.remove;
local r=table.sort;
local d=setmetatable;
local h=next;
local _ENV=nil;
local function f()
local i={};
local t;
local n={};
local o={};
local s=nil;
local function u(n,i)
local e=o[i];
if not e or h(e)==nil then return;end
local t={};
for e in a(e)do
l(t,e);
end
r(t,function(t,a)return e[t]>e[a];end);
n[i]=t;
return t;
end;
d(i,{__index=u});
local function d(t,n,a)
local e=o[t];
if e then
e[n]=a or 0;
else
e={[n]=a or 0};
o[t]=e;
end
i[t]=nil;
end;
local function r(t,a)
local e=o[t];
if e then
e[a]=nil;
i[t]=nil;
if h(e)==nil then
o[t]=nil;
end
end
end;
local function f(e)
return i[e];
end;
local function w(e)
for t,e in a(e)do
d(t,e);
end
end;
local function u(e)
for t,e in a(e)do
r(t,e);
end
end;
local function h(t,a)
local e=i[t];
if e and not s then
for t=1,#e do
local e=e[t](a);
if e~=nil then return e;end
end
elseif e and s then
for o=1,#e do
local e=s(e[o],t,a);
if e~=nil then return e;end
end
end
end;
local function c(s,r)
local a=n[s]or t;
if a then
local e=#a;
local function n(o,i)
e=e-1;
if e==0 then
if t==nil or a==t then
return h(o,i);
end
a,e=t,#t;
return a[e](n,o,i);
else
return a[e](n,o,i);
end
end
return a[e](n,s,r);
end
return h(s,r);
end
local function l(a,o)
local e;
if a==false then
e=t;
if not e then
e={};
t=e;
end
else
e=n[a];
if not e then
e={};
n[a]=e;
end
end
e[#e+1]=o;
end
local function h(a,o)
local e;
if a==false then
e=t;
else
e=n[a];
end
if not e then return;end
for t=#e,1,-1 do
if e[t]==o then
m(e,t);
end
end
if#e==0 then
if a==false then
t=nil;
else
n[a]=nil;
end
end
end
local function e(e)
local t=s;
s=e;
return t;
end
return{
add_handler=d;
remove_handler=r;
add_handlers=w;
remove_handlers=u;
get_handlers=f;
wrappers={
add_handler=l;
remove_handler=h;
};
add_wrapper=l;
remove_wrapper=h;
set_debug_hook=e;
fire_event=c;
_handlers=i;
_event_map=o;
};
end
return{
new=f;
};
end)
package.preload['util.dataforms']=(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=setmetatable;
local n=ipairs;
local s,p=type,next;
local h=tonumber;
local l=tostring;
local d=table.concat;
local f=require"util.stanza";
local u=require"util.jid".prep;
local _ENV=nil;
local y='jabber:x:data';
local m='http://jabber.org/protocol/xdata-validate';
local r={};
local a={__index=r};
local function c(t)
return e(t,a);
end
local function w(e)
local o={
title=e:get_child_text("title");
instructions=e:get_child_text("instructions");
};
for t in e:childtags("field")do
local e={
name=t.attr.var;
label=t.attr.label;
type=t.attr.type;
required=t:get_child("required")and true or nil;
value=t:get_child_text("value");
};
o[#o+1]=e;
if e.type then
local a={};
if e.type:match"list%-"then
for e in t:childtags("option")do
a[#a+1]={label=e.attr.label,value=e:get_child_text("value")};
end
for e in t:childtags("value")do
a[#a+1]={label=e.attr.label,value=e:get_text(),default=true};
end
elseif e.type:match"%-multi"then
for e in t:childtags("value")do
a[#a+1]=e.attr.label and{label=e.attr.label,value=e:get_text()}or e:get_text();
end
if e.type=="text-multi"then
e.value=d(a,"\n");
else
e.value=a;
end
end
end
local t=t:get_child("validate",m);
if t then
e.datatype=datatype.attr.datatype;
local t=t:get_child("range");
if t then
e.range_min=h(t.attr.min);
e.range_max=h(t.attr.max);
end
end
end
return c(o);
end
function r.form(t,h,i)
if not i then i="form"end
local e=f.stanza("x",{xmlns=y,type=i});
if i=="cancel"then
return e;
end
if i~="submit"then
if t.title then
e:tag("title"):text(t.title):up();
end
if t.instructions then
e:tag("instructions"):text(t.instructions):up();
end
end
for t,a in n(t)do
local o=a.type or"text-single";
e:tag("field",{type=o,var=a.var or a.name,label=i~="submit"and a.label or nil});
if i~="submit"then
if a.desc then
e:text_tag("desc",a.desc);
end
end
if i=="form"and a.datatype then
e:tag("validate",{xmlns=m,datatype=a.datatype});
if a.range_min or a.range_max then
e:tag("range",{
min=a.range_min and l(a.range_min),
max=a.range_max and l(a.range_max),
}):up();
end
e:up();
end
local t=a.value;
local r=a.options;
if h and h[a.name]~=nil then
t=h[a.name];
if i=="form"and s(t)=="table"
and(o=="list-single"or o=="list-multi")then
r,t=t,nil;
end
end
if i=="form"and r then
local a={};
for o,t in n(r)do
if s(t)=="table"then
e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up();
if t.default then
a[#a+1]=t.value;
end
else
e:tag("option",{label=t}):tag("value"):text(t):up():up();
end
end
if not t then
if o=="list-single"then
t=a[1];
elseif o=="list-multi"then
t=a;
end
end
end
if t~=nil then
if s(t)=="number"then
t=("%g"):format(t);
end
if o=="hidden"then
if s(t)=="table"then
e:tag("value")
:add_child(t)
:up();
else
e:tag("value"):text(t):up();
end
elseif o=="boolean"then
e:tag("value"):text((t and"1")or"0"):up();
elseif o=="fixed"then
e:tag("value"):text(t):up();
elseif o=="jid-multi"then
for a,t in n(t)do
e:tag("value"):text(t):up();
end
elseif o=="jid-single"then
e:tag("value"):text(t):up();
elseif o=="text-single"or o=="text-private"then
e:tag("value"):text(t):up();
elseif o=="text-multi"then
for t in t:gmatch("([^\r\n]+)\r?\n*")do
e:tag("value"):text(t):up();
end
elseif o=="list-single"then
e:tag("value"):text(t):up();
elseif o=="list-multi"then
for a,t in n(t)do
e:tag("value"):text(t):up();
end
end
end
local t=a.media;
if t then
e:tag("media",{xmlns="urn:xmpp:media-element",height=("%g"):format(t.height),width=("%g"):format(t.width)});
for a,t in n(t)do
e:tag("uri",{type=t.type}):text(t.uri):up()
end
e:up();
end
if i=="form"and a.required then
e:tag("required"):up();
end
e:up();
end
return e;
end
local t={};
local l={};
function r.data(e,h,i)
local o={};
local a={};
local s={};
for n,e in n(e)do
local n;
for t in h:childtags("field")do
if(e.var or e.name)==t.attr.var then
n=t;
break;
end
end
if not n then
if i and i[e.name]~=nil then
o[e.name]=i[e.name];
elseif e.required then
a[e.name]="Required value missing";
end
elseif e.name then
s[e.name]=true;
local t=t[e.type];
if t then
local t,i=t(n,e.required);
local n=e.datatype and l[e.datatype];
if t~=nil and n then
local o,a=n(t,e);
if o then
t=a;
else
t,i=nil,a or("Invalid value for data of type "..e.datatype);
end
end
o[e.name],a[e.name]=t,i;
end
end
end
if p(a)then
return o,a,s;
end
return o,nil,s;
end
local function e(e,a)
local e=e:get_child_text("value");
if a and(e==nil or e=="")then
return nil,"Required value missing";
end
return e;
end
t["text-single"]=e;
t["text-private"]=e;
t["jid-single"]=
function(o,a)
local e,a=e(o,a);
if not e then return e,a;end
local a=u(e);
if not a then
return nil,"Invalid JID: "..e;
end
return a;
end
t["jid-multi"]=
function(o,i)
local a={};
local e={};
for t in o:childtags("value")do
local t=t:get_text();
local o=u(t);
a[#a+1]=o;
if t and not o then
e[#e+1]=("Invalid JID: "..t);
end
end
if#a>0 then
return a,(#e>0 and d(e,"\n")or nil);
elseif i then
return nil,"Required value missing";
end
end
t["list-multi"]=
function(o,a)
local e={};
for t in o:childtags("value")do
e[#e+1]=t:get_text();
end
if#e>0 then
return e;
elseif a then
return nil,"Required value missing";
end
end
t["text-multi"]=
function(a,e)
local e,a=t["list-multi"](a,e);
if e then
e=d(e,"\n");
end
return e,a;
end
t["list-single"]=e;
local o={
["1"]=true,["true"]=true,
["0"]=false,["false"]=false,
};
t["boolean"]=
function(i,a)
local e,a=e(i,a);
if not e then return e,a;end
local a=o[e];
if a==nil then
return nil,"Invalid boolean representation:"..e;
end
return a;
end
t["hidden"]=
function(e)
return e:get_child_text("value");
end
l["xs:integer"]=
function(e,t)
local e=h(e);
if not e then
return false,"not a number";
elseif e%1~=0 then
return false,"not an integer";
end
if t.range_max and e>t.range_max then
return false,"out of bounds";
elseif t.range_min and e<t.range_min then
return false,"out of bounds";
end
return true,e;
end
local function t(e)
if not f.is_stanza(e)then
return nil,"not a stanza object";
elseif e.attr.xmlns~="jabber:x:data"or e.name~="x"then
return nil,"not a dataform element";
end
for e in e:childtags("field")do
if e.attr.var=="FORM_TYPE"then
return e:get_child_text("value");
end
end
return"";
end
return{
new=c;
from_stanza=w;
get_type=t;
};
end)
package.preload['util.caps']=(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 d=require"util.encodings".base64.encode;
local u=require"util.hashes".sha1;
local n,h,s=table.insert,table.sort,table.concat;
local r=ipairs;
local _ENV=nil;
local function l(e)
local o,a,i={},{},{};
for t,e in r(e)do
if e.name=="identity"then
n(o,(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(a,e.attr.var or"");
elseif e.name=="x"and e.attr.xmlns=="jabber:x:data"then
local t={};
local o;
for a,e in r(e.tags)do
if e.name=="field"and e.attr.var then
local a={};
for t,e in r(e.tags)do
e=#e.tags==0 and e:get_text();
if e then n(a,e);end
end
h(a);
if e.attr.var=="FORM_TYPE"then
o=a[1];
elseif#a>0 then
n(t,e.attr.var.."\0"..s(a,"<"));
else
n(t,e.attr.var);
end
end
end
h(t);
t=s(t,"<");
if o then t=o.."\0"..t;end
n(i,t);
end
end
h(o);
h(a);
h(i);
if#o>0 then o=s(o,"<"):gsub("%z","/").."<";else o="";end
if#a>0 then a=s(a,"<").."<";else a="";end
if#i>0 then i=s(i,"<"):gsub("%z","<").."<";else i="";end
local e=o..a..i;
local t=d(u(e));
return t,e;
end
return{
calculate_hash=l;
};
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 i=require"util.stanza";
local a,u=table.insert,table.concat;
local n=type;
local e,s,c=next,pairs,ipairs;
local r,l,d,h;
local f="\n";
local o;
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 p(e)
return e:gsub("\\?[\\nt:;,]",{
["\\\\"]="\\",
["\\n"]="\n",
["\\r"]="\r",
["\\t"]="\t",
["\\:"]=":",
["\\;"]=";",
["\\,"]=",",
[":"]="\29",
[";"]="\30",
[","]="\31",
});
end
local function w(e)
local a=i.stanza(e.name,{xmlns="vcard-temp"});
local t=o[e.name];
if t=="text"then
a:text(e[1]);
elseif n(t)=="table"then
if t.types and e.TYPE then
if n(e.TYPE)=="table"then
for o,t in s(t.types)do
for o,e in s(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 s(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 m(t)
local e=i.stanza("vCard",{xmlns="vcard-temp"});
for a=1,#t do
e:add_child(w(t[a]));
end
return e;
end
function h(e)
if not e[1]or e[1].name then
return m(e)
else
local t=i.stanza("xCard",{xmlns="vcard-temp"});
for a=1,#e do
t:add_child(m(e[a]));
end
return t;
end
end
function r(t)
t=t
:gsub("\r\n","\n")
:gsub("\n ","")
:gsub("\n\n+","\n");
local s={};
local e;
for t in t:gmatch("[^\n]+")do
local t=p(t);
local n,t,i=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$");
i=i:gsub("\29",":");
if#t>0 then
local o={};
for a,i,n in t:gmatch("\30([^=]+)(=?)([^\30]*)")do
a=a:upper();
local e={};
for t in n:gmatch("[^\31]+")do
e[#e+1]=t
e[t]=true;
end
if i=="="then
o[a]=e;
else
o[a]=true;
end
end
t=o;
end
if n=="BEGIN"and i=="VCARD"then
e={};
s[#s+1]=e;
elseif n=="END"and i=="VCARD"then
e=nil;
elseif e and o[n]then
local o=o[n];
local n={name=n};
e[#e+1]=n;
local s=e;
e=n;
if o.types then
for o,a in c(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 c(o.props)do
if t[a]then
if t[a]==true then
e[a]=true;
else
for o,t in c(t[a])do
e[a]=t;
end
end
end
end
end
if o=="text"or o.value then
a(e,i);
elseif o.values then
local t="\30"..i;
for t in t:gmatch("\30([^\30]*)")do
a(e,t);
end
end
e=s;
end
end
return s;
end
local function i(t)
local e={};
for a=1,#t do
e[a]=y(t[a]);
end
e=u(e,";");
local a="";
for e,t in s(t)do
if n(e)=="string"and e~="name"then
a=a..(";%s=%s"):format(e,n(t)=="table"and u(t,",")or t);
end
end
return("%s%s:%s"):format(t.name,a,e)
end
local function t(t)
local e={};
a(e,"BEGIN:VCARD")
for o=1,#t do
a(e,i(t[o]));
end
a(e,"END:VCARD")
return u(e,f);
end
function l(e)
if e[1]and e[1].name then
return t(e)
else
local a={};
for o=1,#e do
a[o]=t(e[o]);
end
return u(a,f);
end
end
local function u(i)
local t=i.name;
local e=o[t];
local t={name=t};
if e=="text"then
t[1]=i:get_text();
elseif n(e)=="table"then
if e.value then
t[1]=i:get_child_text(e.value)or"";
elseif e.values then
local e=e.values;
if e.behaviour=="repeat-last"then
for e=1,#i.tags do
a(t,i.tags[e]:get_text()or"");
end
else
for o=1,#e do
a(t,i:get_child_text(e[o])or"");
end
end
elseif e.names then
local e=e.names;
for a=1,#e do
if i:get_child(e[a])then
t[1]=e[a];
break;
end
end
end
if e.props_verbatim then
for e,a in s(e.props_verbatim)do
t[e]=a;
end
end
if e.types then
local e=e.types;
t.TYPE={};
for o=1,#e do
if i:get_child(e[o])then
a(t.TYPE,e[o]:lower());
end
end
if#t.TYPE==0 then
t.TYPE=nil;
end
end
if e.props then
local e=e.props;
for o=1,#e do
local e=e[o]
local o=i: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 i(e)
local t=e.tags;
local e={};
for o=1,#t do
a(e,u(t[o]));
end
return e
end
function d(e)
if e.attr.xmlns~="vcard-temp"then
return nil,"wrong-xmlns";
end
if e.name=="xCard"then
local a={};
local e=e.tags;
for t=1,#e do
a[t]=i(e[t]);
end
return a
elseif e.name=="vCard"then
return i(e)
end
end
o={
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",
};
o.LOGO=o.PHOTO;
o.SOUND=o.PHOTO;
return{
from_text=r;
to_text=l;
from_xep54=d;
to_xep54=h;
lua_to_text=l;
lua_to_xep54=h;
text_to_lua=r;
text_to_xep54=function(...)return h(r(...));end;
xep54_to_lua=d;
xep54_to_text=function(...)return l(d(...))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 i=pairs;
local r=ipairs;
local h=require;
local s=table.remove;
local _ENV=nil;
local e={};
local t;
local function n(e)
local a=t(e,"debug");
local n=t(e,"info");
local i=t(e,"warn");
local o=t(e,"error");
return function(t,e,...)
if t=="debug"then
return a(e,...);
elseif t=="info"then
return n(e,...);
elseif t=="warn"then
return i(e,...);
elseif t=="error"then
return o(e,...);
end
end
end
function t(o,a)
local t=e[a];
if not t then
t={};
e[a]=t;
end
local e=function(i,...)
for e=1,#t do
t[e](o,a,i,...);
end
end
return e;
end
local function d()
for t,e in i(e)do
for t=1,#e do
e[t]=nil;
end
end
end
local function o(t,a)
if not e[t]then
e[t]={a};
else
e[t][#e[t]+1]=a;
end
end
local function l(i,s)
local n=h"util.format".format;
local function e(t,a,e,...)
return i(t,a,n(e,...));
end
for a,t in r(s or{"debug","info","warn","error"})do
o(t,e);
end
return e;
end
local function h(o)
local a;
for t,e in i(e)do
for t=#e,1,-1 do
if e[t]==o then
s(e,t);
a=true;
end
end
end
return a;
end
return{
init=n;
make_logger=t;
reset=d;
add_level_sink=o;
add_simple_sink=l;
new=t;
remove_sink=h;
};
end)
package.preload['util.datetime']=(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=os.date;
local i=os.time;
local l=os.difftime;
local t=math.floor;
local s=tonumber;
local _ENV=nil;
local function d(a)
return e("!%Y-%m-%d",a and t(a)or nil);
end
local function r(a)
if a==nil or a%1==0 then
return e("!%Y-%m-%dT%H:%M:%SZ",a);
end
local o=a%1;
local a=t(a);
return e("!%Y-%m-%dT%H:%M:%S.%%06dZ",a):format(t(o*1e6));
end
local function u(a)
if a==nil or a%1==0 then
return e("!%H:%M:%S",a);
end
local o=a%1;
local a=t(a);
return e("!%H:%M:%S.%%06d",a):format(t(o*1e6));
end
local function m(a)
return e("!%Y%m%dT%H:%M:%S",a and t(a)or nil);
end
local function c(a)
if a then
local h,r,d,c,u,a,n=a:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$");
if h then
local o=i();
local l=l(i(e("*t",o)),i(e("!*t",o)));
local o=0;
if n~=""and n~="Z"then
local a,t,e=n:match("([+%-])(%d%d):?(%d*)");
if not a then return;end
if#e~=2 then e="0";end
t,e=s(t),s(e);
o=t*60*60+e*60;
if a=="-"then o=-o;end
end
local e=a%1;
a=t(a+l)-o;
return i({year=h,month=r,day=d,hour=c,min=u,sec=a,isdst=false})+e;
end
end
end
return{
date=d;
datetime=r;
time=u;
legacy=m;
parse=c;
};
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 u=type;
local t,w,y=table.insert,table.concat,table.remove;
local s=string.char;
local x,d=tostring,tonumber;
local p,l,z=pairs,ipairs,require"util.iterators".sorted_pairs;
local i=next;
local b,f=getmetatable,setmetatable;
local r=print;
local a,e=pcall(require,"util.array");
local v=a and b(e())or{};
local a={};
local n=f({},{__tostring=function()return"null";end;});
a.null=n;
local m={
["\""]="\\\"",["\\"]="\\\\",["\b"]="\\b",
["\f"]="\\f",["\n"]="\\n",["\r"]="\\r",["\t"]="\\t"};
for e=0,31 do
local t=s(e);
if not m[t]then m[t]=("\\u%.4X"):format(e);end
end
local function j(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 q={
__array=true;
__hash=true;
};
local o,g,h,c;
function c(a,e)
t(e,"\""..(a:gsub(".",m)).."\"");
end
function h(a,e)
t(e,"[");
if i(a)then
for i,a in l(a)do
o(a,e);
t(e,",");
end
y(e);
end
t(e,"]");
end
function g(d,e)
local a={};
local s={};
local r={};
for t,e in l(d)do
a[t]=e;
end
for e,t in p(d)do
local o,i=u(e),u(t);
if k[i]or t==n then
if o=="string"and not q[e]then
r[e]=t;
elseif(k[o]or e==n)and a[e]==nil then
s[e]=t;
end
end
end
if i(s)~=nil or i(r)~=nil or i(a)==nil then
t(e,"{");
local n=#e;
local d=e.ordered and z or p;
for a,i in d(r)do
c(a,e);
t(e,":");
o(i,e);
t(e,",");
end
if i(s)~=nil then
t(e,"\"__hash\":[");
for a,i in p(s)do
o(a,e);
t(e,",");
o(i,e);
t(e,",");
end
y(e);
t(e,"]");
t(e,",");
end
if i(a)then
t(e,"\"__array\":");
h(a,e);
t(e,",");
end
if n~=#e then y(e);end
t(e,"}");
else
h(a,e);
end
end
function o(e,a)
local o=u(e);
if e==n then
t(a,"null");
elseif o=="number"then
t(a,x(e));
elseif o=="string"then
c(e,a);
elseif o=="table"then
local t=b(e);
if t==v then
h(e,a);
else
g(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 w(e);
end
function a.encode_ordered(t)
local e={ordered=true};
o(t,e);
return w(e);
end
function a.encode_array(t)
local e={};
h(t,e);
return w(e);
end
local function o(t,e)
return t:find("[^ \t\r\n]",e)or e;
end
local function u(e)
local a=e.__array;
if a then
e.__array=nil;
for o,a in l(a)do
t(e,a);
end
end
local a=e.__hash;
if a then
e.__hash=nil;
local t;
for o,a in l(a)do
if t~=nil then
e[t]=a;t=nil;
else
t=a;
end
end
end
return e;
end
local i,h;
local function c(t,e)
local n={};
while true do
local s,a;
e=o(t,e+1);
if t:byte(e)~=34 then
if t:byte(e)==125 then return n,e+1;end
return nil,"key expected";
end
s,e=h(t,e);
if s==nil then return nil,e;end
e=o(t,e);
if t:byte(e)~=58 then return nil,"colon expected";end
a,e=i(t,e+1);
if a==nil then return nil,e;end
n[s]=a;
e=o(t,e);
local t=t:byte(e);
if t==125 then return u(n),e+1;end
if t~=44 then return nil,"object eof";end
end
end
local function u(s,e)
local n={};
while true do
local a,h;
a,e,h=i(s,e+1,93);
if a==nil then
if h then
if#n~=0 then
return nil,"value expected";
end
a,e=f(n,v),e+1;
end
return a,e;
end
t(n,a);
e=o(s,e);
local t=s:byte(e);
if t==93 then return f(n,v),e+1;end
if t~=44 then return nil,"array eof";end
end
end
local t;
local function l(e)
local t,e=d(e:sub(3,6),16),d(e:sub(9,12),16);
local e=t*1024+e-56613888;
local o=e%64;
e=(e-o)/64;
local a=e%64;
e=(e-a)/64;
local t=e%64;
e=(e-t)/64;
return s(240+e,128+t,128+a,128+o);
end
local function s(e)
e=e:match("%x%x%x%x",3);
if e then
local e=d(e,16)
if e>=55296 and e<=57343 then t=true;end
return j(e);
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[dD][89abAB]%x%x\\u[dD][cdefCDEF]%x%x",l);
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 s(e,t)
local e=e:match("[0-9%.%-eE%+]+",t);
return d(e),t+#e;
end
local function d(t,e)
local t,a,o=t:byte(e+1,e+3);
if t==117 and a==108 and o==108 then
return n,e+4;
end
return nil,"null parse failed";
end
local function l(t,e)
local o,t,a=t:byte(e+1,e+3);
if o==114 and t==117 and a==101 then
return true,e+4;
end
return nil,"true parse failed";
end
local function n(t,e)
local o,i,a,t=t:byte(e+1,e+4);
if o==97 and i==108 and a==115 and t==101 then
return false,e+5;
end
return nil,"false parse failed";
end
function i(a,e,i)
e=o(a,e);
local t=a:byte(e);
if t==123 then
return c(a,e);
elseif t==91 then
return u(a,e);
elseif t==34 then
return h(a,e);
elseif t~=nil and t>=48 and t<=57 or t==45 then
return s(a,e);
elseif t==110 then
return d(a,e);
elseif t==116 then
return l(a,e);
elseif t==102 then
return n(a,e);
elseif t==i then
return nil,e,true;
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=i(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
r("FAILED");
r("encoded:",e);
r("recoded:",t);
else
r(e);
end
return e==t;
end
return a;
end)
package.preload['util.xml']=(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"util.stanza";
local d=require"lxp";
local r=table.insert;
local h=table.remove;
local l=error;
local _ENV=nil;
local e=(function()
local c={
["http://www.w3.org/XML/1998/namespace"]="xml";
};
local n="\1";
local s="^([^"..n.."]*)"..n.."?(.*)$";
return function(u,o)
local e={};
local a=t.stanza("root");
local t={};
local i={};
function e:StartNamespaceDecl(e,a)
if e~=nil then
r(t,a);
r(i,e);
end
end
function e:EndNamespaceDecl(e)
if e~=nil then
h(t);
h(i);
end
end
function e:StartElement(o,e)
local o,n=o:match(s);
if n==""then
o,n="",o;
end
if o~=""then
e.xmlns=o;
end
for t=1,#e do
local a=e[t];
e[t]=nil;
local t,o=a:match(s);
if o~=""then
t=c[t];
if t then
e[t..":"..o]=e[a];
e[a]=nil;
end
end
end
local o={}
for e=1,#t do
o[i[e]]=t[e];
end
a:tag(n,e,o);
end
function e:CharacterData(e)
a:text(e);
end
function e:EndElement()
a:up();
end
local function t(t)
if not t.stop or not t:stop()then
l("Failed to abort parsing");
end
end
e.StartDoctypeDecl=t;
if not o or not o.allow_comments then
e.Comment=t;
end
if not o or not o.allow_processing_instructions then
e.ProcessingInstruction=t;
end
local t=d.new(e,n);
local e,o,n,i=t:parse(u);
if e then e,o,n,i=t:parse();end
if e then
return a.tags[1];
else
return e,("%s (line %d, col %d))"):format(o,n,i);
end
end;
end)();
return{
parse=e;
};
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 h=require"util.stanza".stanza;
local o,i=tostring,tonumber;
local s=type;
local r=pairs;
local n='http://jabber.org/protocol/rsm';
local a={};
do
local e=a;
local function t(e)
return i((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=i(e.attr.index);e:get_text()};
end;
e.last=a;
e.count=t;
end
local s=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(o(e)):up();
end
end;
before=function(t,e)
if e==true then
t:tag("before"):up();
else
t:tag("before"):text(o(e)):up();
end
end
},{
__index=function(t,e)
return function(a,t)
a:tag(e):text(o(t)):up();
end
end;
});
local function o(e)
local o={};
for t in e:childtags()do
local e=t.name;
local a=e and a[e];
if a then
o[e]=a(t);
end
end
return o;
end
local function i(t)
local e=h("set",{xmlns=n});
for t,o in r(t)do
if a[t]then
s[t](e,o);
end
end
return e;
end
local function t(e)
local e=e:get_child("set",n);
if e and#e.tags>0 then
return o(e);
end
end
return{parse=o,generate=i,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 s=require"util.net";
local n=require"util.hex";
local a={};
local o={
__index=function(e,t)
local a=a[t];
if not a then return nil;end
local a=a(e);
e[t]=a;
return a;
end,
__tostring=function(e)return e.addr;end,
};
o.__eq=function(e,t)
if getmetatable(e)~=o or getmetatable(t)~=o then
return false;
end
return e.packed==t.packed;
end
local h={
["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 t(a,e)
local i;
if(not e or e=="IPv6")and a:find('%',1,true)then
a,i=a:match("^(.-)%%(.*)");
end
local t,n=s.pton(a);
if not t then return t,n end
if e=="IPv6"and#t~=16 then
return nil,"invalid-ipv6";
elseif e=="IPv4"and#t~=4 then
return nil,"invalid-ipv4";
elseif not e then
if#t==16 then
e="IPv6";
elseif#t==4 then
e="IPv4";
else
return nil,"unknown protocol";
end
elseif e~="IPv6"and e~="IPv4"then
return nil,"invalid protocol";
end
return setmetatable({addr=a,packed=t,proto=e,zone=i},o);
end
function a:normal()
return s.ntop(self.packed);
end
function a.bits(e)
return n.encode(e.packed):upper():gsub(".",h);
end
function a.bits_full(e)
if e.proto=="IPv4"then
e=e.toV4mapped;
end
return e.bits;
end
local e;
local function y(t,a)
t,a=t.bits_full,a.bits_full;
for e=1,128 do
if t:sub(e,e)~=a:sub(e,e)then
return e-1;
end
end
return 128;
end
local i=t("::1");
local m=t("127.0.0.0");
local u=t("2002::");
local h=t("2001::");
local f=t("fe80::");
local w=t("169.254.0.0");
local c=t("fc00::");
local n=t("fec0::");
local r=t("3ffe::");
local d=t("::");
local v=t("ff00::");
local l=t("::ffff:0:0");
local function p(t)
if e(t,m,8)then
return 2;
elseif e(t,w,16)then
return 2;
else
return 14;
end
end
local function w(t)
if t==i then
return 2;
elseif e(t,f,10)then
return 2;
elseif e(t,n,10)then
return 5;
elseif e(t,v,10)then
return t.packed:byte(2)%16;
else
return 14;
end
end
local function m(t)
if t==i then
return 0;
elseif e(t,u,16)then
return 2;
elseif e(t,h,32)then
return 5;
elseif e(t,c,7)then
return 13;
elseif e(t,n,10)then
return 11;
elseif e(t,r,16)then
return 12;
elseif e(t,d,96)then
return 3;
elseif e(t,l,96)then
return 4;
else
return 1;
end
end
local function f(t)
if t==i then
return 50;
elseif e(t,u,16)then
return 30;
elseif e(t,h,32)then
return 5;
elseif e(t,c,7)then
return 3;
elseif e(t,n,10)then
return 1;
elseif e(t,r,16)then
return 1;
elseif e(t,d,96)then
return 1;
elseif e(t,l,96)then
return 35;
else
return 40;
end
end
function a:toV4mapped()
if self.proto~="IPv4"then return nil,"No IPv4 address"end
local e=t("::ffff:"..self.normal);
return e;
end
function a:label()
if self.proto=="IPv4"then
return m(self.toV4mapped);
else
return m(self);
end
end
function a:precedence()
if self.proto=="IPv4"then
return f(self.toV4mapped);
else
return f(self);
end
end
function a:scope()
if self.proto=="IPv4"then
return p(self);
else
return w(self);
end
end
local n=t("10.0.0.0");
local i=t("172.16.0.0");
local r=t("192.168.0.0");
local h=t("100.64.0.0");
function a:private()
local t=self.scope~=14;
if not t and self.proto=="IPv4"then
return e(self,n,8)or e(self,i,12)or e(self,r,16)or e(self,h,10);
end
return t;
end
local function n(e)
local o;
local a=e:find("/",1,true);
if a then
o=tonumber(e:sub(a+1,-1));
e=e:sub(1,a-1);
end
return t(e),o;
end
function e(a,t,e)
if not e or e>=128 or t.proto=="IPv4"and e>=32 then
return a==t;
elseif e<1 then
return true;
end
if a.proto~=t.proto then
if a.proto=="IPv4"then
a=a.toV4mapped;
elseif t.proto=="IPv4"then
t=t.toV4mapped;
e=e+(128-32);
end
end
return a.bits:sub(1,e)==t.bits:sub(1,e);
end
local function i(e)
return getmetatable(e)==o;
end
local function o(e,a)
if a%8~=0 then
return error("ip.truncate() only supports multiples of 8 bits");
end
local a=a/8;
if not i(e)then
e=t(e);
end
return t(s.ntop(e.packed:sub(1,a)..("\0"):rep(#e.packed-a)))
end
return{
new_ip=t,
commonPrefixLength=y,
parse_cidr=n,
match=e,
is_ip=i;
truncate=o;
};
end)
package.preload['util.hex']=(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=string.char;
local s=string.format;
local a=string.gsub;
local h=string.lower;
local o={};
local i={};
do
local t,e;
for a=0,255 do
t,e=n(a),s("%02x",a);
o[t]=e;
i[e]=t;
end
end
local function t(e)
return(a(e,".",o));
end
local function e(e)
return(a(h(e),"%X*(%x%x)%X*",i));
end
return{
encode=t,decode=e;
to=t,from=e;
};
end)
package.preload['util.net']=(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{
pton=function(e)
if e:find":"then
return"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
else
return"\0\0\0\0"
end
end
}
end)
package.preload['util.sslconfig']=(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=type;
local a=pairs;
local t=rawset;
local r=rawget;
local f=error;
local m=table.concat;
local u=table.insert;
local c=setmetatable;
local l=require"util.paths".resolve_relative_path;
local _ENV=nil;
local n={};
local e={};
local d=function(e)return e end
function n.options(h,s,e)
local o=h[s]or{};
if i(e)~="table"then e={e}end
for a,e in a(e)do
if e==true or e==false then
o[a]=e;
else
o[e]=true;
end
end
t(h,s,o)
end
n.verifyext=n.options;
function e.options(o)
local t={};
for a,e in a(o)do
if e then
t[#t+1]=a;
end
end
return t;
end
e.verifyext=e.options;
function e.ciphers(t)
if i(t)=="table"then
return m(t,":");
end
return t;
end
e.curveslist=e.ciphers;
e.ciphersuites=e.ciphers;
function e.key(t,a)
if i(t)=="string"then
return l(a._basedir,t);
else
return nil
end
end
e.certificate=e.key;
e.cafile=e.key;
e.capath=e.key;
e.dhparam=e.key;
local o={"sslv2","sslv3","tlsv1","tlsv1_1","tlsv1_2","tlsv1_3"};
for e=1,#o do o[o[e].."+"]=e-1;end
local function h(e)
local t=o[e.protocol];
if t then
e.protocol="sslv23";
for t=1,t do
u(e.options,"no_"..o[t]);
end
end
end
local function s(o,e)
t(o,"_cache",nil);
if i(e)=="table"then
for e,a in a(e)do
if e:sub(1,1)~="_"then
(n[e]or t)(o,e,a);
end
end
end
return o
end
local function i(i)
local o={};
for t,a in a(i)do
if t:sub(1,1)~="_"then
o[t]=(e[t]or d)(a,i);
end
end
h(o);
return o;
end
local function n(e)
local a=r(e,"_cache");
if a then
return a,nil
end
local a,o=r(e,"_context_factory")(e:final(),e);
if a then
t(e,"_cache",a);
end
return a,o
end
local e={
__index={
apply=s;
final=i;
build=n;
};
__newindex=function()
f("SSL config objects cannot be modified directly. Use :apply()")
end;
};
local function n(t,a)
return c({
_context_factory=t,
_basedir=a,
options={},
},e);
end
local function h(i)
local o=n();
for e,a in a(i)do
t(o,e,a);
end
return o
end
e.__index.clone=h;
return{
apply=s;
final=i;
_new=n;
};
end)
package.preload['util.paths']=(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=table.concat;
local e=package.config:sub(1,1);
local a={}
function a.resolve_relative_path(o,t)
if t then
o=o:gsub("%"..e.."+$","");
t=t:gsub("^%.%"..e.."+","");
local a;
if e=="/"and t:sub(1,1)~="/"then
a=true;
elseif e=="\\"and(t:sub(1,1)~="/"and(t:sub(2,3)~=":\\"and t:sub(2,3)~=":/"))then
a=true;
end
if a then
return o..e..t;
end
end
return t;
end
function a.glob_to_pattern(e)
return"^"..e:gsub("[%p*?]",function(e)
if e=="*"then
return".*";
elseif e=="?"then
return".";
else
return"%"..e;
end
end).."$";
end
function a.join(t,a,o,...)
if a then
if o then
if...then
return i({t,a,o,...},e);
end
return t..e..a..e..o;
end
return t..e..a;
end
return t;
end
function a.complement_lua_path(t)
local o=_VERSION:match(" (.+)$");
local a=package.config:sub(3,3);
local e=package.config:sub(1,1);
local o=e.."lua"..e..o..e;
if not string.find(package.path,t,1,true)then
package.path=package.path..a..t..e.."share"..o.."?.lua";
package.path=package.path..a..t..e.."share"..o.."?"..e.."init.lua";
end
if not string.find(package.path,t,1,true)then
package.cpath=package.cpath..a..t..e.."lib"..o.."?.so";
end
end
return a;
end)
package.preload['util.mathcompat']=(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
if not math.type then
local function t(e)
if type(e)=="number"then
if e%1==0 and e~=e+1 and e~=e-1 then
return"integer"
else
return"float"
end
end
end
_G.math.type=t
end
end)
package.preload['util.dnsregistry']=(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{
classes={
["IN"]=1;[1]="IN";
["CH"]=3;[3]="CH";
["HS"]=4;[4]="HS";
["ANY"]=255;[255]="ANY";
};
types={
["A"]=1;[1]="A";
["NS"]=2;[2]="NS";
["MD"]=3;[3]="MD";
["MF"]=4;[4]="MF";
["CNAME"]=5;[5]="CNAME";
["SOA"]=6;[6]="SOA";
["MB"]=7;[7]="MB";
["MG"]=8;[8]="MG";
["MR"]=9;[9]="MR";
["NULL"]=10;[10]="NULL";
["WKS"]=11;[11]="WKS";
["PTR"]=12;[12]="PTR";
["HINFO"]=13;[13]="HINFO";
["MINFO"]=14;[14]="MINFO";
["MX"]=15;[15]="MX";
["TXT"]=16;[16]="TXT";
["RP"]=17;[17]="RP";
["AFSDB"]=18;[18]="AFSDB";
["X25"]=19;[19]="X25";
["ISDN"]=20;[20]="ISDN";
["RT"]=21;[21]="RT";
["NSAP"]=22;[22]="NSAP";
["NSAP-PTR"]=23;[23]="NSAP-PTR";
["SIG"]=24;[24]="SIG";
["KEY"]=25;[25]="KEY";
["PX"]=26;[26]="PX";
["GPOS"]=27;[27]="GPOS";
["AAAA"]=28;[28]="AAAA";
["LOC"]=29;[29]="LOC";
["NXT"]=30;[30]="NXT";
["EID"]=31;[31]="EID";
["NIMLOC"]=32;[32]="NIMLOC";
["SRV"]=33;[33]="SRV";
["ATMA"]=34;[34]="ATMA";
["NAPTR"]=35;[35]="NAPTR";
["KX"]=36;[36]="KX";
["CERT"]=37;[37]="CERT";
["A6"]=38;[38]="A6";
["DNAME"]=39;[39]="DNAME";
["SINK"]=40;[40]="SINK";
["OPT"]=41;[41]="OPT";
["APL"]=42;[42]="APL";
["DS"]=43;[43]="DS";
["SSHFP"]=44;[44]="SSHFP";
["IPSECKEY"]=45;[45]="IPSECKEY";
["RRSIG"]=46;[46]="RRSIG";
["NSEC"]=47;[47]="NSEC";
["DNSKEY"]=48;[48]="DNSKEY";
["DHCID"]=49;[49]="DHCID";
["NSEC3"]=50;[50]="NSEC3";
["NSEC3PARAM"]=51;[51]="NSEC3PARAM";
["TLSA"]=52;[52]="TLSA";
["SMIMEA"]=53;[53]="SMIMEA";
["HIP"]=55;[55]="HIP";
["NINFO"]=56;[56]="NINFO";
["RKEY"]=57;[57]="RKEY";
["TALINK"]=58;[58]="TALINK";
["CDS"]=59;[59]="CDS";
["CDNSKEY"]=60;[60]="CDNSKEY";
["OPENPGPKEY"]=61;[61]="OPENPGPKEY";
["CSYNC"]=62;[62]="CSYNC";
["ZONEMD"]=63;[63]="ZONEMD";
["SVCB"]=64;[64]="SVCB";
["HTTPS"]=65;[65]="HTTPS";
["SPF"]=99;[99]="SPF";
["NID"]=104;[104]="NID";
["L32"]=105;[105]="L32";
["L64"]=106;[106]="L64";
["LP"]=107;[107]="LP";
["EUI48"]=108;[108]="EUI48";
["EUI64"]=109;[109]="EUI64";
["TKEY"]=249;[249]="TKEY";
["TSIG"]=250;[250]="TSIG";
["IXFR"]=251;[251]="IXFR";
["AXFR"]=252;[252]="AXFR";
["MAILB"]=253;[253]="MAILB";
["MAILA"]=254;[254]="MAILA";
["*"]=255;[255]="*";
["URI"]=256;[256]="URI";
["CAA"]=257;[257]="CAA";
["AVC"]=258;[258]="AVC";
["DOA"]=259;[259]="DOA";
["AMTRELAY"]=260;[260]="AMTRELAY";
["TA"]=32768;[32768]="TA";
["DLV"]=32769;[32769]="DLV";
};
errors={
[0]="NoError";["NoError"]="No Error";
[1]="FormErr";["FormErr"]="Format Error";
[2]="ServFail";["ServFail"]="Server Failure";
[3]="NXDomain";["NXDomain"]="Non-Existent Domain";
[4]="NotImp";["NotImp"]="Not Implemented";
[5]="Refused";["Refused"]="Query Refused";
[6]="YXDomain";["YXDomain"]="Name Exists when it should not";
[7]="YXRRSet";["YXRRSet"]="RR Set Exists when it should not";
[8]="NXRRSet";["NXRRSet"]="RR Set that should exist does not";
[9]="NotAuth";["NotAuth"]="Server Not Authoritative for zone";
[10]="NotZone";["NotZone"]="Name not contained in zone";
[11]="DSOTYPENI";["DSOTYPENI"]="DSO-TYPE Not Implemented";
[16]="BADVERS";["BADVERS"]="Bad OPT Version";
[17]="BADKEY";["BADKEY"]="Key not recognized";
[18]="BADTIME";["BADTIME"]="Signature out of time window";
[19]="BADMODE";["BADMODE"]="Bad TKEY Mode";
[20]="BADNAME";["BADNAME"]="Duplicate key name";
[21]="BADALG";["BADALG"]="Algorithm not supported";
[22]="BADTRUNC";["BADTRUNC"]="Bad Truncation";
[23]="BADCOOKIE";["BADCOOKIE"]="Bad/missing Server Cookie";
};
};
end)
package.preload['net.tls_luasec']=(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"ssl";
local o=t.newcontext;
local s=t.context or require"ssl.context";
local n=io.open;
local e={};
local h={__index=e};
function e:set_sni_host(a,t,e)
local e,t=self._builder:clone():apply({
certificate=t,
key=e,
}):build();
if not e then
return false,t
end
self._sni_contexts[a]=e._inner
return true,nil
end
function e:remove_sni_host(e)
self._sni_contexts[e]=nil
end
function e:wrap(e)
local e,t,a=pcall(t.wrap,e,self._inner);
if not e then
return nil,a
end
return t,nil
end
local function i(e,i)
if type(e.dhparam)=="string"then
local t,a=n(e.dhparam);
if not t then return nil,"Could not open DH parameters: "..a end
local a=t:read("*a");
t:close();
e.dhparam=function()return a;end
end
local t,a=o(e);
if not t then
return nil,a
end
if t and e.ciphers then
local o;
o,a=s.setcipher(t,e.ciphers);
if not o then
return nil,a
end
end
return setmetatable({
_inner=t,
_builder=i,
_sni_contexts={},
},h),nil
end
return{
new_context=i,
};
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 s,h=require"mime".b64,require"mime".unb64;
local t=require"util.hashes";
local i=require"bit";
local l=require"util.random";
local c=tonumber;
local a,e=string.char,string.byte;
local o=string.gsub;
local i=i.bxor;
local function r(n,t)
return(o(n,"()(.)",function(n,o)
return a(i(e(o),e(t,n)))
end));
end
local y,t=t.sha1,t.hmac_sha1;
local function w(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=r(a,e);
end
return a;
end
local function f(e)
return e;
end
local function a(e)
return(o(e,"[,=]",{[","]="=2C",["="]="=3D"}));
end
local function n(e)
if e:ssl()then
local e=e:socket();
if e.info and e:info().protocol=="TLSv1.3"then
if e.exportkeyingmaterial then
return"p=tls-exporter",e:exportkeyingmaterial("EXPORTER-Channel-Binding",32,"");
end
elseif e.getfinished then
return"p=tls-unique",e:getfinished();
end
end
end
local function d(e,i)
local a="n="..a(e.username);
local o=s(l.bytes(15));
local u="r="..o;
local l=a..","..u;
local d="";
local a="n";
if i=="SCRAM-SHA-1-PLUS"then
a,d=n(e.conn);
elseif n(e.conn)then
a="y";
end
local n=a..",,";
local a=n..l;
local a,m=coroutine.yield(a);
if a~="challenge"then return false end
local a,i,p=m:match("(r=[^,]+),s=([^,]*),i=(%d+)");
local c=c(p);
i=h(i);
if not a or not i or not c then
return false,"Could not parse server_first_message";
elseif a:find(o,3,true)~=3 then
return false,"nonce sent by server does not match our nonce";
elseif a==u then
return false,"server did not append s-nonce to nonce";
end
local o=n..d;
local o="c="..s(o);
local d=o..","..a;
local a;
local o;
local n;
if e.client_key and e.server_key then
o=e.client_key;
n=e.server_key;
else
if e.salted_password then
a=e.salted_password;
elseif e.password then
a=w(f(e.password),i,c);
end
n=t(a,"Server Key");
o=t(a,"Client Key");
end
local a=y(o);
local e=l..","..m..","..d;
local a=t(a,e);
local o=r(o,a);
local a=t(n,e);
local e="p="..s(o);
local e=d..","..e;
local t,e=coroutine.yield(e);
if t~="success"then return false,"success-expected"end
local e=e:match("v=([^,]+)");
if h(e)~=a 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
if n(e.conn)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("\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['util.sasl.oauthbearer']=(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=="OAUTHBEARER"and t.username then
return function(e)
local t=e.bearer_token and("Bearer "..e.bearer_token)or"";
local t,a=coroutine.yield("n,a="..e.username.."@"..e.host..",\001auth="..t.."\001");
if t=="success"then
return true;
elseif t=="challenge"then
e:event("oauth-failure",{
json=a;
});
if coroutine.yield("\001")~="failure"then
error("Unexpected SASL state: expected failure after challenge");
end
return false;
end
end,t.bearer_token and 6 or 4;
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...");
local t=a.tls_builder(".")
:apply({mode="client",protocol="sslv23",options="no_sslv2",capath="/etc/ssl/certs"})
:apply(e.ssl or{});
e.conn:starttls(t:build(),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,r=require"mime".b64,require"mime".unb64;
local o="urn:ietf:params:xml:ns:xmpp-sasl";
function n.plugins.sasl(e)
local function d(t)
if e.authenticated then return;end
e:debug("Authenticating with SASL...");
local t=t:get_child("mechanisms",o);
if not t then return end
local a={};
local i={};
local h={};
for t in t:childtags("mechanism")do
t=t:get_text();
e:debug("Server offers %s",t);
h[t]=true;
if not a[t]then
local n=t:match("[^-]+");
local s,o=pcall(require,"util.sasl."..n:lower());
if s then
e:debug("Loaded SASL %s module",n);
a[t],i[t]=o(e,t);
elseif not tostring(o):match("not found")then
e:debug("Loading failed: %s",tostring(o));
end
end
end
local t={};
for e in pairs(a)do
table.insert(t,e);
end
if not t[1]then
e:event("authentication-failure",{condition="no-supported-sasl-mechanisms",mechanisms=h});
e:close();
return;
end
table.sort(t,function(t,e)return i[t]>i[e];end);
local t,i=t[1];
e:debug("Selecting %s mechanism...",t);
e.sasl_mechanism=coroutine.wrap(a[t]);
i=e:sasl_mechanism(t);
local t=n.stanza("auth",{xmlns=o,mechanism=t});
if i then
t:text(s(i));
end
e:send(t);
return true;
end
local function a(t)
if t.name=="failure"then
local a=t.tags[1];
local t=t:get_child_text("text");
e:event("authentication-failure",{condition=a.name,text=t});
e:close();
return false;
end
local t,a=e.sasl_mechanism(t.name,r(t:get_text()));
if not t then
e:event("authentication-failure",{condition=a});
e:close();
return false;
elseif t==true then
e:event("authentication-success");
e.authenticated=true
e:reopen();
else
e:send(n.stanza("response",{xmlns=o}):text(s(t)));
end
return true;
end
e:hook("stream-features",d,300);
e:hook("stream/"..o,a);
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({id="bind",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 o,t,a=t:get_error();
e:event("bind-failure",{error=t,text=a,type=o});
end
end);
end
e:hook("stream-features",i,200);
return true;
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=nil;
local a=nil;
local r=nil;
local i;
local o=nil;
local function l(t)
if o and(t.attr.xmlns=="jabber:client"or not t.attr.xmlns)then
o=o+1;
e:debug("Increasing handled stanzas to %d for %s",o,t:top_tag());
end
end
local function d(a)
if t and(a.name and not a.attr.xmlns)then
t[#t+1]=tostring(a);
r=h();
if not i then
i=true;
e:debug("Waiting to send ack request...");
n.add_task(1,function()
if#t==0 then
i=false;
return;
end
local a=h()-r;
if a<1 and#t<10 then
return 1-a;
end
e:debug("Time up, sending <r>...");
i=false;
e:send(n.stanza("r",{xmlns=s}));
end);
end
end
end
local function u()
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 c()
e.resumption_token=nil;
end
local function m(i)
if i.name=="r"then
e:debug("Ack requested... acking %d handled stanzas",o);
e:send(n.stanza("a",{xmlns=s,h=tostring(o)}));
elseif i.name=="a"then
local o=tonumber(i.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;
elseif o<a then
e:warn("Received bad ack for "..o.." when last ack was "..a);
end
elseif i.name=="enabled"then
o=0;
e.pre_smacks_features=nil;
if i.attr.id then
e.resumption_token=i.attr.id;
end
elseif i.name=="resumed"then
e.pre_smacks_features=nil;
local o=tonumber(i.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");
elseif i.name=="failed"then
e.bound=nil
e.smacks=nil
a=nil
o=nil
t={};
local t=e.pre_smacks_features;
e.pre_smacks_features=nil;
e:event("stream-features",t);
else
e:warn("Don't know how to handle "..s.."/"..i.name);
end
end
local function i()
if e.stream_management_supported and not e.smacks then
e:debug("smacks: sending enable");
t={};
a=0;
r=h();
e:send(n.stanza("enable",{xmlns=s,resume="true"}));
e.smacks=true;
end
end
local function a(t)
if t:get_child("sm",s)then
e.pre_smacks_features=t;
e.stream_management_supported=true;
if e.smacks and e.bound then
e:debug("Resuming stream with %d handled stanzas",o);
e:send(n.stanza("resume",{xmlns=s,
h=tostring(o),previd=e.resumption_token}));
return true;
else
end
end
end
e:hook("stream-features",a,250);
e:hook("stream/"..s,m);
e:hook("bind-success",i,1);
e:hook("stanza",l);
e:hook("outgoing",d);
e:hook("closed",c,100);
e:hook("disconnected",u,100);
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 r=require"util.caps".calculate_hash;
local n="http://jabber.org/protocol/caps";
local e="http://jabber.org/protocol/disco";
local o=e.."#info";
local i=e.."#items";
function t.plugins.disco(e)
e:add_plugin("presence");
local s={
__index=function(a,e)
local t={identities={},features={}};
if e=="identities"or e=="features"then
return a[false][e]
end
a[e]=t;
return t;
end,
};
local a={
__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,
[o]=true,
[i]=true,
},
},
},s);
items=setmetatable({[false]={}},a);
};
e.caps={}
e.caps.node='http://code.matthewwild.co.uk/verse/'
local function h(a)
local i=e.disco.info[a or false];
if a and a==e.caps.node.."#"..e.caps.hash then
i=e.disco.info[false];
end
local n,i=i.identities,i.features
local e=t.stanza("query",{
xmlns=o,
node=a,
});
for a,t in pairs(n)do
e:tag('identity',t):up()
end
for t in pairs(i)do
e:tag('feature',{var=t}):up()
end
return e;
end
setmetatable(e.caps,{
__call=function(...)
local a=r(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:event("disco-info-changed");
end
function e:add_identity(a,t)
local t=self.disco.info[t or false].identities;
t[#t+1]=a;
e:event("disco-info-changed");
end
function e:add_disco_feature(t,a)
local t=t.var or t;
self.disco.info[a or false].features[t]=true;
e:event("disco-info-changed");
end
function e:remove_disco_feature(t,a)
local t=t.var or t;
self.disco.info[a or false].features[t]=nil;
e:event("disco-info-changed");
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(a,o)
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,a,o)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,h)
local t=t.iq({to=e,type="get"})
:tag("query",{xmlns=o,node=a});
self:send_iq(t,function(t)
if t.attr.type=="error"then
return h(nil,t:get_error());
end
local n,s,i={},{},{};
for e in t:get_child("query",o):childtags()do
if e.name=="identity"then
n[e.attr.category.."/"..e.attr.type]=e.attr.name or true;
elseif e.name=="feature"then
s[e.attr.var]=true;
end
end
for t in t:get_child("query",o):childtags("x","jabber:x:data")do
local e=t:get_child_with_attr("field",nil,"var","FORM_TYPE");
local e=e and e:get_child_text("value");
if e then
i[e]=t;
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=n;
self.disco.cache[e].nodes[a].features=s;
self.disco.cache[e].nodes[a].extended=i;
else
self.disco.cache[e].identities=n;
self.disco.cache[e].features=s;
self.disco.cache[e].extended=i;
end
return h(self.disco.cache[e]);
end);
end
function e:disco_items(a,o,n)
local t=t.iq({to=a,type="get"})
:tag("query",{xmlns=i,node=o});
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",i):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 o then
if not self.disco.cache[a].nodes[o]then
self.disco.cache[a].nodes[o]={nodes={}};
end
self.disco.cache[a].nodes[o].items=t;
else
self.disco.cache[a].items=t;
end
return n(t);
end);
end
e:hook("iq/"..o,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/"..i,function(o)
local a=o.tags[1];
if o.attr.type=='get'and a.name=="query"then
local n=e.disco.items[a.attr.node or false];
local t=t.reply(o):tag('query',{
xmlns=i,
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 a(a)
local t=e.disco.cache[a];
if t then
for t in pairs(t.identities)do
local t,o=t:match("^(.*)/(.*)$");
e:event("disco/service-discovered/"..t,{
type=o,jid=a;
});
end
end
end
e:disco_info(e.host,nil,function()
a(e.host);
end);
e:disco_local_services(function(t)
for o,t in ipairs(t)do
a(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);
e:hook("disco-info-changed",function()
e:resend_presence();
end);
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(i,t)
t=t or function(e)return self:event("version/response",e);end
self:send_iq(o.iq({type="get",to=i})
:tag("query",{xmlns=a}),
function(o)
if o.attr.type=="result"then
local e=o:get_child("query",a);
local a=e and e:get_child_text("name");
local o=e and e:get_child_text("version");
local e=e and e:get_child_text("os");
t({
name=a;
version=o;
platform=e;
});
else
local e,a,o=o:get_error();
t({
error=true;
condition=a;
text=o;
type=e;
});
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 t=require"verse";
local o=require"socket".gettime;
local s=require"util.id".short;
local n="urn:xmpp:ping";
function t.plugins.ping(e)
function e:ping(a,i)
local h=o();
local s=s();
local t=t.iq{id=s,to=a,type="get"}:tag("ping",{xmlns=n});
e:send_iq(t,
function(e)
if e.attr.type=="error"then
local o,e,t=e:get_error();
if e~="service-unavailable"and e~="feature-not-implemented"then
i(nil,a,{type=o,condition=e,text=t});
return;
end
end
i(o()-h,a);
end);
return s;
end
e:hook("iq/"..n,function(a)
return e:send(t.reply(a));
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 o,t,e=e:get_error();
a({
error=true;
condition=t;
text=e;
type=o;
});
end
end);
end
return true;
end
end)
package.preload['verse.plugins.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
local e=require"verse";
local i=require"util.datetime";
local o="urn:xmpp:time";
function e.plugins.time(t)
function t:query_time(a,t)
t=t or function(e)return self:event("time/response",e);end
self:send_iq(e.iq({type="get",to=a})
:tag("time",{xmlns=o}),
function(a)
if a.attr.type=="result"then
local e=a:get_child("time",o);
local e={
tzo=e:get_child_text("tzo");
utc=e:get_child_text("utc");
};
if e.utc then
e.timestamp=i.parse(e.utc);
end
t(e);
else
local e,a,o=a:get_error();
t({
error=true;
condition=a;
text=o;
type=e;
});
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 o=require"verse";
local a="urn:xmpp:blocking";
function o.plugins.blocking(e)
e.blocking={};
function e.blocking:block_jid(i,t)
e:send_iq(o.iq{type="set"}
:tag("block",{xmlns=a})
: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(o.iq{type="set"}
:tag("unblock",{xmlns=a})
: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(o.iq{type="set"}
:tag("unblock",{xmlns=a})
,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(o.iq{type="get"}
:tag("blocklist",{xmlns=a})
,function(e)
local a=e:get_child("blocklist",a);
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 a=require"verse";
local e=require"util.timer";
local o=require"util.id".short;
local i="urn:xmpp:jingle:1";
local h="urn:xmpp:jingle:errors:1";
local t={};
t.__index=t;
local e={};
local e={};
function a.plugins.jingle(e)
e:hook("ready",function()
e:add_disco_feature(i);
end,10);
function e:jingle(i)
return a.eventable(setmetatable(base or{
role="initiator";
peer=i;
sid=o();
stream=e;
},t));
end
function e:register_jingle_transport(e)
end
function e:register_jingle_content_type(e)
end
local function u(n)
local s=n:get_child("jingle",i);
local o=s.attr.sid;
local r=s.attr.action;
local o=e:event("jingle/"..o,n);
if o==true then
e:send(a.reply(n));
return true;
end
if r~="session-initiate"then
local t=a.error_reply(n,"cancel","item-not-found")
:tag("unknown-session",{xmlns=h}):up();
e:send(t);
return;
end
local l=s.attr.sid;
local o=a.eventable{
role="receiver";
peer=n.attr.from;
sid=l;
stream=e;
};
setmetatable(o,t);
local h;
local r,d;
for t in s:childtags()do
if t.name=="content"and t.attr.xmlns==i then
local i=t:child_with_name("description");
local a=i.attr.xmlns;
if a then
local e=e:event("jingle/content/"..a,o,i);
if e then
r=e;
end
end
local a=t:child_with_name("transport");
local i=a.attr.xmlns;
d=e:event("jingle/transport/"..i,o,a);
if r and d then
h=t;
break;
end
end
end
if not r then
e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified content is not supported"));
return true;
end
if not d then
e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified transport is not supported"));
return true;
end
e:send(a.reply(n));
o.content_tag=h;
o.creator,o.name=h.attr.creator,h.attr.name;
o.content,o.transport=r,d;
function o:decline()
end
e:hook("jingle/"..l,function(e)
if e.attr.from~=o.peer then
return false;
end
local e=e:get_child("jingle",i);
return o:handle_command(e);
end);
e:event("jingle",o);
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,o,t)
local e=a.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(o);
if not t then
self.stream:send(e);
else
self.stream:send_iq(e,t);
end
end
function t:accept(t)
local a=a.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"),t);
a:add_child(o);
local t=self.transport:generate_accept(self.content_tag:child_with_name("transport"),t);
a:add_child(t);
local t=self;
e:send_iq(a,function(a)
if a.attr.type=="error"then
local a,t,a=a:get_error();
e:error("session-accept rejected: %s",t);
return false;
end
t.transport:connect(function(a)
e:warn("CONNECTED (receiver)!!!");
t.state="active";
t:event("connected",a);
end);
end);
end
e:hook("iq/"..i,u);
return true;
end
function t:offer(t,o)
local e=a.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,o);
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=a.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 o=require"verse";
local a=require"ltn12";
local n=package.config:sub(1,1);
local t="urn:xmpp:jingle:apps:file-transfer:4";
function o.plugins.jingle_ft(e)
e:hook("ready",function()
e:add_disco_feature(t);
end,10);
local i={type="file"};
function i:generate_accept(t,e)
if e and e.save_file then
self.jingle:hook("connected",function()
local e=a.sink.file(io.open(e.save_file,"w+"));
self.jingle:set_sink(e);
end);
end
return t;
end
local i={__index=i};
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},i);
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 o.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(i,t)
local e,o=io.open(t);
if not e then return e,o;end
local o=e:seek("end",0);
e:seek("set",0);
local a=a.source.file(e);
local e=self:jingle(i);
e:offer("file",{
filename=t:match("[^"..n.."]+$");
size=o;
});
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 t=require"verse";
local o="urn:xmpp:jingle:transports:s5b:1";
local l="http://jabber.org/protocol/bytestreams";
local h=require"util.hashes".sha1;
local d=require"util.id".short;
local function r(e,s)
local function n()
e:unhook("connected",n);
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,#s)..s.."\0\0");
e:hook("incoming-raw",o,100);
return true;
end
e:hook("connected",n,200);
e:hook("incoming-raw",i,100);
e:send("\005\001\000");
end
local function n(a,e,i)
local e=t.new(nil,{
streamhosts=e,
current_host=0;
});
local function t(o)
if o then
return a(nil,o.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
r(e,i);
return true;
end
e:unhook("disconnected",t);
return a(nil);
end
e:hook("disconnected",t,100);
e:hook("connected",function()
e:unhook("disconnected",t);
a(e.streamhosts[e.current_host],e);
end,100);
t();
return e;
end
function t.plugins.jingle_s5b(e)
e:hook("ready",function()
e:add_disco_feature(o);
end,10);
local a={};
function a:generate_initiate()
self.s5b_sid=d();
local a=t.stanza("transport",{xmlns=o,
mode="tcp",sid=self.s5b_sid});
local t=0;
for o,i in pairs(e.proxy65.available_streamhosts)do
t=t+1;
a:tag("candidate",{jid=o,host=i.host,
port=i.port,cid=o,priority=t,type="proxy"}):up();
end
e:debug("Have %d proxies",t)
return a;
end
function a:generate_accept(e)
local a={};
self.s5b_peer_candidates=a;
self.s5b_mode=e.attr.mode or"tcp";
self.s5b_sid=e.attr.sid or self.jingle.sid;
for e in e:childtags()do
a[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=t.stanza("transport",{xmlns=o});
return e;
end
function a:connect(i)
e:warn("Connecting!");
local a={};
for t,e in pairs(self.s5b_peer_candidates or{})do
a[#a+1]=e;
end
if#a>0 then
self.connecting_peer_candidates=true;
local function s(a,e)
self.jingle:send_command("transport-info",t.stanza("content",{creator=self.creator,name=self.name})
:tag("transport",{xmlns=o,sid=self.s5b_sid})
:tag("candidate-used",{cid=a.cid}));
self.onconnect_callback=i;
self.conn=e;
end
local e=h(self.s5b_sid..self.peer..e.jid,true);
n(s,a,e);
else
e:warn("Actually, I'm going to wait for my peer to tell me its streamhost...");
self.onconnect_callback=i;
end
end
function a:info_received(a)
e:warn("Info received");
local s=a: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 a=i:child_with_name("candidate-used");
if a then
local function i(i,e)
if self.jingle.role=="initiator"then
self.jingle.stream:send_iq(t.iq({to=i.jid,type="set"})
:tag("query",{xmlns=l,sid=self.s5b_sid})
:tag("activate"):text(self.jingle.peer),function(i)
if i.attr.type=="result"then
self.jingle:send_command("transport-info",t.stanza("content",s.attr)
:tag("transport",{xmlns=o,sid=self.s5b_sid})
:tag("activated",{cid=a.attr.cid}));
self.conn=e;
self.onconnect_callback(e);
else
self.jingle.stream:error("Failed to activate bytestream");
end
end);
end
end
self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]);
local t={
self.jingle.stream.proxy65.available_streamhosts[a.attr.cid];
};
local e=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 a:disconnect()
if self.conn then
self.conn:close();
end
end
function a:handle_accepted(e)
end
local t={__index=a};
e:hook("jingle/transport/"..o,function(e)
return setmetatable({
role=e.role,
peer=e.peer,
stream=e.stream,
jingle=e,
},t);
end);
end
end)
package.preload['verse.plugins.proxy65']=(function(...)
local _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 h=require"util.id".short;
local r=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,n)
local e=a.new(nil,{
target_jid=t;
bytestream_sid=h();
});
local o=a.iq{type="set",to=t}
:tag("query",{xmlns=i,mode="tcp",sid=e.bytestream_sid});
for t,e in ipairs(n or self.proxies)do
o:tag("streamhost",e):up();
end
self.stream:send_iq(o,function(o)
if o.attr.type=="error"then
local o,t,a=o:get_error();
e:event("connection-failed",{conn=e,type=o,condition=t,text=a});
else
local o=o.tags[1]:get_child("streamhost-used");
e.streamhost_jid=o.attr.jid;
local o,h;
for a,t in ipairs(n or self.proxies)do
if t.jid==e.streamhost_jid then
o,h=t.host,t.port;
break;
end
end
e:connect(o,h);
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,t,a,o)
local i=r(t..a..o);
local function s()
e:unhook("connected",s);
return true;
end
local function n(t)
e:unhook("incoming-raw",n);
if t:sub(1,2)~="\005\000"then
return e:event("error","connection-failure");
end
e:event("connected");
return true;
end
local function a(o)
e:unhook("incoming-raw",a);
if o~="\005\000"then
local t="version-mismatch";
if o:sub(1,1)=="\005"then
t="authentication-failure";
end
return e:event("error",t);
end
e:send(string.char(5,1,0,3,#i)..i.."\0\0");
e:hook("incoming-raw",n,100);
return true;
end
e:hook("connected",s,200);
e:hook("incoming-raw",a,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.id".short;
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 a={};
local h={__index=a};
local function r(t)
local t=setmetatable({stream=t},h)
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=r(self.stream);
e.sid=self.sid or e.sid;
e.stanza=self.stanza or e.stanza;
e.block=self.block or e.block;
e:initiate(self.peer,self.sid,self.stanza);
self.conn=e;
end
function t:generate_initiate()
print("ibb:generate_initiate() as "..self.role);
local t=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 i=require"verse";
local n=table.insert;
local o="http://jabber.org/protocol/pubsub";
local h="http://jabber.org/protocol/pubsub#owner";
local a="http://jabber.org/protocol/pubsub#event";
local t={};
local s={__index=t};
function i.plugins.pubsub(e)
e.pubsub=setmetatable({stream=e},s);
e:hook("message",function(t)
local o=t.attr.from;
for t in t:childtags("event",a)do
local t=t:get_child("items");
if t then
local a=t.attr.node;
for t in t:childtags("item")do
e:event("pubsub/event",{
from=o;
node=a;
item=t;
});
end
for t in t:childtags("retract")do
e:event("pubsub/retraction",{
from=o;
node=a;
item=t;
});
end
end
end
end);
return true;
end
function t:create(a,t,e)
return self:service(a):node(t):create(nil,e);
end
function t:subscribe(e,t,o,a)
return self:service(e):node(t):subscribe(o,nil,a);
end
function t:publish(i,o,t,a,e)
return self:service(i):node(o):publish(t,nil,a,e);
end
local a={};
local r={__index=a};
function t:service(e)
return setmetatable({stream=self.stream,service=e},r)
end
local function t(t,r,s,a,d,h,e,n)
local t=i.iq{type=t or"get",to=r}
:tag("pubsub",{xmlns=s or o})
local o={node=d,jid=h};
if n then
for t,e in pairs(n)do
o[t]=e;
end
end
if a then t:tag(a,o);end
if e then
t:tag("item",{id=e~=true and e or nil});
end
return t;
end
function a:subscriptions(e)
self.stream:send_iq(t(nil,self.service,nil,"subscriptions")
,e and function(a)
if a.attr.type=="result"then
local t=a:get_child("pubsub",o);
local t=t and t:get_child("subscriptions");
local a={};
if t then
for e in t:childtags("subscription")do
local t=self:node(e.attr.node)
t.subscription=e;
t.subscribed_jid=e.attr.jid;
n(a,t);
end
end
e(a);
else
e(false,a:get_error());
end
end or nil);
end
function a:affiliations(a)
self.stream:send_iq(t(nil,self.service,nil,"affiliations")
,a and function(e)
if e.attr.type=="result"then
local e=e:get_child("pubsub",o);
local e=e and e:get_child("affiliations")or{};
local o={};
if e then
for t in e:childtags("affiliation")do
local e=self:node(t.attr.node)
e.affiliation=t;
n(o,e);
end
end
a(o);
else
a(false,e:get_error());
end
end or nil);
end
function a:nodes(a)
self.stream:disco_items(self.service,nil,function(e,...)
if e then
for t=1,#e do
e[t]=self:node(e[t].node);
end
end
a(e,...)
end);
end
local e={};
local o={__index=e};
function a:node(e)
return setmetatable({stream=self.stream,service=self.service,node=e},o)
end
function s:__call(t,e)
local t=self:service(t);
return e and t:node(e)or t;
end
function e:hook(a,o)
self._hooks=self._hooks or setmetatable({},{__mode='kv'});
local function t(e)
if(not e.service or e.from==self.service)and e.node==self.node then
return a(e)
end
end
self._hooks[a]=t;
self.stream:hook("pubsub/event",t,o);
return t;
end
function e:unhook(e)
if e then
local e=self._hooks[e];
self.stream:unhook("pubsub/event",e);
elseif self._hooks then
for e in pairs(self._hooks)do
self.stream:unhook("pubsub/event",e);
end
end
end
function e:create(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(e,a,o,i)
if a~=nil then
error("Node configuration is not implemented yet.");
end
self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,e or true)
:add_child(o)
,i);
end
function e:subscribe(e,o,a)
e=e or self.stream.jid;
if o~=nil then
error("Subscription configuration is not implemented yet.");
end
self.stream:send_iq(t("set",self.service,nil,"subscribe",self.node,e)
,a);
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(t,e)
if t then
return self:item(nil,e);
else
self.stream:disco_items(self.service,self.node,e);
end
end
function e:item(a,e)
self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,a)
,e);
end
function e:retract(o,e,a)
if type(e)=="function"then
e,a=false,e;
end
self.stream:send_iq(
t(
"set",
self.service,
nil,
"retract",
self.node,
nil,
o,
{notify=e and"1"or nil}
),
a
);
end
function e:purge(e,a)
self.stream:send_iq(
t(
"set",
self.service,
h,
"purge",
self.node,
nil,
nil,
{notify=e and"1"or nil}
),
a
);
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 t=require"verse";
local e="http://jabber.org/protocol/pubsub";
local e=e.."#event";
function t.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,id=t.item.attr.id,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 r="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(i,o,t)
local e=setmetatable({
stream=e,jid=i,
command=o,callback=t
},a);
return e:execute();
end
local function r(t,e)
if not(e)or e=="user"then return true;end
if type(e)=="function"then
return e(t);
end
end
function e:add_adhoc_command(o,a,h,s)
i[a]=n.new(o,a,h,s);
e:add_disco_item({jid=e.jid,node=a,name=o},t);
return i[a];
end
local function s(t)
local a=t.tags[1];
local a=a.attr.node;
local a=i[a];
if not a then return;end
if not r(t.attr.from,a.permission)then
e:send(o.error_reply(t,"auth","forbidden","You don't have permission to execute this command"):up()
:add_child(a:cmdtag("canceled")
:tag("note",{type="error"}):text("You don't have permission to execute this command")));
return true
end
return n.handle_cmd(a,{send=function(t)return e:send(t)end},t);
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",r);
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";
local o=require"util.stanza";
function a.plugins.presence(t)
t.last_presence=nil;
t:hook("presence-out",function(e)
if not e.attr.to then
t.last_presence=o.clone(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 a=require"verse";
local t="jabber:iq:private";
function a.plugins.private(n)
function n:private_set(o,i,e,n)
local t=a.iq({type="set"})
:tag("query",{xmlns=t});
if e then
if e.name==o and e.attr and e.attr.xmlns==i then
t:add_child(e);
else
t:tag(o,{xmlns=i})
:add_child(e);
end
end
self:send_iq(t,n);
end
function n:private_get(e,o,i)
self:send_iq(a.iq({type="get"})
:tag("query",{xmlns=t})
:tag(e,{xmlns=o}),
function(a)
if a.attr.type=="result"then
local t=a:get_child("query",t);
local e=t:get_child(e,o);
i(e);
end
end);
end
end
end)
package.preload['verse.plugins.roster']=(function(...)
local _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 n="urn:xmpp:features:rosterver";
local o=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",n)then
s=true;
end
end);
local function h(t)
local e=i.stanza("item",{xmlns=a});
for a,t in pairs(t)do
if a~="groups"then
e.attr[a]=t;
else
for a=1,#t do
e:tag("group"):text(t[a]):up();
end
end
end
return e;
end
local function r(a)
local e={};
local t={};
e.groups=t;
for t,a in pairs(a.attr)do
if t~="xmlns"then
e[t]=a
end
end
for e in a:childtags("group")do
o(t,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 a=s and s:get_child("item");
if a then
local n,o;
local i=a.attr.jid;
if a.attr.subscription=="remove"then
n="removed"
o=r(i);
else
n=e.items[i]and"changed"or"added";
h(a)
o=e.items[i];
end
e.ver=s.attr.ver;
if o then
t:event("roster/item-"..n,o);
end
end
t:send(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 i="jabber:iq:register";
function t.plugins.register(e)
local function a(o)
if o:get_child("register","http://jabber.org/features/iq-register")then
local t=t.iq({to=e.host_,type="set"})
:tag("query",{xmlns=i})
: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 i=select(3,n.split(e.attr.from));
local n=e:get_child_text("body");
local o=e:get_child("delay",h);
local a={
room_jid=a;
room=t;
sender=t.occupants[i];
nick=i;
body=n;
stanza=e;
delay=(o and o.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(t,o,a,e)
self:send(i.iq({type="set"})
:query(s.."#admin")
:tag("item",{nick=t,[o]=a})
:tag("reason"):text(e or""));
end
function a:set_role(t,e,a)
self:admin_set(t,"role",e,a);
end
function a:set_affiliation(e,t,a)
self:admin_set(e,"affiliation",t,a);
end
function a:kick(t,e)
self:set_role(t,"none",e);
end
function a:ban(t,e)
self:set_affiliation(t,"outcast",e);
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 i=require"verse";
local n="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 i.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=i.stanza("x",{xmlns=n})
: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",n)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 r="urn:xmpp:forward:0";
local s=os.time;
local h=require"util.datetime".parse;
local n=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 a;
e:hook("bind-success",function()
a=n(e.jid);
end);
e:hook("message",function(i)
local t=i:get_child(nil,o);
if i.attr.from==a and t then
local o=t.name;
local t=t:get_child("forwarded",r);
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 s(),
});
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 a=require"verse";
local t=require"util.stanza";
local e="urn:xmpp:mam:2"
local l="urn:xmpp:forward:0";
local d="urn:xmpp:delay";
local r=require"util.id".short;
local c=require"util.datetime".parse;
local s=require"util.datetime".datetime;
local o=require"util.dataforms".new;
local n=require"util.rsm";
local u={};
local m=o{
{name="FORM_TYPE";type="hidden";value=e;};
{name="with";type="jid-single";};
{name="start";type="text-single"};
{name="end";type="text-single";};
};
function a.plugins.archive(i)
function i:query_archive(i,a,h)
local o=r();
local i=t.iq{id=o,type="set",to=i}
:tag("query",{xmlns=e,queryid=o});
local t,r=tonumber(a["start"]),tonumber(a["end"]);
a["start"]=t and s(t);
a["end"]=r and s(r);
i:add_child(m:form(a,"submit"));
i:add_child(n.generate(a));
local t={};
local function s(a)
local e=a:get_child("result",e);
if e and e.attr.queryid==o then
local a=e:get_child("forwarded",l);
local i=e.attr.id;
local e=a:get_child("delay",d);
local o=e and c(e.attr.stamp)or nil;
local e=a:get_child("message","jabber:client")
t[#t+1]={id=i,stamp=o,message=e};
return true
end
end
self:hook("message",s,1);
self:send_iq(i,function(a)
self:unhook("message",s);
if a.attr.type=="error"then
self:warn(table.concat({a:get_error()}," "))
h(false,a:get_error())
return true;
end
local e=a:get_child("fin",e)
if e then
t.complete=e.attr.complete=="true"or e.attr.complete=="1";
local e=n.get(e);
for a,e in pairs(e or u)do t[a]=e;end
end
h(t);
return true
end);
end
local n={
always=true,[true]="always",
never=false,[false]="never",
roster="roster",
}
local function h(t)
local e={};
local a=t.attr.default;
if a then
e[false]=n[a];
end
local a=t:get_child("always");
if a then
for t in a:childtags("jid")do
local t=t:get_text();
e[t]=true;
end
end
local t=t:get_child("never");
if t then
for t in t:childtags("jid")do
local t=t:get_text();
e[t]=false;
end
end
return e;
end
local function s(o)
local a
a,o[false]=o[false],nil;
if a~=nil then
a=n[a];
end
local a=t.stanza("prefs",{xmlns=e,default=a})
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 i:archive_prefs_get(a)
self:send_iq(t.iq{type="get"}:tag("prefs",{xmlns=e}),
function(e)
if e and e.attr.type=="result"and e.tags[1]then
local t=h(e.tags[1]);
a(t,e);
else
a(nil,e);
end
end);
end
function i:archive_prefs_set(e,a)
self:send_iq(t.iq{type="set"}:add_child(s(e)),a);
end
end
end)
package.preload['verse.plugins.browsing']=(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:browsing:0";
function a.plugins.browsing(e)
e:add_plugin("pep");
function e:browsing(t,i)
if type(t)=="string"then
t={uri=t};
end
local a=a.stanza("page",{xmlns=o})
for t,e in pairs(t)do
a:tag(t):text(e):up();
end
return e:publish_pep(a,i);
end
e:hook_pep(o,function(a)
local t=a.item;
return e:event("browsing",{
from=a.from;
description=t:get_child_text"description";
keywords=t:get_child_text"keywords";
title=t:get_child_text"title";
uri=t:get_child_text"uri";
});
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 i,t=string.format,string.char;
local h,r=pairs,ipairs;
local o,s=table.insert,table.concat;
local e={};
for a=0,255 do
local t=t(a);
local a=i("%%%02x",a);
e[t]=a;
e[a]=t;
e[a:upper()]=t;
end
local function n(t)
return t and(t:gsub("[^a-zA-Z0-9.~_-]",e));
end
local function a(t)
return t and(t:gsub("%%%x%x",e));
end
local function e(e)
return e and(n(e):gsub("%%20","+"));
end
local function d(t)
local a={};
if t[1]then
for i,t in r(t)do
o(a,e(t.name).."="..e(t.value));
end
else
for i,t in h(t)do
o(a,e(i).."="..e(t));
end
end
return s(a,"&");
end
local function s(e)
if not e:match("=")then return a(e);end
local i={};
for e,t in e:gmatch("([^=&]*)=([^&]*)")do
e,t=e:gsub("%+","%%20"),t:gsub("%+","%%20");
e,t=a(e),a(t);
o(i,{name=e,value=t});
i[e]=t;
end
return i;
end
local function o(e,t)
e=","..e:gsub("[ \t]",""):lower()..",";
return e:find(","..t:lower()..",",1,true)~=nil;
end
local function t(e,t)
if t then
if e:sub(-1,-1)~="/"then e=e.."/";end
else
if e:sub(-1,-1)=="/"then e=e:sub(1,-2);end
end
if e:sub(1,1)~="/"then e="/"..e;end
return e;
end
return{
urlencode=n,urldecode=a;
formencode=d,formdecode=s;
contains_token=o;
normalize_path=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 l=tonumber;
local b=assert;
local q=require"socket.url".parse;
local t=require"util.http".urldecode;
local g=require"util.dbuffer";
local function j(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 k={};
function k.new(c,n,e,r)
local f=true;
if not e or e=="server"then f=false;else b(e=="client","Invalid parser type");end
local w=l(r and r().body_size_limit)or 10*1024*1024;
local s=l(r and r().head_size_limit)or 10*1024;
local k=l(r and r().buffer_size_limit)or w*2;
local a=g.new(k);
local d;
local h=nil;
local e;
local t;
local y;
local o;
return{
feed=function(u,i)
if o then return nil,"parse has failed";end
if not i then
if h and f and not t then
a:collapse();
e.body=a:read_chunk()or"";
e.partial=nil;
c(e);
h=nil;
elseif a:length()~=0 then
o=true;return n("unexpected-eof");
end
return;
end
if not a:write(i)then o=true;return n("max-buffer-size-exceeded");end
while a:length()>0 do
if h==nil then
local b=a:sub(1,s):find("\r\n\r\n",nil,true);
if not b then return;end
local p,u,m,s,x;
local v;
local i={};
for t in a:read(b+3):gmatch("([^\r\n]+)\r\n")do
if v then
local e,t=t:match("^([^%s:]+): *(.*)$");
if not e then o=true;return n("invalid-header-line");end
e=e:lower();
i[e]=i[e]and i[e]..","..t or t;
else
v=t;
if f then
m,s,x=t:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$");
s=l(s);
if not s then o=true;return n("invalid-status-line");end
y=not
((r and r().method=="HEAD")
or(s==204 or s==304 or s==301)
or(s>=100 and s<200));
else
p,u,m=t:match("^(%w+) (%S+) HTTP/(1%.[01])$");
if not p then o=true;return n("invalid-status-line");end
end
end
end
if not v then o=true;return n("invalid-status-line");end
d=y and i["transfer-encoding"]=="chunked";
t=l(i["content-length"]);
if f then
if not y then t=0;end
e={
code=s;
httpversion=m;
headers=i;
body=false;
body_length=t;
chunked=d;
partial=true;
responseversion=m;
responseheaders=i;
};
else
local a;
if u:byte()==47 then
local t,e=u:match("([^?]*).?(.*)");
if e==""then e=nil;end
a={path=t,query=e};
else
a=q(u);
if not(a and a.path)then o=true;return n("invalid-url");end
end
u=j(a.path);
i.host=a.host or i.host;
t=t or 0;
e={
method=p;
url=a;
path=u;
httpversion=m;
headers=i;
body=false;
body_sink=nil;
chunked=d;
partial=true;
};
end
if not t or t>w then
c(e);
if not e.body_sink and(t and t>w)then
o=true;
return n("content-length-limit-exceeded");
end
end
if d and not e.body_sink then
c(e);
if not e.body_sink then
e.body_buffer=g.new(k);
end
end
h=true;
end
if h then
if d then
local s=a:sub(1,512);
local t,i=s:match("^(%x+)[^\r\n]*\r\n()");
if not t then return;end
t=t and l(t,16);
if not t then o=true;return n("invalid-chunk-size");end
if t==0 and s:find("\r\n\r\n",i-2,true)then
local t=e.body_buffer;
if t then
e.body_buffer=nil;
t:collapse();
e.body=t:read_chunk()or"";
end
a:collapse();
local t=a:read_chunk();
t=t:gsub("^.-\r\n\r\n","");
a:write(t);
h,d=nil,nil;
e.partial=nil;
c(e);
elseif a:length()-i-1>=t then
a:discard(i-1);
(e.body_sink or e.body_buffer):write(a:read(t));
a:discard(2);
else
break;
end
elseif e.body_sink then
local i=a:read_chunk(t);
while i and(not t or t>0)do
if e.body_sink:write(i)then
if t then
t=t-#i;
end
i=a:read_chunk(t);
else
o=true;
return n("body-sink-write-failure");
end
end
if t==0 then
h=nil;
e.partial=nil;
c(e);
end
elseif not t or a:length()>=t then
b(not d)
e.body=t and a:read(t)or a:read_chunk()or"";
h=nil;
e.partial=nil;
c(e);
else
break;
end
else
break;
end
end
end;
};
end
return k;
end)
package.preload['net.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 z=require"util.encodings".base64.encode;
local T=require"socket.url"
local _=require"net.http.parser".new;
local d=require"util.http";
local O=require"util.events";
local A=require"util.x509".verify_identity;
local E=require"util.promise";
local j=require"net.http.errors";
local x=require"net.resolvers.basic";
local q=require"net.connect".connect;
local v=pcall(require,"ssl");
local s,p=table.insert,table.concat;
local l=pairs;
local y,c,t=
tonumber,tostring,debug.traceback;
local w=os.time;
local b=require"util.xpcall".xpcall;
local f=error
local o=require"util.logger".init("http");
local _ENV=nil;
local a={};
local function g(e)return(c(e):match("%x+$"));end
local n={default_port=80,default_mode="*a"};
local function k(e)o("error","Traceback[http]: %s",t(c(e),2));return e;end
local function m(e,t,...)
if not t then
o("error","Request '%s': error in callback: %s",e.id,(...));
if not e.suppress_errors then
f(...);
end
end
return...;
end
local function r(e)
local t=e.conn;
if t then
e.conn=nil;
t:close()
end
end
local function u(e,t)
if e.callback then
e.callback(t or"cancelled",0,e);
e.callback=nil;
end
if e.conn then
r(e);
end
end
local function h(e,n,a)
if not e.parser then
local function i(t)
if e.callback then
e.callback(t or"connection-closed",0,e);
e.callback=nil;
end
r(e);
end
if not n then
i(a);
return;
end
local a;
local function n(t)
if t.partial then
o("debug","Request '%s': partial response (%s%s)",
e.id,
t.chunked and"chunked, "or"",
t.body_length and("%d bytes"):format(t.body_length)or"unknown length"
);
if e.streaming_handler then
o("debug","Request '%s': Streaming via handler",e.id);
t.body_sink,a=e.streaming_handler(t);
end
return;
elseif a then
o("debug","Request '%s': Finalizing response stream");
a(t);
end
if e.callback then
e.callback(t.body,t.code,t,e);
e.callback=nil;
end
r(e);
end
local function t()
return e;
end
e.parser=_(n,i,"client",t);
end
e.parser:feed(n);
end
function n.onconnect(t)
local e=a[t];
e.write=function(...)return e.conn:write(...);end
local r=e.callback;
e.callback=function(n,a,i,s)
do
local t={http=e.http,url=e.url,request=e,response=i,content=n,code=a,callback=e.callback};
e.http.events.fire_event("response",t);
n,a,i=t.content,t.code,t.response;
end
o("debug","Request '%s': Calling callback, status %s",e.id,a or"---");
return m(e.id,b(r,k,n,a,i,s));
end
e.reader=h;
e.state="status";
e.cancel=u;
a[e.conn]=e;
if not e.insecure and t:ssl()then
local a=t:socket();
local o=a.getpeerverification and a:getpeerverification();
if not o then
e.callback("certificate-chain-invalid",0,e);
e.callback=nil;
t:close();
return;
end
local a=a.getpeercertificate and a:getpeercertificate();
if not a or not A(e.host,false,a)then
e.callback("certificate-verify-failed",0,e);
e.callback=nil;
t:close();
return;
end
end
local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"};
if e.query then
s(a,4,"?"..e.query);
end
for e,t in l(e.headers)do
s(a,e..": "..t.."\r\n");
end
s(a,"\r\n")
t:write(p(a));
if e.body then
t:write(e.body);
end
end
function n.onincoming(i,t)
local e=a[i];
if not e then
o("warn","Received response from connection %s with no request attached!",i);
return;
end
if t and e.reader then
e:reader(t);
end
end
function n.ondisconnect(t,o)
local e=a[t];
if e and e.conn then
e:reader(nil,o or"closed");
end
a[t]=nil;
end
function n.onattach(e,t)
a[e]=t;
t.conn=e;
end
function n.ondetach(e)
a[e]=nil;
end
function n.onfail(e,t)
e.http.events.fire_event("request-connection-error",{http=e.http,request=e,url=e.url,err=t});
e.callback(t or"connection failed",0,e);
end
local function m(a,h,t,i)
local e=T.parse(h);
if not(e and e.host)then
i("invalid-url",0,e);
return nil,"invalid-url";
end
e.url=h;
e.http=a;
e.time=w();
if not e.path then
e.path="/";
end
e.id=t and t.id or g(e);
do
local o={http=a,url=h,request=e,options=t,callback=i};
local a=a.events.fire_event("pre-request",o);
if a then
return a;
end
e,h,t,e.callback=o.request,o.url,o.options,o.callback;
end
local u,s,d;
local m,i=e.host,e.port;
local r=m;
if(i=="80"and e.scheme=="http")
or(i=="443"and e.scheme=="https")then
i=nil;
elseif i then
r=r..":"..i;
end
s={
["Host"]=r;
["User-Agent"]="Prosody XMPP Server";
};
if e.userinfo then
s["Authorization"]="Basic "..z(e.userinfo);
end
if t then
e.onlystatus=t.onlystatus;
d=t.body;
if d then
u="POST";
s["Content-Length"]=c(#d);
s["Content-Type"]="application/x-www-form-urlencoded";
end
if t.method then u=t.method;end
if t.headers then
for e,t in l(t.headers)do
s[e]=t;
end
end
e.insecure=t.insecure;
e.suppress_errors=t.suppress_errors;
e.streaming_handler=t.streaming_handler;
end
o("debug","Making %s %s request '%s' to %s",e.scheme:upper(),u or"GET",e.id,(t and t.suppress_url and r)or h);
e.method,e.headers,e.body=u,s,d;
local o=e.scheme=="https";
if o and not v then
f("SSL not available, unable to contact https URL");
end
local r=i and y(i)or(o and 443 or 80);
local s=a.options and a.options.use_dane;
local i=false;
if o then
i=t and t.sslctx or a.options and a.options.sslctx;
if t and t.use_dane~=nil then
s=t.use_dane;
end
end
local t=x.new(m,r,"tcp",{servername=e.host;use_dane=s});
q(t,n,{sslctx=i},e);
a.events.fire_event("request",{http=a,request=e,url=h});
return e;
end
local function e(a)
local e={
options=a;
request=function(a,t,e,o)
if o~=nil then
return m(a,t,e,o);
else
return E.new(function(n,o)
m(a,t,e,function(i,a,e,t)
if a==0 then
o(j.new(i,{request=e}));
else
e.request=t;
n(e);
end
end);
end);
end
end;
new=a and function(o)
local t={};
for e,a in l(a)do t[e]=a;end
if o then
for a,e in l(o)do t[a]=e;end
end
return e(t);
end or e;
events=O.new();
};
return e;
end
local t=e({
sslctx={mode="client",protocol="sslv23",options={"no_sslv2","no_sslv3"},alpn="http/1.1",verify="peer"};
suppress_errors=true;
});
return{
request=function(o,a,e)
return t:request(o,a,e);
end;
default=t;
new=e;
events=t.events;
urlencode=d.urlencode;
urldecode=d.urldecode;
formencode=d.formencode;
formdecode=d.formdecode;
destroy_request=r;
features={
sni=true;
};
};
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 o=require"util.encodings".stringprep.nameprep;
local r=require"util.encodings".idna.to_ascii;
local f=require"util.encodings".idna.to_unicode;
local c=require"util.encodings".base64;
local e=require"util.logger".init("x509");
local y=require"util.multitable";
local p=string.format;
local d=ipairs;
local _ENV=nil;
local u="2.5.4.3";
local h="2.5.29.17";
local i="1.3.6.1.5.5.7.8.5";
local n="1.3.6.1.5.5.7.8.7";
local function l(o,a)
local t=r(o)
if t==nil then
e("info","Host %s failed IDNA ToASCII operation",o)
return false
end
t=t:lower()
local i=t:gsub("^[^.]+%.","")
for o=1,#a do
local a=a[o]
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 w(a,t)
local i=o(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=o(t)
if a==nil then
e("info","Ignoring xmppAddr %s, failed nameprep!",t)
else
if i==a then
e("debug","Cert xmppAddr %s matched hostname",t)
return true
end
end
end
end
return false
end
local function m(i,o,t)
local a=r(i)
if a==nil then
e("info","Host %s failed IDNA ToASCII operation",i);
return false
end
if o:match("^_")==nil then o="_"..o end
a=a:lower();
local n=a:gsub("^[^.]+%.","")
for i=1,#t do
local i,t=t[i]: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 v(a,o,s)
if s.setencode then
s:setencode("utf8");
end
local t=s:extensions()
if t[h]then
local e=t[h];
local t=false
if e[i]then
t=true
if o=="_xmpp-client"or o=="_xmpp-server"then
if w(a,e[i])then return true end
end
end
if e[n]then
t=true
if o and m(a,o,e[n])then return true end
end
if e["dNSName"]then
t=true
if l(a,e["dNSName"])then return true end
end
if e["uniformResourceIdentifier"]then
t=true
end
if t then return false end
end
local o=s:subject()
local t=nil
for a=1,#o do
local a=o[a]
if a["oid"]==u then
if t then
e("info","Certificate has multiple common names")
return false
end
t=a["value"];
end
end
if t then
return l(a,{t})
end
return false
end
local function l(s)
if s.setencode then
s:setencode("utf8");
end
local a=y.new();
local e=s:extensions();
local t=e[h];
if t then
if t["dNSName"]then
for t,e in d(t["dNSName"])do
local t=e:sub(1,2)=="*.";
if t then e=e:sub(3);end
e=f(o(e));
if e then
if t then e="*."..e;end
a:set(e,"*",true);
end
end
end
if t[i]then
for t,e in d(t[i])do
e=o(e);
if e then
a:set(e,"xmpp-client",true);
a:set(e,"xmpp-server",true);
end
end
end
if t[n]then
for t,e in d(t[n])do
local t,e=e:match("^_([^.]+)%.(.*)");
if t then
e=o(e);
if e then
a:set(e,t,true);
end
end
end
end
end
local e=s:subject();
for t=1,#e do
local e=e[t];
if e.oid==u then
local e=o(e.value);
if e and r(e)then
a:set(e,"*",true);
end
end
end
return a.data;
end
local t="%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n"..
"([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-";
local function a(e)
local t,e=e:match(t);
if t and e then
return c.decode(e),t;
end
end
local i=('.'):rep(64);
local o="-----BEGIN %s-----\n%s\n-----END %s-----\n"
local function n(t,e)
e=e and e:upper()or"CERTIFICATE";
t=c.encode(t);
return p(o,e,t:gsub(i,'%0\n',(#t-1)/64),e);
end
return{
verify_identity=v;
get_identities=l;
pem2der=a;
der2pem=n;
};
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 o=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 a=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 t=self:_make_body();
local e=self.bosh_outgoing_buffer;
for o,a in ipairs(e)do
t:add_child(a);
e[o]=nil;
end
self:_make_request(t);
else
self:debug("Decided not to flush.");
end
end
function e:_make_request(i)
local e,t=o.request(self.bosh_url,{body=tostring(i)},function(o,e,t)
if e~=0 then
self.inactive_since=nil;
return self:_handle_response(o,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),a);
end
timer.add_task(a,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(i);
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';
o.request(self.bosh_url,{body=tostring(e)},function(e,t)
if t==0 then
return self:_disconnected();
end
local e=self:_parse_response(e)
if not e then
self:warn("Invalid session creation response");
self:_disconnected();
return;
end
self.bosh_sid=e.attr.sid;
self.bosh_wait=tonumber(e.attr.wait);
self.bosh_hold=tonumber(e.attr.hold);
self.bosh_max_inactivity=tonumber(e.attr.inactivity);
self.bosh_max_requests=tonumber(e.attr.requests)or self.bosh_hold;
self.connected=true;
self:event("connected");
self:_handle_response_payload(e);
end);
end
function e:_handle_response(t,a,e)
if self.bosh_waiting_requests[1]~=e then
self:warn("Server replied to request that wasn't the oldest");
for a,t in ipairs(self.bosh_waiting_requests)do
if t==e then
self.bosh_waiting_requests[a]=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(t,e)t.payload:add_child(e);end;
};
function e:_parse_response(e)
self:debug("Parsing response: %s",e);
if e==nil then
self:debug("%s",debug.traceback());
self:_disconnected();
return;
end
local t={notopen=true,stream=self};
local a=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 i=t.stream_mt;
local s=require"util.jid".split;
local h=require"net.adns";
local a=require"util.stanza";
local o=require"util.id".short;
math.randomseed((require"socket".gettime()*1e6)%2147483648);
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;
function t.iq(e)
if not e.id then
e.id=o();
end
return a.iq(e);
end
local r=require"util.xmppstream".new;
local n="http://etherx.jabber.org/streams";
local function d(e,t)
if e.priority==t.priority then
if not e.weight_r then
e.weight_r=math.random();
end
if not t.weight_r then
t.weight_r=math.random();
end
return(1+e.weight)*e.weight_r>(1+t.weight)*t.weight_r;
end
return e.priority<t.priority;
end
local o={
stream_ns=n,
stream_tag="stream",
default_ns="jabber:client"};
function o.streamopened(e,t)
e.stream_id=t.id;
if not e:event("opened",t)then
e.notopen=nil;
end
return true;
end
function o.streamclosed(e)
e.notopen=true;
if not e.closed then
e:send("</stream:stream>");
e.closed=true;
end
e:event("closed");
return e:close("stream closed")
end
function o.handlestanza(t,e)
if e.attr.xmlns==n then
return t:event("stream-"..e.name,e);
elseif e.attr.xmlns then
return t:event("stream/"..e.attr.xmlns,e);
end
return t:event("stanza",e);
end
function o.error(a,t,e)
if a:event(t,e)==nil then
if e then
local t=e:get_child(nil,"urn:ietf:params:xml:ns:xmpp-streams");
local e=e:get_child_text("text","urn:ietf:params:xml:ns:xmpp-streams");
error(t.name..(e and": "..e or""));
else
error(e and e.name or t or"unknown-error");
end
end
end
function i:reset()
if self.stream then
self.stream:reset();
else
self.stream=r(self,o);
end
self.notopen=true;
return true;
end
function i:connect_client(e,a,i,o)
self.jid,self.password=e,a;
self.client_key,self.server_key=i,o;
self.username,self.host,self.resource=s(e);
self:add_plugin("tls");
self:add_plugin("sasl");
self:add_plugin("bind");
function self.data(t,e)
local a,t=self.stream:feed(e);
if a then return;end
self:debug("Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," "));
self:close("xml-not-well-formed");
end
self:hook("connected",function()self:reopen();end);
self:hook("incoming-raw",function(e)return self.data(self.conn,e);end);
self:hook("read-timeout",function()self:send(" ");return true;end,-1);
self.curr_id=0;
self.tracked_iqs={};
self:hook("stanza",function(e)
local t,a=e.attr.id,e.attr.type;
if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then
self.tracked_iqs[t](e);
self.tracked_iqs[t]=nil;
return true;
end
end);
self:hook("stanza",function(e)
local a;
if e.attr.xmlns==nil or e.attr.xmlns=="jabber:client"then
if e.name=="iq"and(e.attr.type=="get"or e.attr.type=="set")then
local o=e.tags[1]and e.tags[1].attr.xmlns;
if o then
a=self:event("iq/"..o,e);
if not a then
a=self:event("iq",e);
end
end
if a==nil then
self:send(t.error_reply(e,"cancel","service-unavailable"));
return true;
end
else
a=self:event(e.name,e);
end
end
return a;
end,-1);
self:hook("outgoing",function(e)
if e.name then
self:event("stanza-out",e);
end
end);
self:hook("stanza-out",function(e)
if not e.attr.xmlns then
self:event(e.name.."-out",e);
end
end);
local function e()
self:event("ready");
end
self:hook("session-success",e,-1)
self:hook("bind-success",e,-1);
local t=self.close;
function self:close(e)
self.close=t;
if not self.closed then
self:send("</stream:stream>");
self.closed=true;
else
return self:close(e);
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
h.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,d);
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 i:reopen()
self:reset();
self:send(a.stanza("stream:stream",{to=self.host,["xmlns:stream"]='http://etherx.jabber.org/streams',
xmlns="jabber:client",version="1.0",["xml:lang"]=self.lang}):top_tag());
end
function i:send_iq(e,a)
local t=e.attr.id or uuid.generate();
self.tracked_iqs[t]=a;
e.attr.id=t;
self:send(e);
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 t=require"verse";
local s=t.stream_mt;
local r=require"util.jid".split;
local a=require"util.stanza";
local h=require"util.hashes".sha1;
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 o="jabber:component:accept";
local i={
stream_ns=n,
stream_tag="stream",
default_ns=o};
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)
return e:event("closed");
end
function i.handlestanza(t,e)
if e.attr.xmlns==n 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 o),e);
end
return t:event("stanza",e);
end
function s:connect_component(e,n)
self.jid,self.password=e,n;
self.username,self.host,self.resource=r(e);
function self:reset()
if self.stream then
self.stream:reset();
else
self.stream=d(self,i);
end
self.notopen=true;
return true;
end
function self:reopen()
self:reset();
self:send(a.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams',
xmlns=o,version="1.0"}):top_tag());
end
function self: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 self:send_iq(t,a)
local e=self:new_id();
self.tracked_iqs[e]=a;
t.attr.id=e;
self:send(t);
end
function self:new_id()
self.curr_id=self.curr_id+1;
return tostring(self.curr_id);
end
function self.data(t,e)
local a,t=self.stream:feed(e);
if a then return;end
stream:debug("Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," "));
stream:close("xml-not-well-formed");
end
self:hook("incoming-raw",function(e)return self.data(self.conn,e);end);
self.curr_id=0;
self.tracked_iqs={};
self:hook("stanza",function(e)
local t,a=e.attr.id,e.attr.type;
if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then
self.tracked_iqs[t](e);
self.tracked_iqs[t]=nil;
return true;
end
end);
self:hook("stanza",function(e)
local a;
if e.attr.xmlns==nil or e.attr.xmlns=="jabber:client"then
if e.name=="iq"and(e.attr.type=="get"or e.attr.type=="set")then
local o=e.tags[1]and e.tags[1].attr.xmlns;
if o then
a=self:event("iq/"..o,e);
if not a then
a=self:event("iq",e);
end
end
if a==nil then
self:send(t.error_reply(e,"cancel","service-unavailable"));
return true;
end
else
a=self:event(e.name,e);
end
end
return a;
end,-1);
self:hook("opened",function(e)
print(self.jid,self.stream_id,e.id);
local e=h(self.stream_id..n,true);
self:send(a.stanza("handshake",{xmlns=o}):text(e));
self:hook("stream/"..o,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
end)
pcall(require,"luarocks.require");
local h=require"socket";
pcall(require,"ssl");
local a=require"net.server";
local s=require"util.events";
local o=require"util.logger";
local n=require"util.format".format;
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(a,o)
local t=setmetatable(o or{},t);
i=i+1;
t.id=tostring(i);
t.logger=a 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");
function e.set_log_handler(e,t)
t=t or{"debug","info","warn","error"};
o.reset();
if io.type(e)=="file"then
local o=e;
function e(a,t,e)
o:write(a,"\t",t,"\t",e,"\n");
end
end
if e then
local function i(o,a,t,...)
return e(o,a,n(t,...));
end
for t,e in ipairs(t)do
o.add_level_sink(e,i);
end
end
end
function e._default_log_handler(o,t,a)
return io.stderr:write(o,"\t",t,"\t",a,"\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 e.tls_builder(...)
return a.tls_builder(...);
end
function t:listen(t,o)
t=t or"localhost";
o=o or 0;
local e,a=a.addserver(t,o,e.new_listener(self,"server"),"*a");
if e then
self:debug("Bound to %s:%s",t,o);
self.server=e;
end
return e,a;
end
function t:connect(o,i)
o=o or"localhost";
i=tonumber(i)or 5222;
local n=h.tcp()
n:settimeout(0);
n:setoption("keepalive",true);
local s,t=n:connect(o,i);
if not s and t~="timeout"then
self:warn("connect() to %s:%d failed: %s",o,i,t);
return self:event("disconnected",{reason=t})or false,t;
end
local e=a.wrapclient(n,o,i,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:event("shutdown");
self.conn:close();
e(self.conn,t);
end
function t:debug(...)
return self.logger("debug",...);
end
function t:info(...)
return self.logger("info",...);
end
function t:warn(...)
return self.logger("warn",...);
end
function t:error(...)
return self.logger("error",...);
end
function t:event(e,...)
self:debug("Firing event: "..tostring(e));
return self.events.fire_event(e,...);
end
function t:hook(e,...)
return self.events.add_handler(e,...);
end
function t:unhook(t,e)
return self.events.remove_handler(t,e);
end
function e.eventable(e)
e.events=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
function a.onreadtimeout(e)
return t:event("read-timeout");
end
return a;
end
return e;

mercurial