2023-07-19 16:53:15 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
organize file structure
diff --git a/cogit.lua b/cogit.lua deleted file mode 100644 index 5457bf9..0000000 --- a/cogit.lua +++ /dev/null @@ -1,275 +0,0 @@ -local git = require "git" -local scan = require "scan" -local hash = require "hash" - -local lud = require "ludweb" - -local function time_fmt(sig) - local s = os.date("%Y-%m-%d %H:%M:%S", sig.time_) - local offset = sig.offset - local sign - if offset < 0 then - offset = -offset - sign = "-" - else - sign = "+" - end - local hours = math.floor(offset / 60) - local mins = offset % 60 - s = s .. (" (UTC%s%02d:%02d)"):format(sign, hours, mins) - return s -end - -local function diff_cb(line_type, line) - line = lud.template.escape(line:sub(1, #line-1)) - if line_type == " " then - line = ' <span class="diff_ctx">' .. line .. '</span>' - elseif line_type == "+" then - line = '+<span class="diff_add">' .. line .. '</span>' - elseif line_type == "-" then - line = '-<span class="diff_del">' .. line .. '</span>' - elseif line_type == "=" then - line = ' <span class="diff_nonl diff_ctx_nonl">' .. line .. '</span>' - elseif line_type == ">" then - line = ' <span class="diff_nonl diff_old_nonl">' .. line .. '</span>' - elseif line_type == "<" then - line = ' <span class="diff_nonl diff_new_nonl">' .. line .. '</span>' - elseif line_type == "F" then - line = '\n<span class="diff_file_hdr">' .. line .. '</span>' - elseif line_type == "H" then - line = '<span class="diff_hunk_hdr">' .. line .. '</span>' - elseif line_type == "B" then - line = '<span class="diff_bin">' .. line .. '</span>' - else - line = line_type .. line - end - return line .. "\n" -end - -local function allowed(user, gname) - if gname == "public" then - return true - elseif user == nil then - return false - end - for _, ok in ipairs(user.groups) do - if gname == ok then - return true - end - end - return false -end - -local LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG = 0, 1, 2, 3 - -local Cogit = {} -Cogit.__index = Cogit - -function Cogit:init() - if not self.initialized then - git.init() - self.initialized = true - end -end - -function Cogit:finish() - git.shutdown() - self.initialized = false -end - -local function get_user(cookies) - local session_id = cookies["sid"] - if session_id == nil then - return nil - end - local uname = sessions[session_id] - if uname == nil then - return nil - end - return conf.users[uname] -end - -function Cogit:run() - self:init() - self.conf.log(LOG_INFO, "server running on port "..self.conf.port) - self.app:run(self.conf.port) -end - -function Cogit:scan() - self.conf.log(LOG_INFO, "scanning repos in "..self.path) - self.groups = scan.scanrepos(self.path) -end - -function Cogit:log(level, msg) - self.conf.log(level, msg) -end - -function Cogit:routes() - conf = self.conf - title = conf.title - sessions = self.sessions - return { - {"GET", "/?", - function (req) - local user = get_user(req.cookies) - local gnames = {} - self:scan() - for gname in pairs(self.groups) do - if allowed(user, gname) then - table.insert(gnames, gname) - end - end - local env = {title=title, user=user, gnames=gnames} - return lud.template.render_file("view/home.html", env) - end}, - {"GET", "/login", - function (req) - local user = get_user(req.cookies) - if user ~= nil then -- already logged in - return "/", 303 - else - return lud.template.render_file("view/login.html", {title=title}) - end - end}, - {"POST", "/login", - function (req) - local uname = req.form.username - local pass = req.form.password - local user = conf.users[uname] - local salt, h - if user == nil then - -- hash something as if we're trying to login anyway - salt = hash.get_salt() - h = hash.hash_pass(pass, salt) - conf.log(LOG_INFO, "invalid username") - else - salt = lud.crypt.b64_dec(user.salt) - h = hash.hash_pass(pass, salt) - if h == lud.crypt.b64_dec(user.hash) then - local session_id = lud.crypt.b64_enc(lud.crypt.uuid4()) - sessions[session_id] = uname - conf.log(LOG_INFO, "logged in as "..uname) - local age = conf.session_age - local cookie = {key="sid", val=session_id, path="/", age=age} - return "/", 303, "See Other", {cookie} - else - conf.log(LOG_INFO, "invalid password") - end - end - return "/login", 303 - end}, - {"GET", "/logout", - function (req) - local session_id = req.cookies["sid"] - if session_id ~= nil then - conf.log(LOG_INFO, "logged out as "..sessions[session_id]) - sessions[session_id] = nil - end - return "/", 303 - end}, - {"GET", "/group/([%w_-]+)", - function (req, gname) - local user = get_user(req.cookies) - if not allowed(user, gname) then - return "/login", 303 - end - local rnames = {} - for rname in pairs(self.groups[gname]) do - table.insert(rnames, rname) - end - local env = {title=title, user=user, gname=gname, rnames=rnames} - return lud.template.render_file("view/group.html", env) - end}, - {"GET", "/group/([%w_-]+)/repo/([%w_-]+)", - function (req, gname, rname) - local user = get_user(req.cookies) - if not allowed(user, gname) then - return "/login", 303 - end - local repo = self.groups[gname][rname] - local bnames = repo:branches() - local tnames = repo:tags() - local env = { - title=title, user=user, repo=repo, gname=gname, - rname=rname, bnames=bnames, tnames=tnames, - } - return lud.template.render_file("view/repo.html", env) - end}, - {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/history/([%w_-]+)", - function (req, gname, rname, first) - local user = get_user(req.cookies) - if not allowed(user, gname) then - return "/login", 303 - end - local repo = self.groups[gname][rname] - local commit = repo:commit(first) - local prev = repo:find_prev(commit:id(), conf.limit) - local env = { - title=title, user=user, gname=gname, rname=rname, bname=bname, - commit=commit, limit=conf.limit, prev=prev, first=first, - } - return lud.template.render_file("view/history.html", env) - end}, - {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)", - function (req, gname, rname, cid) - local user = get_user(req.cookies) - if not allowed(user, gname) then - return "/login", 303 - end - local repo = self.groups[gname][rname] - local commit = repo:commit(cid) - local prev = repo:find_prev(commit:id(), 1) - local sig = commit:signature() - local time_str = time_fmt(sig) - local diff = repo:diff(commit, diff_cb) - local env = { - title=title, user=user, gname=gname, rname=rname, bname=bname, - commit=commit, time_str=time_str, sig=sig, cid=cid, prev=prev, diff=diff, - } - return lud.template.render_file("view/commit.html", env) - end}, - {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)/tree/(.*)", - function (req, gname, rname, cid, path) - local user = get_user(req.cookies) - if not allowed(user, gname) then - return "/login", 303 - end - local repo = self.groups[gname][rname] - local commit = repo:commit(cid) - local node = commit:tree_entry(path) - if node == nil then - return "File not found", 404, "Not found" - end - local parts = {} - for part in path:gmatch("[^/]+") do - table.insert(parts, part) - end - local base = req.path - if base:sub(#base) ~= "/" then - base = base .. "/" - end - local env = { - title=title, user=user, gname=gname, rname=rname, cid=cid, - path=path, base=base, parts=parts, node=node, - } - if node.type_ == "dir" then - return lud.template.render_file("view/dir.html", env) - elseif node.type_ == "file" then - return lud.template.render_file("view/file.html", env) - end - end}, -} end - -local function new_cogit(path) - local self = setmetatable({}, Cogit) - self.path = path - self.sessions = {} - self.initialized = false - self.conf = dofile(path.."/conf.lua") - self:init() - self:scan() - self.app = lud.app.new_app(self:routes()) - return self -end - -return {new_cogit=new_cogit} diff --git a/cogit/cogit.lua b/cogit/cogit.lua new file mode 100644 index 0000000..5de1996 --- /dev/null +++ b/cogit/cogit.lua @@ -0,0 +1,275 @@ +local git = require "cogit.git" +local scan = require "cogit.scan" +local hash = require "cogit.hash" + +local lud = require "ludweb" + +local function time_fmt(sig) + local s = os.date("%Y-%m-%d %H:%M:%S", sig.time_) + local offset = sig.offset + local sign + if offset < 0 then + offset = -offset + sign = "-" + else + sign = "+" + end + local hours = math.floor(offset / 60) + local mins = offset % 60 + s = s .. (" (UTC%s%02d:%02d)"):format(sign, hours, mins) + return s +end + +local function diff_cb(line_type, line) + line = lud.template.escape(line:sub(1, #line-1)) + if line_type == " " then + line = ' <span class="diff_ctx">' .. line .. '</span>' + elseif line_type == "+" then + line = '+<span class="diff_add">' .. line .. '</span>' + elseif line_type == "-" then + line = '-<span class="diff_del">' .. line .. '</span>' + elseif line_type == "=" then + line = ' <span class="diff_nonl diff_ctx_nonl">' .. line .. '</span>' + elseif line_type == ">" then + line = ' <span class="diff_nonl diff_old_nonl">' .. line .. '</span>' + elseif line_type == "<" then + line = ' <span class="diff_nonl diff_new_nonl">' .. line .. '</span>' + elseif line_type == "F" then + line = '\n<span class="diff_file_hdr">' .. line .. '</span>' + elseif line_type == "H" then + line = '<span class="diff_hunk_hdr">' .. line .. '</span>' + elseif line_type == "B" then + line = '<span class="diff_bin">' .. line .. '</span>' + else + line = line_type .. line + end + return line .. "\n" +end + +local function allowed(user, gname) + if gname == "public" then + return true + elseif user == nil then + return false + end + for _, ok in ipairs(user.groups) do + if gname == ok then + return true + end + end + return false +end + +local LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG = 0, 1, 2, 3 + +local Cogit = {} +Cogit.__index = Cogit + +function Cogit:init() + if not self.initialized then + git.init() + self.initialized = true + end +end + +function Cogit:finish() + git.shutdown() + self.initialized = false +end + +local function get_user(cookies) + local session_id = cookies["sid"] + if session_id == nil then + return nil + end + local uname = sessions[session_id] + if uname == nil then + return nil + end + return conf.users[uname] +end + +function Cogit:run() + self:init() + self.conf.log(LOG_INFO, "server running on port "..self.conf.port) + self.app:run(self.conf.port) +end + +function Cogit:scan() + self.conf.log(LOG_INFO, "scanning repos in "..self.path) + self.groups = scan.scanrepos(self.path) +end + +function Cogit:log(level, msg) + self.conf.log(level, msg) +end + +function Cogit:routes() + conf = self.conf + title = conf.title + sessions = self.sessions + return { + {"GET", "/?", + function (req) + local user = get_user(req.cookies) + local gnames = {} + self:scan() + for gname in pairs(self.groups) do + if allowed(user, gname) then + table.insert(gnames, gname) + end + end + local env = {title=title, user=user, gnames=gnames} + return lud.template.render_file("view/home.html", env) + end}, + {"GET", "/login", + function (req) + local user = get_user(req.cookies) + if user ~= nil then -- already logged in + return "/", 303 + else + return lud.template.render_file("view/login.html", {title=title}) + end + end}, + {"POST", "/login", + function (req) + local uname = req.form.username + local pass = req.form.password + local user = conf.users[uname] + local salt, h + if user == nil then + -- hash something as if we're trying to login anyway + salt = hash.get_salt() + h = hash.hash_pass(pass, salt) + conf.log(LOG_INFO, "invalid username") + else + salt = lud.crypt.b64_dec(user.salt) + h = hash.hash_pass(pass, salt) + if h == lud.crypt.b64_dec(user.hash) then + local session_id = lud.crypt.b64_enc(lud.crypt.uuid4()) + sessions[session_id] = uname + conf.log(LOG_INFO, "logged in as "..uname) + local age = conf.session_age + local cookie = {key="sid", val=session_id, path="/", age=age} + return "/", 303, "See Other", {cookie} + else + conf.log(LOG_INFO, "invalid password") + end + end + return "/login", 303 + end}, + {"GET", "/logout", + function (req) + local session_id = req.cookies["sid"] + if session_id ~= nil then + conf.log(LOG_INFO, "logged out as "..sessions[session_id]) + sessions[session_id] = nil + end + return "/", 303 + end}, + {"GET", "/group/([%w_-]+)", + function (req, gname) + local user = get_user(req.cookies) + if not allowed(user, gname) then + return "/login", 303 + end + local rnames = {} + for rname in pairs(self.groups[gname]) do + table.insert(rnames, rname) + end + local env = {title=title, user=user, gname=gname, rnames=rnames} + return lud.template.render_file("view/group.html", env) + end}, + {"GET", "/group/([%w_-]+)/repo/([%w_-]+)", + function (req, gname, rname) + local user = get_user(req.cookies) + if not allowed(user, gname) then + return "/login", 303 + end + local repo = self.groups[gname][rname] + local bnames = repo:branches() + local tnames = repo:tags() + local env = { + title=title, user=user, repo=repo, gname=gname, + rname=rname, bnames=bnames, tnames=tnames, + } + return lud.template.render_file("view/repo.html", env) + end}, + {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/history/([%w_-]+)", + function (req, gname, rname, first) + local user = get_user(req.cookies) + if not allowed(user, gname) then + return "/login", 303 + end + local repo = self.groups[gname][rname] + local commit = repo:commit(first) + local prev = repo:find_prev(commit:id(), conf.limit) + local env = { + title=title, user=user, gname=gname, rname=rname, bname=bname, + commit=commit, limit=conf.limit, prev=prev, first=first, + } + return lud.template.render_file("view/history.html", env) + end}, + {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)", + function (req, gname, rname, cid) + local user = get_user(req.cookies) + if not allowed(user, gname) then + return "/login", 303 + end + local repo = self.groups[gname][rname] + local commit = repo:commit(cid) + local prev = repo:find_prev(commit:id(), 1) + local sig = commit:signature() + local time_str = time_fmt(sig) + local diff = repo:diff(commit, diff_cb) + local env = { + title=title, user=user, gname=gname, rname=rname, bname=bname, + commit=commit, time_str=time_str, sig=sig, cid=cid, prev=prev, diff=diff, + } + return lud.template.render_file("view/commit.html", env) + end}, + {"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)/tree/(.*)", + function (req, gname, rname, cid, path) + local user = get_user(req.cookies) + if not allowed(user, gname) then + return "/login", 303 + end + local repo = self.groups[gname][rname] + local commit = repo:commit(cid) + local node = commit:tree_entry(path) + if node == nil then + return "File not found", 404, "Not found" + end + local parts = {} + for part in path:gmatch("[^/]+") do + table.insert(parts, part) + end + local base = req.path + if base:sub(#base) ~= "/" then + base = base .. "/" + end + local env = { + title=title, user=user, gname=gname, rname=rname, cid=cid, + path=path, base=base, parts=parts, node=node, + } + if node.type_ == "dir" then + return lud.template.render_file("view/dir.html", env) + elseif node.type_ == "file" then + return lud.template.render_file("view/file.html", env) + end + end}, +} end + +local function new_cogit(path) + local self = setmetatable({}, Cogit) + self.path = path + self.sessions = {} + self.initialized = false + self.conf = dofile(path.."/conf.lua") + self:init() + self:scan() + self.app = lud.app.new_app(self:routes()) + return self +end + +return {new_cogit=new_cogit} diff --git a/cogit/git.lua b/cogit/git.lua new file mode 100644 index 0000000..b16ff24 --- /dev/null +++ b/cogit/git.lua @@ -0,0 +1,365 @@ +local ffi = require "ffi" + +ffi.cdef[[ +typedef int64_t git_off_t; +typedef int64_t git_time_t; +typedef uint64_t git_object_size_t; +typedef struct git_repository git_repository; +typedef struct git_object git_object; +typedef struct git_commit git_commit; +typedef struct git_tree git_tree; +typedef struct git_tree_entry git_tree_entry; +typedef struct git_diff git_diff; +typedef struct git_blob git_blob; +typedef struct git_tag git_tag; +typedef struct git_revwalk git_revwalk; +typedef enum { + GIT_OBJECT_ANY = -2, /**< Object can be any of the following */ + GIT_OBJECT_INVALID = -1, /**< Object is invalid. */ + GIT_OBJECT_COMMIT = 1, /**< A commit object. */ + GIT_OBJECT_TREE = 2, /**< A tree (directory listing) object. */ + GIT_OBJECT_BLOB = 3, /**< A file revision object. */ + GIT_OBJECT_TAG = 4, /**< An annotated tag object. */ + GIT_OBJECT_OFS_DELTA = 6, /**< A delta, base is given by an offset. */ + GIT_OBJECT_REF_DELTA = 7 /**< A delta, base is given by object id. */ +} git_object_t; +typedef struct git_oid { + /** raw binary formatted id */ + unsigned char id[20]; +} git_oid; +typedef struct git_strarray { + char **strings; + size_t count; +} git_strarray; +typedef struct git_time { + git_time_t time; /**< time in seconds from epoch */ + int offset; /**< timezone offset, in minutes */ + char sign; /**< indicator for questionable '-0000' offsets in signature */ +} git_time; +typedef struct git_signature { + char *name; /**< full name of the author */ + char *email; /**< email of the author */ + git_time when; /**< time when the action happened */ +} git_signature; +typedef struct git_diff_options git_diff_options; +typedef enum { + GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */ + GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */ + GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */ + GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */ + GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */ + GIT_DIFF_FORMAT_PATCH_ID = 6u /**< git diff as used by git patch-id */ +} git_diff_format_t; +typedef enum { + GIT_DELTA_UNMODIFIED = 0, /**< no changes */ + GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ + GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ + GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ + GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ + GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ + GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ + GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ + GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ + GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ + GIT_DELTA_CONFLICTED = 10 /**< entry in the index is conflicted */ +} git_delta_t; +typedef struct { + git_oid id; + const char *path; + git_object_size_t size; + uint32_t flags; + uint16_t mode; + uint16_t id_abbrev; +} git_diff_file; +typedef struct { + git_delta_t status; + uint32_t flags; /**< git_diff_flag_t values */ + uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */ + uint16_t nfiles; /**< number of files in this delta */ + git_diff_file old_file; + git_diff_file new_file; +} git_diff_delta; +typedef struct { + int old_start; /**< Starting line number in old_file */ + int old_lines; /**< Number of lines in old_file */ + int new_start; /**< Starting line number in new_file */ + int new_lines; /**< Number of lines in new_file */ + size_t header_len; /**< Number of bytes in header text */ + char header[128]; /**< Header text, NUL-byte terminated */ +} git_diff_hunk; +typedef struct { + char origin; /**< A git_diff_line_t value */ + int old_lineno; /**< Line number in old file or -1 for added line */ + int new_lineno; /**< Line number in new file or -1 for deleted line */ + int num_lines; /**< Number of newline characters in content */ + size_t content_len; /**< Number of bytes of data */ + git_off_t content_offset; /**< Offset in the original file to the content */ + const char *content; /**< Pointer to diff text, not NUL-byte terminated */ +} git_diff_line; +typedef int (git_diff_line_cb)(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); + +int git_libgit2_init(); +int git_libgit2_shutdown(); +int git_libgit2_version(int *major, int *minor, int *rev); + +const git_oid * git_object_id(const git_object *obj); +void git_object_free(git_object *object); + +int git_repository_open_ext(git_repository **out, const char *path, unsigned int flags, const char *ceiling_dirs); +int git_repository_open_bare(git_repository **out, const char *bare_path); + +int git_oid_fromstr(git_oid *out, const char *str); +char * git_oid_tostr(char *out, size_t n, const git_oid *id); + +int git_reference_list(git_strarray *array, git_repository *repo); +void git_strarray_free(git_strarray *array); + +int git_tag_list(git_strarray *tag_names, git_repository *repo); + +int git_revparse_single(git_object **out, git_repository *repo, const char *spec); + +int git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id); +int git_commit_parent(git_commit **out, const git_commit *commit, unsigned int n); +const git_oid * git_commit_id(const git_commit *commit); +const char * git_commit_message(const git_commit *commit); +const char * git_commit_summary(git_commit *commit); +const git_signature * git_commit_author(const git_commit *commit); + +int git_revwalk_new(git_revwalk **out, git_repository *repo); +int git_revwalk_push_range(git_revwalk *walk, const char *range); +int git_revwalk_next(git_oid *out, git_revwalk *walk); + +int git_commit_tree(git_tree **tree_out, const git_commit *commit); +size_t git_tree_entrycount(const git_tree *tree); +const git_tree_entry * git_tree_entry_byindex(const git_tree *tree, size_t idx); +git_object_t git_tree_entry_type(const git_tree_entry *entry); +const char * git_tree_entry_name(const git_tree_entry *entry); +int git_tree_entry_bypath(git_tree_entry **out, const git_tree *root, const char *path); +int git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry); +void git_tree_entry_free(git_tree_entry *entry); + +int git_blob_is_binary(const git_blob *blob); +git_object_size_t git_blob_rawsize(const git_blob *blob); +const void * git_blob_rawcontent(const git_blob *blob); + +int git_diff_tree_to_tree(git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts); +int git_diff_print(git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload); +]] +local C = ffi.load("git2") + +local function strarr_to_table(strarr) + local tab = {} + for i = 1, tonumber(strarr[0].count) do + local str = ffi.string(strarr[0].strings[i-1]) + table.insert(tab, str) + end + return tab +end + +local Commit = {} +Commit.__index = Commit + +local function get_commit(commit, repo) + return setmetatable({commit=commit, repo=repo}, Commit) +end + +function Commit:id(len) + len = len or 16 + local oid = ffi.new("char ["..(len+1).."]") + C.git_oid_tostr(oid, len, C.git_commit_id(self.commit)) + return ffi.string(oid) +end + +function Commit:signature() + local sign = C.git_commit_author(self.commit) + return { + time_=tonumber(sign.when.time), + offset=sign.when.offset, + name=ffi.string(sign.name), + email=ffi.string(sign.email), + } +end + +function Commit:message() + return ffi.string(C.git_commit_message(self.commit)) +end + +function Commit:summary() + return ffi.string(C.git_commit_summary(self.commit)) +end + +function Commit:parent(n) + n = n or 1 + local pcommit = ffi.new("git_commit *[1]") + local ret = C.git_commit_parent(pcommit, self.commit, n-1) + if ret == 0 then + return get_commit(pcommit[0], self.repo) + else + return nil + end +end + +function Commit:tree() + local ptree = ffi.new("git_tree *[1]") + local ret = C.git_commit_tree(ptree, self.commit) + if ret == 0 then + return ptree[0] + else + return nil + end +end + +function Commit:children(tree) + local n = tonumber(C.git_tree_entrycount(tree)) + local children = {} + for i = 1, n do + local entry = C.git_tree_entry_byindex(tree, i-1) + local entry_type = C.git_tree_entry_type(entry) + local entry_name = ffi.string(C.git_tree_entry_name(entry)) + local child + if entry_type == 2 then + child = entry_name.."/" + elseif entry_type == 3 then + child = entry_name + end + table.insert(children, child) + end + return children +end + +function Commit:node(entry) + local entry_type = C.git_tree_entry_type(entry) + local entry_name = C.git_tree_entry_name(entry) + local pobj = ffi.new("git_object *[1]") + C.git_tree_entry_to_object(pobj, self.repo, entry) + if entry_type == 2 then + local tree = ffi.cast("git_tree *", pobj[0]) + local children = self:children(tree) + return {type_="dir", name=name, children=children} + elseif entry_type == 3 then + local blob = ffi.cast("git_blob *", pobj[0]) + local data = ffi.string(C.git_blob_rawcontent(blob), C.git_blob_rawsize(blob)) + local bin = C.git_blob_is_binary(blob) ~= 0 + return {type_="file", name=name, data=data, bin=bin} + else + return nil + end +end + +function Commit:tree_entry(path) + local tree = self:tree() + if tree == nil then + return nil + end + if path == "" then + local children = self:children(tree) + return {type_="dir", name="", children=children} + end + local pentry = ffi.new("git_tree_entry *[1]") + local ret = C.git_tree_entry_bypath(pentry, tree, path) + if ret == 0 then + return self:node(pentry[0]) + else + return nil + end +end + +local Repo = {} +Repo.__index = Repo + +function Repo:refs() + local strarr = ffi.new("git_strarray[1]") + C.git_reference_list(strarr, self.repo) + local refs = strarr_to_table(strarr) + C.git_strarray_free(strarr); + return refs +end + +function Repo:branches() + local branches = {} + for _, ref in ipairs(self:refs()) do + local branch_name = ref:match("refs/heads/(.*)") + if branch_name ~= nil then + table.insert(branches, branch_name) + end + end + return branches +end + +function Repo:tags() + local strarr = ffi.new("git_strarray[1]") + C.git_tag_list(strarr, self.repo) + local tags = strarr_to_table(strarr) + C.git_strarray_free(strarr); + return tags +end + +function Repo:commit(rev) + local pobj = ffi.new("git_object *[1]") + C.git_revparse_single(pobj, self.repo, rev) + local obj = pobj[0] + local oid = C.git_object_id(obj) + local pcommit = ffi.new("git_commit *[1]") + C.git_commit_lookup(pcommit, self.repo, oid) + local commit = pcommit[0] + C.git_object_free(obj) + return get_commit(commit, self.repo) +end + +function Repo:find_prev(cid, dist) + for i, bname in ipairs(self:branches()) do + local cids = {} + local commit = self:commit(bname) + while commit do + local cur_cid = commit:id() + table.insert(cids, cur_cid) + if cur_cid == cid then + break + end + commit = commit:parent() + end + if cids[#cids] == cid then + if #cids > 1 then + local index = math.max(1, #cids - dist) + return self:commit(cids[index]) + else + return nil + end + end + end + return nil +end + +function Repo:diff(commit, custom_cb) + local new_tree = commit:tree() + local parent = commit:parent() + local old_tree = parent and parent:tree() + local pdiff = ffi.new("git_diff *[1]") + C.git_diff_tree_to_tree(pdiff, self.repo, old_tree, new_tree, nil) + local diff = pdiff[0] + local s = "" + local function cb(delta, hunk, line, payload) + local lua_line = ffi.string(line.content, line.content_len) + local line_type = string.char(line.origin) + local diff_line + if custom_cb ~= nil then + diff_line = custom_cb(line_type, lua_line) + else + diff_line = line_type .. lua_line + end + s = s .. diff_line + return 0 + end + C.git_diff_print(diff, C.GIT_DIFF_FORMAT_PATCH, cb, nil) + return s +end + +local function open(path) + local self = setmetatable({}, Repo) + local prepo = ffi.new("git_repository *[1]") + assert(C.git_repository_open_bare(prepo, path) == 0) + self.repo = prepo[0] + return self +end + +return {init=C.git_libgit2_init, shutdown=C.git_libgit2_shutdown, open=open} diff --git a/cogit/hash.lua b/cogit/hash.lua new file mode 100644 index 0000000..c396c66 --- /dev/null +++ b/cogit/hash.lua @@ -0,0 +1,38 @@ +local lud = require "ludweb" + +local function get_pass(prompt) + io.write(prompt) + os.execute("stty -echo") + local pass = io.read() + os.execute("stty echo") + io.write("\n") + return pass +end + +local function get_salt() + return lud.crypt.urandom(32) +end + +local function hash_pass(pass, salt) + return lud.crypt.pbkdf2(pass, salt, 10000, 64) +end + +if arg[0] ~= "hash.lua" then + return {get_salt=get_salt, hash_pass=hash_pass} +end + +local pass, pass2 + +repeat + pass = get_pass("password: ") + pass2 = get_pass("repeat : ") +until pass2 == pass + +local salt = get_salt() +local hash = hash_pass(pass, salt) + +local salt_b64 = lud.crypt.b64_enc(salt) +local hash_b64 = lud.crypt.b64_enc(hash) + +print("salt: " .. salt_b64) +print("hash: " .. hash_b64) diff --git a/cogit/scan.lua b/cogit/scan.lua new file mode 100644 index 0000000..7c7d603 --- /dev/null +++ b/cogit/scan.lua @@ -0,0 +1,69 @@ +local ffi = require "ffi" +local git = require "cogit.git" + +ffi.cdef[[ +typedef unsigned long ino_t; +typedef unsigned long off_t; +struct dirent { + ino_t d_ino; /* Inode number */ + off_t d_off; /* Not an offset; see below */ + unsigned short d_reclen; /* Length of this record */ + unsigned char d_type; /* Type of file; not supported + by all filesystem types */ + char d_name[256]; /* Null-terminated filename */ +}; +int scandir(const char *restrict dirp, + struct dirent ***restrict namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, + const struct dirent **)); +int alphasort(const struct dirent **a, const struct dirent **b); +enum { + DT_UNKNOWN = 0, + DT_FIFO = 1, + DT_CHR = 2, + DT_DIR = 4, + DT_BLK = 6, + DT_REG = 8, + DT_LNK = 10, + DT_SOCK = 12, +}; +]] +local C = ffi.C + +local function listdir(path, filter) + local c_filter = filter and function (entry) + return filter(entry) + end + local pentries = ffi.new("struct dirent **[1]") + local n = C.scandir(path, pentries, c_filter, C.alphasort) + local entries = pentries[0] + local list = {} + for i = 1, n do + table.insert(list, ffi.string(entries[i-1].d_name)) + end + return list +end + +local function scanrepos(path) + local folders = listdir(path, function (e) + return e.d_type == C.DT_DIR and ffi.string(e.d_name):sub(1, 1) ~= "." + end) + local groups = {} + for _, folder in ipairs(folders) do + local group_path = path.."/"..folder + local git_folders = listdir(group_path, function (e) + return e.d_type == C.DT_DIR and (ffi.string(e.d_name):match(".*%.git$") ~= nil) + end) + local group = {} + for _, git_folder in ipairs(git_folders) do + local repo_path = group_path.."/"..git_folder + local repo_name = git_folder:match("(.*)%.git$") + group[repo_name] = git.open(repo_path) + end + groups[folder] = group + end + return groups +end + +return {scanrepos=scanrepos} diff --git a/git.lua b/git.lua deleted file mode 100644 index b16ff24..0000000 --- a/git.lua +++ /dev/null @@ -1,365 +0,0 @@ -local ffi = require "ffi" - -ffi.cdef[[ -typedef int64_t git_off_t; -typedef int64_t git_time_t; -typedef uint64_t git_object_size_t; -typedef struct git_repository git_repository; -typedef struct git_object git_object; -typedef struct git_commit git_commit; -typedef struct git_tree git_tree; -typedef struct git_tree_entry git_tree_entry; -typedef struct git_diff git_diff; -typedef struct git_blob git_blob; -typedef struct git_tag git_tag; -typedef struct git_revwalk git_revwalk; -typedef enum { - GIT_OBJECT_ANY = -2, /**< Object can be any of the following */ - GIT_OBJECT_INVALID = -1, /**< Object is invalid. */ - GIT_OBJECT_COMMIT = 1, /**< A commit object. */ - GIT_OBJECT_TREE = 2, /**< A tree (directory listing) object. */ - GIT_OBJECT_BLOB = 3, /**< A file revision object. */ - GIT_OBJECT_TAG = 4, /**< An annotated tag object. */ - GIT_OBJECT_OFS_DELTA = 6, /**< A delta, base is given by an offset. */ - GIT_OBJECT_REF_DELTA = 7 /**< A delta, base is given by object id. */ -} git_object_t; -typedef struct git_oid { - /** raw binary formatted id */ - unsigned char id[20]; -} git_oid; -typedef struct git_strarray { - char **strings; - size_t count; -} git_strarray; -typedef struct git_time { - git_time_t time; /**< time in seconds from epoch */ - int offset; /**< timezone offset, in minutes */ - char sign; /**< indicator for questionable '-0000' offsets in signature */ -} git_time; -typedef struct git_signature { - char *name; /**< full name of the author */ - char *email; /**< email of the author */ - git_time when; /**< time when the action happened */ -} git_signature; -typedef struct git_diff_options git_diff_options; -typedef enum { - GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */ - GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */ - GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */ - GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */ - GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */ - GIT_DIFF_FORMAT_PATCH_ID = 6u /**< git diff as used by git patch-id */ -} git_diff_format_t; -typedef enum { - GIT_DELTA_UNMODIFIED = 0, /**< no changes */ - GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ - GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ - GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ - GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ - GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ - GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ - GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ - GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ - GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ - GIT_DELTA_CONFLICTED = 10 /**< entry in the index is conflicted */ -} git_delta_t; -typedef struct { - git_oid id; - const char *path; - git_object_size_t size; - uint32_t flags; - uint16_t mode; - uint16_t id_abbrev; -} git_diff_file; -typedef struct { - git_delta_t status; - uint32_t flags; /**< git_diff_flag_t values */ - uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */ - uint16_t nfiles; /**< number of files in this delta */ - git_diff_file old_file; - git_diff_file new_file; -} git_diff_delta; -typedef struct { - int old_start; /**< Starting line number in old_file */ - int old_lines; /**< Number of lines in old_file */ - int new_start; /**< Starting line number in new_file */ - int new_lines; /**< Number of lines in new_file */ - size_t header_len; /**< Number of bytes in header text */ - char header[128]; /**< Header text, NUL-byte terminated */ -} git_diff_hunk; -typedef struct { - char origin; /**< A git_diff_line_t value */ - int old_lineno; /**< Line number in old file or -1 for added line */ - int new_lineno; /**< Line number in new file or -1 for deleted line */ - int num_lines; /**< Number of newline characters in content */ - size_t content_len; /**< Number of bytes of data */ - git_off_t content_offset; /**< Offset in the original file to the content */ - const char *content; /**< Pointer to diff text, not NUL-byte terminated */ -} git_diff_line; -typedef int (git_diff_line_cb)(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload); - -int git_libgit2_init(); -int git_libgit2_shutdown(); -int git_libgit2_version(int *major, int *minor, int *rev); - -const git_oid * git_object_id(const git_object *obj); -void git_object_free(git_object *object); - -int git_repository_open_ext(git_repository **out, const char *path, unsigned int flags, const char *ceiling_dirs); -int git_repository_open_bare(git_repository **out, const char *bare_path); - -int git_oid_fromstr(git_oid *out, const char *str); -char * git_oid_tostr(char *out, size_t n, const git_oid *id); - -int git_reference_list(git_strarray *array, git_repository *repo); -void git_strarray_free(git_strarray *array); - -int git_tag_list(git_strarray *tag_names, git_repository *repo); - -int git_revparse_single(git_object **out, git_repository *repo, const char *spec); - -int git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id); -int git_commit_parent(git_commit **out, const git_commit *commit, unsigned int n); -const git_oid * git_commit_id(const git_commit *commit); -const char * git_commit_message(const git_commit *commit); -const char * git_commit_summary(git_commit *commit); -const git_signature * git_commit_author(const git_commit *commit); - -int git_revwalk_new(git_revwalk **out, git_repository *repo); -int git_revwalk_push_range(git_revwalk *walk, const char *range); -int git_revwalk_next(git_oid *out, git_revwalk *walk); - -int git_commit_tree(git_tree **tree_out, const git_commit *commit); -size_t git_tree_entrycount(const git_tree *tree); -const git_tree_entry * git_tree_entry_byindex(const git_tree *tree, size_t idx); -git_object_t git_tree_entry_type(const git_tree_entry *entry); -const char * git_tree_entry_name(const git_tree_entry *entry); -int git_tree_entry_bypath(git_tree_entry **out, const git_tree *root, const char *path); -int git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry); -void git_tree_entry_free(git_tree_entry *entry); - -int git_blob_is_binary(const git_blob *blob); -git_object_size_t git_blob_rawsize(const git_blob *blob); -const void * git_blob_rawcontent(const git_blob *blob); - -int git_diff_tree_to_tree(git_diff **diff, git_repository *repo, git_tree *old_tree, git_tree *new_tree, const git_diff_options *opts); -int git_diff_print(git_diff *diff, git_diff_format_t format, git_diff_line_cb print_cb, void *payload); -]] -local C = ffi.load("git2") - -local function strarr_to_table(strarr) - local tab = {} - for i = 1, tonumber(strarr[0].count) do - local str = ffi.string(strarr[0].strings[i-1]) - table.insert(tab, str) - end - return tab -end - -local Commit = {} -Commit.__index = Commit - -local function get_commit(commit, repo) - return setmetatable({commit=commit, repo=repo}, Commit) -end - -function Commit:id(len) - len = len or 16 - local oid = ffi.new("char ["..(len+1).."]") - C.git_oid_tostr(oid, len, C.git_commit_id(self.commit)) - return ffi.string(oid) -end - -function Commit:signature() - local sign = C.git_commit_author(self.commit) - return { - time_=tonumber(sign.when.time), - offset=sign.when.offset, - name=ffi.string(sign.name), - email=ffi.string(sign.email), - } -end - -function Commit:message() - return ffi.string(C.git_commit_message(self.commit)) -end - -function Commit:summary() - return ffi.string(C.git_commit_summary(self.commit)) -end - -function Commit:parent(n) - n = n or 1 - local pcommit = ffi.new("git_commit *[1]") - local ret = C.git_commit_parent(pcommit, self.commit, n-1) - if ret == 0 then - return get_commit(pcommit[0], self.repo) - else - return nil - end -end - -function Commit:tree() - local ptree = ffi.new("git_tree *[1]") - local ret = C.git_commit_tree(ptree, self.commit) - if ret == 0 then - return ptree[0] - else - return nil - end -end - -function Commit:children(tree) - local n = tonumber(C.git_tree_entrycount(tree)) - local children = {} - for i = 1, n do - local entry = C.git_tree_entry_byindex(tree, i-1) - local entry_type = C.git_tree_entry_type(entry) - local entry_name = ffi.string(C.git_tree_entry_name(entry)) - local child - if entry_type == 2 then - child = entry_name.."/" - elseif entry_type == 3 then - child = entry_name - end - table.insert(children, child) - end - return children -end - -function Commit:node(entry) - local entry_type = C.git_tree_entry_type(entry) - local entry_name = C.git_tree_entry_name(entry) - local pobj = ffi.new("git_object *[1]") - C.git_tree_entry_to_object(pobj, self.repo, entry) - if entry_type == 2 then - local tree = ffi.cast("git_tree *", pobj[0]) - local children = self:children(tree) - return {type_="dir", name=name, children=children} - elseif entry_type == 3 then - local blob = ffi.cast("git_blob *", pobj[0]) - local data = ffi.string(C.git_blob_rawcontent(blob), C.git_blob_rawsize(blob)) - local bin = C.git_blob_is_binary(blob) ~= 0 - return {type_="file", name=name, data=data, bin=bin} - else - return nil - end -end - -function Commit:tree_entry(path) - local tree = self:tree() - if tree == nil then - return nil - end - if path == "" then - local children = self:children(tree) - return {type_="dir", name="", children=children} - end - local pentry = ffi.new("git_tree_entry *[1]") - local ret = C.git_tree_entry_bypath(pentry, tree, path) - if ret == 0 then - return self:node(pentry[0]) - else - return nil - end -end - -local Repo = {} -Repo.__index = Repo - -function Repo:refs() - local strarr = ffi.new("git_strarray[1]") - C.git_reference_list(strarr, self.repo) - local refs = strarr_to_table(strarr) - C.git_strarray_free(strarr); - return refs -end - -function Repo:branches() - local branches = {} - for _, ref in ipairs(self:refs()) do - local branch_name = ref:match("refs/heads/(.*)") - if branch_name ~= nil then - table.insert(branches, branch_name) - end - end - return branches -end - -function Repo:tags() - local strarr = ffi.new("git_strarray[1]") - C.git_tag_list(strarr, self.repo) - local tags = strarr_to_table(strarr) - C.git_strarray_free(strarr); - return tags -end - -function Repo:commit(rev) - local pobj = ffi.new("git_object *[1]") - C.git_revparse_single(pobj, self.repo, rev) - local obj = pobj[0] - local oid = C.git_object_id(obj) - local pcommit = ffi.new("git_commit *[1]") - C.git_commit_lookup(pcommit, self.repo, oid) - local commit = pcommit[0] - C.git_object_free(obj) - return get_commit(commit, self.repo) -end - -function Repo:find_prev(cid, dist) - for i, bname in ipairs(self:branches()) do - local cids = {} - local commit = self:commit(bname) - while commit do - local cur_cid = commit:id() - table.insert(cids, cur_cid) - if cur_cid == cid then - break - end - commit = commit:parent() - end - if cids[#cids] == cid then - if #cids > 1 then - local index = math.max(1, #cids - dist) - return self:commit(cids[index]) - else - return nil - end - end - end - return nil -end - -function Repo:diff(commit, custom_cb) - local new_tree = commit:tree() - local parent = commit:parent() - local old_tree = parent and parent:tree() - local pdiff = ffi.new("git_diff *[1]") - C.git_diff_tree_to_tree(pdiff, self.repo, old_tree, new_tree, nil) - local diff = pdiff[0] - local s = "" - local function cb(delta, hunk, line, payload) - local lua_line = ffi.string(line.content, line.content_len) - local line_type = string.char(line.origin) - local diff_line - if custom_cb ~= nil then - diff_line = custom_cb(line_type, lua_line) - else - diff_line = line_type .. lua_line - end - s = s .. diff_line - return 0 - end - C.git_diff_print(diff, C.GIT_DIFF_FORMAT_PATCH, cb, nil) - return s -end - -local function open(path) - local self = setmetatable({}, Repo) - local prepo = ffi.new("git_repository *[1]") - assert(C.git_repository_open_bare(prepo, path) == 0) - self.repo = prepo[0] - return self -end - -return {init=C.git_libgit2_init, shutdown=C.git_libgit2_shutdown, open=open} diff --git a/hash.lua b/hash.lua deleted file mode 100644 index c396c66..0000000 --- a/hash.lua +++ /dev/null @@ -1,38 +0,0 @@ -local lud = require "ludweb" - -local function get_pass(prompt) - io.write(prompt) - os.execute("stty -echo") - local pass = io.read() - os.execute("stty echo") - io.write("\n") - return pass -end - -local function get_salt() - return lud.crypt.urandom(32) -end - -local function hash_pass(pass, salt) - return lud.crypt.pbkdf2(pass, salt, 10000, 64) -end - -if arg[0] ~= "hash.lua" then - return {get_salt=get_salt, hash_pass=hash_pass} -end - -local pass, pass2 - -repeat - pass = get_pass("password: ") - pass2 = get_pass("repeat : ") -until pass2 == pass - -local salt = get_salt() -local hash = hash_pass(pass, salt) - -local salt_b64 = lud.crypt.b64_enc(salt) -local hash_b64 = lud.crypt.b64_enc(hash) - -print("salt: " .. salt_b64) -print("hash: " .. hash_b64) diff --git a/main.lua b/main.lua index 28f1316..94ad8f6 100644 --- a/main.lua +++ b/main.lua @@ -1,3 +1,3 @@ -local cogit = require "cogit" +local cogit = require "cogit.cogit" cogit.new_cogit(arg[1]):run() diff --git a/scan.lua b/scan.lua deleted file mode 100644 index bd4c5a1..0000000 --- a/scan.lua +++ /dev/null @@ -1,69 +0,0 @@ -local ffi = require "ffi" -local git = require "git" - -ffi.cdef[[ -typedef unsigned long ino_t; -typedef unsigned long off_t; -struct dirent { - ino_t d_ino; /* Inode number */ - off_t d_off; /* Not an offset; see below */ - unsigned short d_reclen; /* Length of this record */ - unsigned char d_type; /* Type of file; not supported - by all filesystem types */ - char d_name[256]; /* Null-terminated filename */ -}; -int scandir(const char *restrict dirp, - struct dirent ***restrict namelist, - int (*filter)(const struct dirent *), - int (*compar)(const struct dirent **, - const struct dirent **)); -int alphasort(const struct dirent **a, const struct dirent **b); -enum { - DT_UNKNOWN = 0, - DT_FIFO = 1, - DT_CHR = 2, - DT_DIR = 4, - DT_BLK = 6, - DT_REG = 8, - DT_LNK = 10, - DT_SOCK = 12, -}; -]] -local C = ffi.C - -local function listdir(path, filter) - local c_filter = filter and function (entry) - return filter(entry) - end - local pentries = ffi.new("struct dirent **[1]") - local n = C.scandir(path, pentries, c_filter, C.alphasort) - local entries = pentries[0] - local list = {} - for i = 1, n do - table.insert(list, ffi.string(entries[i-1].d_name)) - end - return list -end - -local function scanrepos(path) - local folders = listdir(path, function (e) - return e.d_type == C.DT_DIR and ffi.string(e.d_name):sub(1, 1) ~= "." - end) - local groups = {} - for _, folder in ipairs(folders) do - local group_path = path.."/"..folder - local git_folders = listdir(group_path, function (e) - return e.d_type == C.DT_DIR and (ffi.string(e.d_name):match(".*%.git$") ~= nil) - end) - local group = {} - for _, git_folder in ipairs(git_folders) do - local repo_path = group_path.."/"..git_folder - local repo_name = git_folder:match("(.*)%.git$") - group[repo_name] = git.open(repo_path) - end - groups[folder] = group - end - return groups -end - -return {scanrepos=scanrepos}