local n=require("modgui")local n=n.getRightLoggerModule()local _=n.new("ConfigCommon")local c,d,a,B=type,pairs,ipairs,next
local t,h,p=string.format,string.match,string.gsub
local E=require("uci")local e
local b="1.00"local S="/etc/config.export"local C="/tmp/"local U="/etc/config.import"local D="/tmp/"local R={BOARDMNEMONIC="env.rip.board_mnemonic",PRODUCTNAME="env.var.prod_name",SERIALNUMBER="env.var.serial",MAC="env.rip.eth_mac",BUILDVERSION="env.var.friendly_sw_version_activebank",}local r={}local function n()error({info=debug.getinfo(2,'nl')})end
local function l(n)if c(n)=='string'then return n end
if c(n)~='table'or c(n.info)~='table'then return nil end
return t("line=%d, name=%s",n.info.currentline,n.info.name or"?")end
local function i(n,e)n.state="Error"n.info=e or""_:error("Error: "..e or"?")end
local f=require("lcrypto")local w="AES-256-CBC"local y="HMAC-SHA1"local m="/proc/rip/"local v="0108"local s="012b"local o="system.config."local function u(n)local t={["true"]=true,["1"]=true,["yes"]=true,["on"]=true}return(t[e:get(n)]or false)end
local function x()return u(o.."export_plaintext")end
local function P()return u(o.."import_plaintext")end
local function k()return u(o.."export_unsigned")end
local function T()return u(o.."import_unsigned")end
local function H()return u(o.."import_restricted")end
local function u(n)local n=io.open(m..n,"rb")if not n then return end
local e=n:read("*all")n:close()return e
end
local function g()local n=e:get(o.."export_commonkey")if n==nil then
n="GW"end
return n
end
local function m()local n=e:get(o.."import_commonkey")if n==nil then
n="GW"end
return n
end
local function I(t)local e
if t=="GW"then
e=u(v)elseif t=="GW_KEYD"then
e=u(s)else
n()end
if not e or#e<32 then n()end
return e:sub(1,32)end
local function M(t)local e
if t=="GW"then
e=u(v)if not e or#e<64 then n()end
return e:sub(1,64)elseif t=="GW_KEYD"then
e=u(s)if not e or#e<64 then n()end
return e:sub(33,96)else
n()end
end
local function v(i)local l,e,t,o
l,e=i:match("^([^%.]+)(.*)")if not l then n()end
if#e==0 then
return l
end
e,o=e:sub(2):match("^([^%.]+)(.*)")if not e then n()end
if e:sub(1,1)=="@"then
e,t=e:sub(2):match("^([^%[]+)(.*)")if not e then n()end
if#t==0 then
t=true
else
t=t:match("%[(%d+)%]$")if not t then n()end
end
end
if#o==0 then
return l,e,t
end
o=o:sub(2)if not o then n()end
return l,e,t,o
end
local function Y(e)local t=e:match("^'(.*)'$")if t then
return t
end
local e=e:match("^{(.*)}$")if e then
local n={}for e in e:gmatch("'(.-)'")do
n[#n+1]=e
end
return n
end
n()end
local function N(n,e)n.pkg={}for e in e:lines()do
e=e:match("^[^#]*")e=e:match("^%S+")if e then
local o,t,t
if e:sub(1,1)=="^"then
o=false
e=e:sub(2)else
o=true
end
local t,e,i,l=v(e)if i then e="@"..e end
if not n.pkg[t]then n.pkg[t]={sn={}}end
if not e then
n.pkg[t].value=o
else
if not n.pkg[t].sn[e]then n.pkg[t].sn[e]={pm={}}end
if not l then
n.pkg[t].sn[e].value=o
else
if not n.pkg[t].sn[e].pm[l]then n.pkg[t].sn[e].pm[l]={}end
n.pkg[t].sn[e].pm[l].value=o
end
end
end
end
n.all=(n.pkg["*"]and n.pkg["*"].value)or false
end
local function O(l)local t={}local function o(o)local e,l=e:get(R[o])if not e then n()end
t[#t+1]={key=o,value=e}end
t[#t+1]={key="PREAMBLE",value="THENC"}t[#t+1]={key="BACKUPVERSION",value=b}o("BOARDMNEMONIC")o("PRODUCTNAME")o("SERIALNUMBER")o("MAC")o("BUILDVERSION")if not x()then
t[#t+1]={key="CIPHERKEY",value=g()}end
if not k()then
t[#t+1]={key="SIGNATUREKEY",value=g()}end
l.header=t
t={}for e,n in a(l.header)do
t[#t+1]=n.key.."="..n.value
end
l.rawheader=table.concat(t,"\n").."\n\n"end
local function o(n,e)n[#n+1]=e
end
local function V(e,n)o(e,t("[%s]",n))end
local function K(n,e)o(n,"")end
local function L(n,e,l)o(n,t("[%s.%s]",e,l))end
local function G(n,e,e)o(n,"")end
local function s(l,i,e,n)o(l,t("%s.%s=%s",i,e,n))end
local function u(l,i,r,e,n)if c(n)=="string"then
n=p(n,"'","\\'")o(l,t("%s.%s.%s='%s'",i,r,e,n))return
end
if c(n)=="table"then
for t,e in a(n)do
n[t]="'"..p(e,"'","\\'").."'"end
o(l,t("%s.%s.%s={%s}",i,r,e,table.concat(n,',')))return
end
end
local function A(i,l,o,n)L(i,l,o)if o:sub(1,1)=="@"then
local a=o:sub(2)e:foreach(l,a,function(e)local t=t("%s[%d]",o,e['.index'])s(i,l,t,a)for e,o in d(e)do
if not h(e,"^[._]")and((not n.pm[e]and n.value)or(n.pm[e]and n.pm[e].value))then
u(i,l,t,e,o)end
end
return true
end)else
local e=e:get_all(l,o)if e then
s(i,l,o,e['.type'])for e,t in d(e)do
if not h(e,"^[._]")and((not n.pm[e]and n.value)or(n.pm[e]and n.pm[e].value))then
u(i,l,o,e,t)end
end
end
end
G(i,l,o)end
local function L(l,n,i)if i.value then
V(l,n)local o={}local a=e:get("env","var","luci_webui")if a and a=="1"then
e:foreach(n,nil,function(r)local e=r['.type']local a
if r['.anonymous']==false then
a=r['.name']if(i.sn[a]~=nil)then return true end
else
if i.sn["@"..e]~=nil then return true end
o[e]=(o[e]and o[e]+1)or 0
a=t('@%s[%d]',e,o[e])end
s(l,n,a,e)for e,t in d(r)do
if not h(e,"^[._]")then
u(l,n,a,e,t)end
end
return true
end)else
e:foreach(n,function(r)local e=r['.type']local a
if r['.anonymous']==false then
a=r['.name']if(i.sn[a]~=nil)then return true end
else
if i.sn["@"..e]~=nil then return true end
o[e]=(o[e]and o[e]+1)or 0
a=t('@%s[%d]',e,o[e])end
s(l,n,a,e)for e,t in d(r)do
if not h(e,"^[._]")then
u(l,n,a,e,t)end
end
return true
end)end
K(l,n)end
for e,t in d(i.sn)do
A(l,n,e,t)end
end
local function u(r,t)local l={}local i=e:list_configs()if not i then n()end
if t.all then
o(l,"[*]")o(l,"")end
for o,n in a(i)do
if t.pkg[n]or t.all then
local e=e:get_all(n)if B(e)then
L(l,n,t.pkg[n]or{value=true,sn={}})end
end
end
r.plaintext=table.concat(l,"\n")end
local function B(e)e.iv=f.random(16)if not e.iv then n()end
e.ciphertext=f.encrypt(w,I(g()),e.iv,e.plaintext)if not e.ciphertext then n()end
e.plaintext=nil
end
local function s(e)local t={e.rawheader}if e.plaintext then
table.insert(t,e.plaintext)else
table.insert(t,e.iv)table.insert(t,e.ciphertext)end
e.signature=f.sign(y,M(g()),table.concat(t))if not e.signature then n()end
end
local function h(e,t)os.remove(t)local t=io.open(t,"w")if not t then n()end
if not t:write(e.rawheader)then n()end
if e.plaintext then
if not t:write(e.plaintext)then n()end
else
if not t:write(e.iv,e.ciphertext)then n()end
end
if e.signature then
if not t:write(e.signature)then n()end
end
t:close()end
local function g(n)n.info="export started"local o,e
local a={}n.info="composing header info"o,e=pcall(O,a)if not o then
i(n,t("compose header failed (%s)",l(e)or"?"))return
end
n.info="reading export list"local c=io.open(S,"r")if not c then
i(n,"open export list failed")return
end
local r={}o,e=pcall(N,r,c)if not o then
i(n,t("read export list failed (%s)",l(e)or"?"))r=nil
end
c:close()if not r then return end
n.info="collecting export data"o,e=pcall(u,a,r)if not o then
i(n,t("collect export data failed (%s)",l(e)or"?"))return
end
if not x()then
n.info="encrypting export data"o,e=pcall(B,a)if not o then
i(n,t("encrypt export data failed (%s)",l(e)or"?"))return
end
end
if not k()then
n.info="signing export data"o,e=pcall(s,a)if not o then
i(n,t("sign export data failed (%s)",l(e)or"?"))return
end
end
n.info="writing export data"o,e=pcall(h,a,n.location..n.filename)if not o then
i(n,t("write export data failed (%s)",l(e)or"?"))return
end
n.state="Complete"n.info="export succesfully completed"end
function r.export_reset(n)n.state="None"n.info=""end
function r.export_init(e)local n={}r.export_reset(n)n.version=b
if e then
n.location=e
else
n.location=C
end
return n
end
function r.export_start(n,t)if not n.filename or n.filename==""then
i(n,"invalid filename")return
end
e=E.cursor(t)g(n)e:close()e=nil
end
local function i(n,e)n.state="Error"n.info=e or""_:error("Error: "..e or"?")end
local function h(t)if not e:create_config_file(t)then n()end
end
local function B(o)local t={}local l=e:get("env","var","luci_webui")if l and l=="1"then
e:foreach(o,nil,function(n)t[#t+1]=n['.name']return true
end)else
e:foreach(o,function(n)t[#t+1]=n['.name']return true
end)end
for l,t in a(t)do
if not e:delete(o,t)then n()end
end
end
local function C(n,t)e:delete(n,t)end
local function S(a,t)local l,i,o,e
l,i,o,e=v(a)if o or e then n()end
t.package=l
t.section=i
t.param=e
end
local function _(o,e)local l,t
l,t=o:match("^(.-)=(.+)")if not l then n()end
local a,i,r,o
a,i,r,o=v(l)if o then
t=Y(t)if not t then n()end
end
e.package=a
e.section=i
e.index=r
e.param=o
e.value=t
end
local function u(o)local t={}local e={}local l=o:read('*l')if l~="PREAMBLE=THENC"then
n()end
e[1]=l
while true do
local o=o:read("*l")if o==""then break end
local l,i=o:match("^([^=]+)=(.*)$")if not l or t[l]then
n()end
t[l]=i
e[#e+1]=o
end
e[#e+1]="\n"return t,table.concat(e,'\n')end
local function s(t,e)local e=io.open(e,"rb")if not e then
n()end
t.header,t.rawheader=u(e)t.rawcontent=e:read("*all")e:close()end
local function x(t)local e=e:get(R["BUILDVERSION"])if e~=t.header["BUILDVERSION"]then
if string.match(t.header["BUILDVERSION"],"_DUMMY")then
if t.header["BUILDVERSION"]~=string.format("%s%s",e,"_DUMMY")then
n()end
elseif string.match(e,"_DUMMY")then
if e~=string.format("%s%s",t.header["BUILDVERSION"],"_DUMMY")then
n()end
else
n()end
end
end
local function g(e)if e.header["SIGNATUREKEY"]~=m()or#e.rawcontent<20 then
n()end
e.signature=e.rawcontent:sub(-20,-1)e.rawcontent=e.rawcontent:sub(1,-21)if true~=f.validate(y,M(m()),e.rawheader..e.rawcontent,e.signature)then
n()end
end
local function b(e)if e.header["CIPHERKEY"]~=m()or#e.rawcontent<16 then
n()end
local t=e.rawcontent:sub(1,16)e.rawcontent=e.rawcontent:sub(17)local t=f.decrypt(w,I(m()),t,e.rawcontent)if not t then
n()end
e.rawcontent=t
end
local function k(t)local o={}for e in string.gmatch(t.rawcontent,"([^\n]+)")do
if e:match("^%S+")then
local n={}local t=e:match("^%[(.*)%]$")if t then
n.type='header'S(t,n)else
n.type='set'_(e,n)end
table.insert(o,n)end
end
t.rawcontent=nil
t.config=o
end
local function f(t,e)local n=1
while t[n]do
local l=t[n]local o
if e.pkg[l.package]==nil then
o=e.all
else
o=e.pkg[l.package].value
end
if o then
n=n+1
else
table.remove(t,n)end
end
end
local function v()local e=e:list_configs()if not e then return{}end
local n={}for t,e in a(e)do
n[e]={sectionnames={}}end
return n
end
local function _(n)h(n)return{sectionnames={}}end
local function m(n)if not n.section then
B(n.package)else
C(n.package,n.section)end
end
local function u(t,o)if t.index then
local e=e:add(t.package,t.section)if not e then n()end
if not o.sectionnames[t.section]then
o.sectionnames[t.section]={}end
o.sectionnames[t.section][t.index]=e
else
if not e:set(t.package,t.section,t.value)then n()end
end
end
local function h(t,o)local l
if t.index then
l=o.sectionnames[t.section][t.index]end
local o=c(t.value)if o=="string"then
t.value=p(t.value,"\\'","'")elseif o=="table"then
for n,e in a(t.value)do
t.value[n]=p(e,"\\'","'")end
end
if not e:set(t.package,l or t.section,t.param,t.value)then n()end
end
local function p(o)local t=v()t["*"]={}if#o==0 then n()end
for o,e in a(o)do
if e.type=='header'then
if e.package~="*"then
if not t[e.package]then
t[e.package]=_(e.package)end
m(e)t[e.package].commit=true
end
elseif e.type=='set'then
if not e.param then
u(e,t[e.package])else
h(e,t[e.package])end
else
n()end
end
for o,t in d(t)do
if t.commit then
if not e:commit(o)then n()end
end
end
end
local function o(o,t)if o and t then
if not e:set("env","var",o,t)then
n()end
end
end
local function d(n)n.info="import started"local e,o
local a={}n.info="reading import list"local c=io.open(U,"r")if not c then
i(n,"open import list failed")return
end
local r={}e,o=pcall(N,r,c)if not e then
i(n,t("read import list failed (%s)",l(o)or"?"))r=nil
end
c:close()if not r then return end
n.info="reading import data"e,o=pcall(s,a,n.location..n.filename)if not e then
i(n,t("read import data failed (%s)",l(o)or"?"))return
end
if H()then
if a.header["BUILDVERSION"]then
n.info="checking buildversion"e,o=pcall(x,a)if not e then
i(n,t("Buildversion check failed (%s)",l(o)or"?"))return
end
else
i(n,"Buildversion cannot be found")end
end
if a.header["SIGNATUREKEY"]then
n.info="checking signature"e,o=pcall(g,a)if not e then
i(n,t("signature check failed (%s)",l(o)or"?"))return
end
elseif not T()then
i(n,"unsigned import not allowed")return
end
if a.header["CIPHERKEY"]then
n.info="decrypting import data"e,o=pcall(b,a)if not e then
i(n,t("decrypt import data failed (%s)",l(o)or"?"))return
end
elseif not P()then
i(n,"plaintext import not allowed")return
end
n.info="parsing import content"e,o=pcall(k,a)if not e then
i(n,t("parse import content failed (%s)",l(o)or"?"))return
end
n.info="filtering config data"e,o=pcall(f,a.config,r)if not e then
i(n,t("filter config data failed (%s)",l(o)or"?"))return
end
r=nil
n.info="applying config data"e,o=pcall(p,a.config)if not e then
i(n,t("apply config data failed (%s)",l(o)or"?"))return
end
n.state="Complete"n.info="import succesfully completed"end
function r.import_reset(n)n.state="None"n.info=""end
function r.import_init(e)local n={}r.import_reset()if e then
n.location=e
else
n.location=D
end
return n
end
function r.import_start(n)if not n.filename or n.filename==""then
i(n,"invalid filename")return
end
e=E.cursor()d(n)e:close()e=nil
end
return r