Hugo Osvaldo Barrera: 1 Implement git-credential-himitsu 3 files changed, 116 insertions(+), 0 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/himitsu-devel/patches/34025/mbox | git am -3Learn more about email & git
Hi! This patch is not in a "good to merge" state. I'm mostly looking for feedback at this stage. I'm not sure if there's a well-defined method to import modules from another package yet, so I just hacked this in-repo for now. The naming of the helper is so that git will pick it up by simply using: [credential] helper = himitsu I haven't implemented `set` and `erase` yet. These are for saving password into himitsu when they are provided to git manually or via some other helper. I haven't found that workflow very intuitive, but will likely implement those commands just for completeness's sake. Something annoying is that git uses proto=smtp for smtp+tls. Other tools use proto=smtps. I worry that other tools might try to use the password save as proto=smtp for plain-text-smtp. But this can only be fixed by git itself, and would likely be an unpopular change. git SHOULD pass the port to make the protocol more unambiguous. Looking forward to hearing some feedback. --- This helper can be used by git to retrieve the smtps password. Example usage is: [credential] helper = himitsu [sendemail] annotate = yes confirm = always smtpserver = smtp.fastmail.com smtpuser = whynothugo@fastmail.com smtpencryption = tls smtpserverport = 587 The username MUST be specified in the git configuration, otherwise git will not use the credentials helper. This can be tested with: hare build -o git-credential-himitsu cmd/git-credential-himitsu/ && \ cat test-input | ./git-credential-himitsu get Where test-input should contain: protocol=smtp host=smtp.example.com:587 username=whynothugo@example.com --- .gitignore | 1 + Makefile | 4 ++ cmd/git-credential-himitsu/main.ha | 111 +++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 cmd/git-credential-himitsu/main.ha diff --git a/.gitignore b/.gitignore index ed61257..5cc6416 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /himitsud /himitsu-init /hiq +/git-credential-himitsu .teststore/ *.1 *.5 diff --git a/Makefile b/Makefile index a61630f..86b2d03 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,10 @@ himitsu-init: hiq: hare build -o $@ cmd/$@/ +git-credential-himitsu: + hare build -o $@ cmd/$@/ + + check: hare test diff --git a/cmd/git-credential-himitsu/main.ha b/cmd/git-credential-himitsu/main.ha new file mode 100644 index 0000000..fa4ba1c --- /dev/null +++ b/cmd/git-credential-himitsu/main.ha @@ -0,0 +1,111 @@ +use bufio; +use dirs; +use errors; +use getopt; +use fmt; +use himitsu::client; +use himitsu::query; +use io; +use net::unix; +use net; +use os; +use path; +use strings; + +export fn main() void = { + // Verbs are `get`, `store` and `erase`. + // See: https://git-scm.com/docs/gitcredentials#_custom_helpers + if (len(os::args) != 2) { + fmt::fatal("No command specified"); + }; + switch (os::args[1]) { + case "get" => + get(); + case "set" => + fmt::fatalf("Only `get` is implemented"); + case "erase" => + fmt::fatalf("Only `get` is implemented"); + case => + fmt::fatalf("Usage: {} get", os::args[0]); + }; +}; + +fn get() void = { + let query: []str = []; + + for (true) { + match (bufio::scanline(os::stdin)!) { + case io::EOF => + break; + case let line: []u8 => + const line = strings::fromutf8(line); + defer free(line); + const split = strings::split(line, "="); + defer free(split); + + const param = line_to_query(split[0], split[1], query); + append(query, param); + }; + }; + + // TODO: handle error: + let himitsu = client::connect()!; + defer io::close(himitsu)!; + + match (get_password(himitsu, query)) { + case let password: str => + fmt::printfln("password={}", password)!; + case void => + fmt::fatal("No matching credentials found."); + }; +}; + +// For a given input line, return a corresponding portion of a query. +fn line_to_query(key: str, value: str, query: []str) str = { + switch (key) { + case "protocol" => + // FIXME: git sends "smtp" for proto "smtps". + // Other tools use `smtps` for `smtps`, so entries need to be duplicated? + return fmt::asprintf("proto={}", value); + case "username" => + return fmt::asprintf("username={}", value); + case "host" => + const (hostname, port) = strings::cut(value, ":"); + return fmt::asprintf("hostname={}", hostname); + case => + // XXX: Should warn? + return ""; + }; +}; + +// Get the password from a store. +fn get_password(conn: net::socket, query: []str) (str | void) = { + fmt::fprintf(conn, "query -d ")!; + for (let i = 0z; i < len(query); i += 1) { + fmt::fprintf(conn, "{} ", query[i])!; + }; + fmt::fprintf(conn, "\n")!; + + match (bufio::scanline(conn)!) { + case io::EOF => + return; // How do I no-op here? + case let line: []u8 => + const line = strings::fromutf8(line); + defer free(line); + + if (line == "end") { + return; + }; + + const components = strings::split(line, " "); + defer free(components); + + for (let i = 0z; i < len(components); i += 1) { + const (key, value) = strings::cut(components[i], "="); + + if (key == "password!") { + return value; + }; + }; + }; +}; -- 2.37.1
Only comment is that this should not live in the himitsu repository but in a separate himitsu-git repo.