Add some tools for interfaceing with smr
Add a script to reset passwords and another generate archives.
This commit is contained in:
parent
0f17393cd8
commit
4eb5b4a7bd
|
@ -0,0 +1,127 @@
|
|||
--[[
|
||||
Implements some utilities for accounts in an smr-style database.
|
||||
|
||||
# Lists all author_id, author_name in the database
|
||||
lua tools/accounts/main.lua ls
|
||||
|
||||
# Resets the author 13 with a newly generated passfile, and writes the newly
|
||||
# generated file to "new.passfile"
|
||||
lua tools/accounts/main.lua -o new.passfile reset_password 13
|
||||
|
||||
# Deletes the author with id 40, all posts and comments by this author will
|
||||
# also be deleted.
|
||||
lua tools/accounts/main.lua delete 40
|
||||
]]
|
||||
local function sha3(data)
|
||||
local tmpfn = os.tmpname()
|
||||
local tmpf = assert(io.open(tmpfn,"wb"))
|
||||
assert(tmpf:write(data))
|
||||
assert(tmpf:close())
|
||||
local pd = assert(io.popen("openssl dgst -sha3-512 " .. tmpfn, "r"))
|
||||
local hex = pd:read("*a")
|
||||
--[[
|
||||
hex looks like
|
||||
SHA3-512(/tmp/lua_qQtQu4)= 9ece086e9bac491fac5c1d1046ca11d737b92a2b2ebd93f005d7b710110c0a678288166e7fbe796883a4f2e9b3ca9f484f521d0ce464345cc1aec96779149c14
|
||||
]]
|
||||
hex = hex:match("= (%x+)%s*$")
|
||||
assert(pd:close())
|
||||
local ret_data = {}
|
||||
for hex_byte in hex:gmatch("%x%x") do
|
||||
table.insert(ret_data,string.char(tonumber(hex_byte,16)))
|
||||
end
|
||||
return table.concat(ret_data)
|
||||
end
|
||||
local sql = require("lsqlite3")
|
||||
local argparse = require("argparse")
|
||||
|
||||
local parser = argparse(){
|
||||
name = "tools/accounts/main.lua",
|
||||
description = "View, reset, and delete accounts",
|
||||
epilog = "Other tools included in the smr source distribution include:archive",
|
||||
}
|
||||
parser:help_max_width(80)
|
||||
parser:option("-d --database","The database file","kore_chroot/data/posts.db")
|
||||
parser:option("-o --output","Output to file (instead of stdout)")
|
||||
local ls = parser:command("ls") {
|
||||
description = "lists all user_id's and user_names in the database"
|
||||
}
|
||||
local reset_password = parser:command("reset_password") {
|
||||
description = "Resets a password for a user, pass either an account id or an account name. If the inputed string can be converted to an account_id, and the account_id exists, it is considered an account_id, even if there is a user by the same name if it were considered an account_name."
|
||||
}
|
||||
reset_password:mutex(
|
||||
reset_password:argument("account_id_or_name","The accout id or account name to reset")
|
||||
)
|
||||
local delete = parser:command("delete") {
|
||||
description = "Deletes an account from the system. All posts and comments by this user are deleted as well. Once deleted, another user may claim the same username."
|
||||
}
|
||||
delete:mutex(
|
||||
delete:argument("account_id","The accout id to reset"),
|
||||
delete:argument("account_name","The name of the account to reset")
|
||||
)
|
||||
|
||||
|
||||
args = parser:parse()
|
||||
local db,err,errmsg = sql.open(args.database, sql.OPEN_READWRITE)
|
||||
if not db then
|
||||
error(string.format("Failed to open %s : %s",args.database,errmsg))
|
||||
end
|
||||
if args.output then
|
||||
io.stdout = assert(io.open(args.output,"w"))
|
||||
end
|
||||
if args.ls then
|
||||
for row in db:rows("SELECT id, name FROM authors;") do
|
||||
local id,name = unpack(row)
|
||||
io.stdout:write(string.format("%d\t%s\n",id,name))
|
||||
end
|
||||
end
|
||||
if args.reset_password then
|
||||
local rngf = assert(io.open("/dev/urandom","rb"))
|
||||
local passlength = string.byte(rngf:read(1)) + 64
|
||||
local salt = rngf:read(64)
|
||||
local password = rngf:read(passlength)
|
||||
local authorid, authorname
|
||||
authorid = tonumber(args.account_id_or_name)
|
||||
if authorid == nil then --could not be converted to a number
|
||||
authorname = args.account_id_or_name
|
||||
end
|
||||
rngf:close()
|
||||
|
||||
assert(io.stdout:write(password))
|
||||
local hash = sha3(salt .. password)
|
||||
local stmt_update_pass
|
||||
if authorid then
|
||||
stmt_update_pass = db:prepare([[
|
||||
UPDATE authors
|
||||
SET
|
||||
salt = :salt,
|
||||
passhash = :hash
|
||||
WHERE
|
||||
id=:authorid;]]
|
||||
)
|
||||
stmt_update_pass:bind(3,authorid)
|
||||
else
|
||||
stmt_update_pass = db:prepare([[
|
||||
UPDATE authors
|
||||
SET
|
||||
salt = :salt,
|
||||
passhash = :hash
|
||||
WHERE
|
||||
name=:authorname;]]
|
||||
)
|
||||
stmt_update_pass:bind(3,authorname)
|
||||
end
|
||||
|
||||
stmt_update_pass:bind_blob(1,salt)
|
||||
stmt_update_pass:bind_blob(2,hash)
|
||||
local err = stmt_update_pass:step()
|
||||
if err ~= sql.DONE then
|
||||
io.stderr:write(string.format("Failed %d:%s",err,db:errmsg()))
|
||||
end
|
||||
end
|
||||
|
||||
db:close()
|
||||
--[[
|
||||
for k,v in pairs(args) do
|
||||
print(k,":",v)
|
||||
end
|
||||
]]
|
|
@ -0,0 +1,170 @@
|
|||
--Copyright (c) 2007-2008 Neil Richardson (nrich@iinet.net.au)
|
||||
--
|
||||
--Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
--of this software and associated documentation files (the "Software"), to deal
|
||||
--in the Software without restriction, including without limitation the rights
|
||||
--to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
--copies of the Software, and to permit persons to whom the Software is
|
||||
--furnished to do so, subject to the following conditions:
|
||||
--
|
||||
--The above copyright notice and this permission notice shall be included in all
|
||||
--copies or substantial portions of the Software.
|
||||
--
|
||||
--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
--IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
--FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
--AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
--LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
--OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
--IN THE SOFTWARE.
|
||||
local max = 2^32 -1
|
||||
|
||||
local CRC32 = {
|
||||
0,79764919,159529838,222504665,319059676,
|
||||
398814059,445009330,507990021,638119352,
|
||||
583659535,797628118,726387553,890018660,
|
||||
835552979,1015980042,944750013,1276238704,
|
||||
1221641927,1167319070,1095957929,1595256236,
|
||||
1540665371,1452775106,1381403509,1780037320,
|
||||
1859660671,1671105958,1733955601,2031960084,
|
||||
2111593891,1889500026,1952343757,2552477408,
|
||||
2632100695,2443283854,2506133561,2334638140,
|
||||
2414271883,2191915858,2254759653,3190512472,
|
||||
3135915759,3081330742,3009969537,2905550212,
|
||||
2850959411,2762807018,2691435357,3560074640,
|
||||
3505614887,3719321342,3648080713,3342211916,
|
||||
3287746299,3467911202,3396681109,4063920168,
|
||||
4143685023,4223187782,4286162673,3779000052,
|
||||
3858754371,3904687514,3967668269,881225847,
|
||||
809987520,1023691545,969234094,662832811,
|
||||
591600412,771767749,717299826,311336399,
|
||||
374308984,453813921,533576470,25881363,
|
||||
88864420,134795389,214552010,2023205639,
|
||||
2086057648,1897238633,1976864222,1804852699,
|
||||
1867694188,1645340341,1724971778,1587496639,
|
||||
1516133128,1461550545,1406951526,1302016099,
|
||||
1230646740,1142491917,1087903418,2896545431,
|
||||
2825181984,2770861561,2716262478,3215044683,
|
||||
3143675388,3055782693,3001194130,2326604591,
|
||||
2389456536,2200899649,2280525302,2578013683,
|
||||
2640855108,2418763421,2498394922,3769900519,
|
||||
3832873040,3912640137,3992402750,4088425275,
|
||||
4151408268,4197601365,4277358050,3334271071,
|
||||
3263032808,3476998961,3422541446,3585640067,
|
||||
3514407732,3694837229,3640369242,1762451694,
|
||||
1842216281,1619975040,1682949687,2047383090,
|
||||
2127137669,1938468188,2001449195,1325665622,
|
||||
1271206113,1183200824,1111960463,1543535498,
|
||||
1489069629,1434599652,1363369299,622672798,
|
||||
568075817,748617968,677256519,907627842,
|
||||
853037301,1067152940,995781531,51762726,
|
||||
131386257,177728840,240578815,269590778,
|
||||
349224269,429104020,491947555,4046411278,
|
||||
4126034873,4172115296,4234965207,3794477266,
|
||||
3874110821,3953728444,4016571915,3609705398,
|
||||
3555108353,3735388376,3664026991,3290680682,
|
||||
3236090077,3449943556,3378572211,3174993278,
|
||||
3120533705,3032266256,2961025959,2923101090,
|
||||
2868635157,2813903052,2742672763,2604032198,
|
||||
2683796849,2461293480,2524268063,2284983834,
|
||||
2364738477,2175806836,2238787779,1569362073,
|
||||
1498123566,1409854455,1355396672,1317987909,
|
||||
1246755826,1192025387,1137557660,2072149281,
|
||||
2135122070,1912620623,1992383480,1753615357,
|
||||
1816598090,1627664531,1707420964,295390185,
|
||||
358241886,404320391,483945776,43990325,
|
||||
106832002,186451547,266083308,932423249,
|
||||
861060070,1041341759,986742920,613929101,
|
||||
542559546,756411363,701822548,3316196985,
|
||||
3244833742,3425377559,3370778784,3601682597,
|
||||
3530312978,3744426955,3689838204,3819031489,
|
||||
3881883254,3928223919,4007849240,4037393693,
|
||||
4100235434,4180117107,4259748804,2310601993,
|
||||
233574846,2151335527,2231098320,2596047829,
|
||||
2659030626,2470359227,2550115596,2947551409,
|
||||
2876312838,2788305887,2733848168,3165939309,
|
||||
3094707162,3040238851,2985771188,
|
||||
}
|
||||
|
||||
local function xor(a, b)
|
||||
local calc = 0
|
||||
|
||||
for i = 32, 0, -1 do
|
||||
local val = 2 ^ i
|
||||
local aa = false
|
||||
local bb = false
|
||||
|
||||
if a == 0 then
|
||||
calc = calc + b
|
||||
break
|
||||
end
|
||||
|
||||
if b == 0 then
|
||||
calc = calc + a
|
||||
break
|
||||
end
|
||||
|
||||
if a >= val then
|
||||
aa = true
|
||||
a = a - val
|
||||
end
|
||||
|
||||
if b >= val then
|
||||
bb = true
|
||||
b = b - val
|
||||
end
|
||||
|
||||
if not (aa and bb) and (aa or bb) then
|
||||
calc = calc + val
|
||||
end
|
||||
end
|
||||
|
||||
return calc
|
||||
end
|
||||
|
||||
local function lshift(num, left)
|
||||
local res = num * (2 ^ left)
|
||||
return res % (2 ^ 32)
|
||||
end
|
||||
|
||||
local function rshift(num, right)
|
||||
local res = num / (2 ^ right)
|
||||
return math.floor(res)
|
||||
end
|
||||
|
||||
local function crc32(str)
|
||||
local count = string.len(tostring(str))
|
||||
local crc = max
|
||||
|
||||
local i = 1
|
||||
while count > 0 do
|
||||
local byte = string.byte(str, i)
|
||||
|
||||
crc = xor(lshift(crc, 8), CRC32[xor(rshift(crc, 24), byte) + 1])
|
||||
|
||||
i = i + 1
|
||||
count = count - 1
|
||||
end
|
||||
|
||||
return crc
|
||||
end
|
||||
|
||||
return crc32
|
||||
|
||||
--
|
||||
-- CRC32.lua
|
||||
--
|
||||
-- A pure Lua implementation of a CRC32 hashing algorithm. Slower than using a C implemtation,
|
||||
-- but useful having no other dependencies.
|
||||
--
|
||||
--
|
||||
-- Synopsis
|
||||
--
|
||||
-- require('CRC32')
|
||||
--
|
||||
-- crchash = CRC32.Hash('a string')
|
||||
--
|
||||
-- Methods:
|
||||
--
|
||||
-- hashval = CRC32.Hash(val)
|
||||
-- Calculates and returns (as an integer) the CRC32 hash of the parameter 'val'.
|
|
@ -0,0 +1,26 @@
|
|||
<html>
|
||||
<head>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
</head>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Tags</th>
|
||||
<th>Posted</th>
|
||||
</tr>
|
||||
<% for k,story in pairs(stories) do %>
|
||||
<tr>
|
||||
<td><a href="<%= story.filename %>"><%- story.title %></a></td>
|
||||
<td>By <%= story.author %></td>
|
||||
<td><ul class="tag-list">
|
||||
<% for i = 1,#story.tags do %>
|
||||
<li><%= story.tags[i] %></li>
|
||||
<% end %>
|
||||
</ul></td>
|
||||
<td><%= story.posted %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
<html>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Tags</th>
|
||||
<th>Posted</th>
|
||||
</tr>
|
||||
<% for k,story in pairs(stories) do %>
|
||||
<tr>
|
||||
<td><a href="<%= story.url %>"><%- story.title %></a></td>
|
||||
<td>By <%= story.author %></td>
|
||||
<td><ul class="tag-list">
|
||||
<% for i = 1,#story.tags do %>
|
||||
<li><%= story.tags[i] %></li>
|
||||
<% end %>
|
||||
</ul></td>
|
||||
<td><%= story.posted %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,109 @@
|
|||
--[[
|
||||
Implements extracting the database to an archive file, you probably want a cron
|
||||
job for this.
|
||||
|
||||
# Generates the archive
|
||||
lua tools/archive/main.lua
|
||||
]]
|
||||
local sql = require("lsqlite3")
|
||||
local argparse = require("argparse")
|
||||
local zlib = require("zlib")
|
||||
local etlua = require("etlua")
|
||||
local crc32 = require("tools.archive.crc32")
|
||||
|
||||
local parser = argparse(){
|
||||
name = "tools/archive/main.lua",
|
||||
description = "Generate a downloadable zip archive.",
|
||||
epilog = "Other tools included in the smr source distribution include:accounts",
|
||||
}
|
||||
parser:help_max_width(80)
|
||||
parser:option("-d --database","The database file","kore_chroot/data/posts.db")
|
||||
parser:option("-o --output","Output to directory","kore_chroot/data/archive")
|
||||
|
||||
args = parser:parse()
|
||||
local db,err,errmsg = sql.open(args.database, sql.OPEN_READWRITE)
|
||||
local outfile = io.open(args.output,"w")
|
||||
if not db then
|
||||
error(string.format("Failed to open %s : %s",args.database,errmsg))
|
||||
end
|
||||
|
||||
local index_f = assert(io.open("tools/archive/main.etlua"))
|
||||
local index_tmpl = assert(index_f:read("*a"))
|
||||
index_f:close()
|
||||
local template_main = etlua.compile(index_tmpl)
|
||||
|
||||
local story_f = assert(io.open("tools/archive/story.etlua"))
|
||||
story_tmpl = assert(story_f:read("*a"))
|
||||
story_f:close()
|
||||
local template_story = etlua.compile(story_tmpl)
|
||||
local zipped_files = {}
|
||||
for row in db:rows([[
|
||||
SELECT
|
||||
posts.id,
|
||||
posts.post_text,
|
||||
posts.post_title,
|
||||
authors.name,
|
||||
posts.isanon,
|
||||
posts.post_time,
|
||||
GROUP_CONCAT(tags.tag,";")
|
||||
FROM posts, authors
|
||||
LEFT JOIN tags ON tags.postid = posts.id
|
||||
WHERE
|
||||
posts.authorid = authors.id AND
|
||||
posts.unlisted = 0
|
||||
GROUP BY posts.id
|
||||
ORDER BY posts.post_time
|
||||
]]) do
|
||||
local id, text, title, author, isanon, post_time, tags_txt = unpack(row)
|
||||
local tags_txt = (tags_txt or "") .. ";"
|
||||
local tags = {}
|
||||
for tag in tags_txt:gmatch("([^;]+)") do
|
||||
table.insert(tags,tag)
|
||||
end
|
||||
local story_txt = zlib.decompress(text)
|
||||
local file_data = template_story{
|
||||
text = story_txt,
|
||||
author = isanon == 0 and author or "Anonymous",
|
||||
title = title,
|
||||
tags = tags,
|
||||
}
|
||||
local filename = string.format("s%d.html",id)
|
||||
table.insert(zipped_files,{
|
||||
filename = filename,
|
||||
author = isanon == 0 and author or "Anonymous",
|
||||
tags = tags,
|
||||
title = title,
|
||||
posted = os.date("%B %d %Y",tonumber(post_time)),
|
||||
})
|
||||
local fd = assert(io.open(args.output .. "/" .. filename,"w"))
|
||||
assert(fd:write(file_data))
|
||||
assert(fd:close())
|
||||
end
|
||||
|
||||
--index.html
|
||||
local index_txt = template_main{
|
||||
stories = zipped_files,
|
||||
}
|
||||
local fd = assert(io.open(string.format("%s/index.html",args.output),"w"))
|
||||
assert(fd:write(index_txt))
|
||||
assert(fd:close())
|
||||
|
||||
--css
|
||||
local cssfd = assert(io.open("assets/style.css","r"))
|
||||
local csstxt = assert(cssfd:read("*a"))
|
||||
assert(cssfd:close())
|
||||
local fd = assert(io.open(string.format("%s/style.css",args.output),"w"))
|
||||
assert(fd:write(csstxt))
|
||||
assert(fd:close())
|
||||
|
||||
--remove the previous zip archive if it exists
|
||||
local oldzipfd, err = io.open(args.output,"r")
|
||||
if oldzipfd then
|
||||
local rmfd = assert(io.popen(string.format("rm %s.zip",args.output),"r"))
|
||||
assert(rmfd:read("*a"))
|
||||
assert(rmfd:close())
|
||||
end
|
||||
local zipfd = assert(io.popen(string.format("zip -r -j %s %s",args.output, args.output),"r"))
|
||||
assert(zipfd:read("*a"))
|
||||
assert(zipfd:close())
|
||||
db:close()
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="keywords" value="<%= table.concat(tags,",") %>"/>
|
||||
<meta name="author" value="<%= author %>"/>
|
||||
<meta name="generator" value="smr"/>
|
||||
<meta charset="utf-8"/>
|
||||
<link href="style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
|
||||
<article>
|
||||
<h2 class="title"> <%- title %> </h2>
|
||||
<h3>
|
||||
<% if isanon or author == nil then -%>
|
||||
By Anonymous
|
||||
<% else -%>
|
||||
By <%= author %>
|
||||
<% end -%>
|
||||
</h3>
|
||||
|
||||
<%- text %>
|
||||
</article>
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
local argparse = require("argparse")
|
||||
local sql = require("lsqlite3")
|
||||
|
||||
local parser = argparse(){
|
||||
name = "tools/migrate/main.lua",
|
||||
description = "Perform database migrations",
|
||||
epilog = "Other tools included in the smr source distribution include:accounts,archive",
|
||||
}
|
||||
parser:help_max_width(80)
|
||||
parser:option("-d --database","The database file","kore_chroot/data/posts.db")
|
||||
|
||||
args = parser:parse()
|
||||
|
||||
local db,err,errmsg = sql.open(args.database, sql.OPEN_READWRITE)
|
||||
if not db then
|
||||
error(string.format("Failed to open %s : %s",args.database,errmsg))
|
||||
end
|
||||
|
||||
--Unlisted pastes update
|
||||
local db:exec([[
|
||||
UPDATE posts SET hash=randomblob(64) WHERE hash = -1;
|
||||
]])
|
Loading…
Reference in New Issue