aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoursoir <chat@joursoir.net>2025-02-10 22:17:48 +0300
committerJoursoir <chat@joursoir.net>2025-02-10 22:19:36 +0300
commit3e937588c6fe36f5776dd1029a34132e0ec76db5 (patch)
treefa8a55bdf9b9286982ca0025dd17352610f4be5c
parente9a715195fbf82c9ea7d4b2d6699351468fab222 (diff)
downloadlock-password-3e937588c6fe36f5776dd1029a34132e0ec76db5.tar.gz
lock-password-3e937588c6fe36f5776dd1029a34132e0ec76db5.tar.bz2
lock-password-3e937588c6fe36f5776dd1029a34132e0ec76db5.zip
add git feature
It's available at compile-time
-rwxr-xr-xMakefile6
-rw-r--r--README3
-rw-r--r--src/exec-cmd.c50
-rw-r--r--src/lpass.c13
-rw-r--r--src/r-lg2.c207
-rw-r--r--src/r-lg2.h22
6 files changed, 300 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index a990948..9c77ea5 100755
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@ PREFIX = /usr/local/bin
CC = gcc
CFLAGS = -Wall -g #-DDEBUG
LIBS = $(shell pkg-config --cflags --libs gpgme)
+HAVE_LIBGIT2 = $(shell pkg-config --exists libgit2 && echo yes)
MAN_PATH = /usr/share/man/man1
COMPLETION_PATH = /usr/share/bash-completion/completions/lpass
SOURCES = \
@@ -12,6 +13,11 @@ SOURCES = \
src/r-gpgme.c \
src/tree.c \
src/xstd.c
+ifdef HAVE_LIBGIT2
+ CFLAGS += $(shell pkg-config --cflags libgit2) -DLIGBIT
+ LIBS += $(shell pkg-config --libs libgit2)
+ SOURCES += src/r-lg2.c
+endif
ifdef DISPLAY
LIBS += -lX11
CFLAGS += -DDISPLAY
diff --git a/README b/README
index 7910df0..1e524d8 100644
--- a/README
+++ b/README
@@ -9,12 +9,13 @@ Dependencies:
https://www.gnupg.org/
* GPGME
https://www.gnupg.org/software/gpgme/
+* libgit2 (only if you want use git to track your data)
+ https://libgit2.org/
=== TO-DO LIST ===
Feel free to sumbit patches.
-* Track password changes using git: https://libgit2.org/
* Re-encrypt passwords when GPG key is changed.
* Refactor tree-printing routine.
* Use Wayland library to copy text to the clipboard (currently `wl-copy` application is used).
diff --git a/src/exec-cmd.c b/src/exec-cmd.c
index 8d5aa3d..bfba424 100644
--- a/src/exec-cmd.c
+++ b/src/exec-cmd.c
@@ -33,6 +33,7 @@
#include "exec-cmd.h"
#include "tree.h"
#include "output.h"
+#include "r-lg2.h"
#define print_options(msg) print_usage("%s", msg)
@@ -70,6 +71,7 @@ int cmd_insert(int argc, char *argv[])
const char description[] = "insert [-ecf] <passname>\n";
int retval = 0, result;
int flag_echo = 0, flag_force = 0, flag_copy = 0;
+ int overwrite = 0;
const struct option long_options[] = {
{"echo", no_argument, NULL, 'e'},
{"force", no_argument, NULL, 'f'},
@@ -103,6 +105,7 @@ int cmd_insert(int argc, char *argv[])
if(overwrite_answer(path) != 'y')
return 1;
}
+ overwrite = 1;
}
char *f_pass = NULL, *s_pass = NULL;
@@ -148,6 +151,15 @@ int cmd_insert(int argc, char *argv[])
retval = 1;
goto out;
}
+
+#ifdef LIGBIT
+ result = lg2_simple_action(GIT_ACTION_INSERT, overwrite, path, NULL);
+ if (result) {
+ print_error("Error: Can't commit the changes\n");
+ // TODO: restore password? Just notify for now
+ }
+#endif
+
if(flag_copy)
copy_outside(f_pass);
@@ -267,6 +279,14 @@ int cmd_edit(int argc, char *argv[])
return 1;
}
+#ifdef LIGBIT
+ result = lg2_simple_action(GIT_ACTION_EDIT, 0, path, NULL);
+ if (result) {
+ print_error("Error: Can't commit the changes\n");
+ // TODO: restore password? Just notify for now
+ }
+#endif
+
return 0;
}
@@ -275,6 +295,7 @@ int cmd_generate(int argc, char *argv[])
const char description[] = "generate [-l=pass-length] [-f] <passname>\n";
int pass_length = stdlen_pass;
int flag_force = 0, flag_copy = 0, result;
+ int overwrite = 0;
const struct option long_options[] = {
{"length", required_argument, NULL, 'l'},
{"force", no_argument, NULL, 'f'},
@@ -315,6 +336,7 @@ int cmd_generate(int argc, char *argv[])
if(overwrite_answer(path) != 'y')
return 1;
}
+ overwrite = 1;
}
else if(result == F_ISDIR) {
print_error("Error: You can't generate password for directory\n");
@@ -333,6 +355,14 @@ int cmd_generate(int argc, char *argv[])
return 1;
}
+#ifdef LIGBIT
+ result = lg2_simple_action(GIT_ACTION_GENERATE, overwrite, path, NULL);
+ if (result) {
+ print_error("Error: Can't commit the changes\n");
+ // TODO: restore password? Just notify for now
+ }
+#endif
+
if(flag_copy)
copy_outside(g_pass);
else
@@ -363,6 +393,15 @@ int cmd_remove(int argc, char *argv[])
print_error("Error: %s\n", strerror(errno));
return 1;
}
+
+#ifdef LIGBIT
+ result = lg2_simple_action(GIT_ACTION_DELETE, 0, path, NULL);
+ if (result) {
+ print_error("Error: Can't commit the changes\n");
+ // TODO: restore password? Just notify for now
+ }
+#endif
+
return 0;
}
@@ -379,6 +418,7 @@ int cmd_move(int argc, char *argv[])
};
int result, flag_force = 0;
+ int overwrite = 0;
while((result = getopt_long(argc, argv, "f", long_options, NULL)) != -1) {
switch(result) {
case 'f': { flag_force = 1; break; }
@@ -418,6 +458,7 @@ int cmd_move(int argc, char *argv[])
if(overwrite_answer(new_path) != 'y')
return 1;
}
+ overwrite = 1;
}
result = rename(old_path, new_path);
@@ -425,6 +466,15 @@ int cmd_move(int argc, char *argv[])
print_error("Error: %s\n", strerror(errno));
return 1;
}
+
+#ifdef LIGBIT
+ result = lg2_simple_action(GIT_ACTION_MOVE, overwrite, old_path, new_path);
+ if (result) {
+ print_error("Error: Can't commit the changes\n");
+ // TODO: restore password? Just notify for now
+ }
+#endif
+
return 0;
}
diff --git a/src/lpass.c b/src/lpass.c
index 229f3b6..f79c944 100644
--- a/src/lpass.c
+++ b/src/lpass.c
@@ -27,6 +27,7 @@
#include "exec-cmd.h"
#include "xstd.h"
#include "output.h"
+#include "r-lg2.h"
struct cmd_struct {
const char *cmd;
@@ -93,8 +94,20 @@ int main(int argc, char *argv[])
if(goto_maindir())
return 1;
+#ifdef LIGBIT
+ result = lg2_open_repo(".");
+ if (result) {
+ print_error("Something went wrong with git\n");
+ return 1;
+ }
+#endif
+
struct cmd_struct *ptr = get_cmd(argv[1]);
result = ptr ? ptr->func(--argc, ++argv) : cmd_help(argc, argv);
+#ifdef LIGBIT
+ lg2_close_repo();
+#endif
+
return result;
} \ No newline at end of file
diff --git a/src/r-lg2.c b/src/r-lg2.c
new file mode 100644
index 0000000..915b210
--- /dev/null
+++ b/src/r-lg2.c
@@ -0,0 +1,207 @@
+#if defined(LIGBIT)
+
+#include <stdio.h>
+#include "r-lg2.h"
+#include "xstd.h"
+#include "output.h"
+
+git_repository *g_repo = NULL;
+
+static int lg2_commit(git_index *index, char *comment)
+{
+ int error;
+ git_oid commit_oid, tree_oid;
+ git_tree *tree = NULL;
+ git_object *parent = NULL;
+ git_reference *ref = NULL;
+ git_signature *author_signature = NULL, *committer_signature = NULL;
+
+ error = git_revparse_ext(&parent, &ref, g_repo, "HEAD");
+ if (error == GIT_ENOTFOUND) {
+ printf("HEAD not found. Creating first commit\n");
+ error = 0;
+ } else if (error != 0) {
+ const git_error *err = git_error_last();
+ if (err) printf("ERROR %d: %s\n", err->klass, err->message);
+ else printf("ERROR %d: no detailed info\n", error);
+ return error;
+ }
+
+ error = git_index_write_tree(&tree_oid, index);
+ if (error) {
+ print_error("git: Could not write tree\n");
+ return error;
+ }
+ error = git_index_write(index);
+ if (error) {
+ print_error("git: Could not write index\n");
+ return error;
+ }
+
+ error = git_tree_lookup(&tree, g_repo, &tree_oid);
+ if (error) {
+ print_error("git: Error looking up tree\n");
+ return error;
+ }
+
+ error = git_signature_default_from_env(&author_signature, &committer_signature, g_repo);
+ if (error) {
+ print_error("git: Error creating signatures\n");
+ return error;
+ }
+
+ error = git_commit_create_v(
+ &commit_oid,
+ g_repo,
+ "HEAD",
+ author_signature,
+ committer_signature,
+ NULL,
+ comment,
+ tree,
+ parent ? 1 : 0, parent);
+ if (error) {
+ print_error("git: Error creating commit\n");
+ } else {
+ char *commit_hash = git_oid_tostr_s(&commit_oid);
+ printf("git: [HEAD %s] %s\n", commit_hash, comment);
+ error = 0;
+ }
+
+ git_signature_free(author_signature);
+ git_signature_free(committer_signature);
+ git_tree_free(tree);
+ git_object_free(parent);
+ git_reference_free(ref);
+ return error;
+}
+
+int lg2_open_repo(const char *path)
+{
+ int error = 0;
+ error = git_libgit2_init();
+ if (error < 0) {
+ const git_error *e = git_error_last();
+ print_error("git_libgit2_init(): %s\n", e->message);
+ return error;
+ }
+
+ error = git_repository_open(&g_repo, path);
+ if (error < 0) {
+ print_error("Warning: could not open repository, trying git init\n");
+
+ error = git_repository_init(&g_repo, path, 0);
+ if (error < 0) {
+ const git_error *e = git_error_last();
+ print_error("git_repository_init(): %s\n", e->message);
+ return error;
+ }
+
+ git_index *idx = NULL;
+ error = git_repository_index(&idx, g_repo);
+ if (error < 0) {
+ const git_error *e = git_error_last();
+ print_error("git_repository_index(): %s\n", e->message);
+ git_repository_free(g_repo);
+ return error;
+ }
+
+ // Add all files in the repository
+ char *paths[] = {"*", ".*"};
+ git_strarray arr = {paths, 2};
+
+ error = git_index_add_all(idx, &arr, GIT_INDEX_ADD_DEFAULT, NULL, NULL);
+ if (error < 0) {
+ const git_error *e = git_error_last();
+ print_error("git_index_add_all(): %s\n", e->message);
+ git_index_free(idx);
+ git_repository_free(g_repo);
+ return error;
+ }
+
+ error = lg2_commit(idx, "init commit");
+ git_index_free(idx);
+ }
+ return error;
+}
+
+int lg2_close_repo()
+{
+ if (g_repo != NULL)
+ git_repository_free(g_repo);
+ git_libgit2_shutdown();
+ return 0;
+}
+
+int lg2_simple_action(git_action_t action, int is_overwrite, const char *path, const char *new_path)
+{
+ int error = 0;
+ git_index *idx = NULL;
+ char *message = NULL;
+ const char *action_str = "";
+ const char *overwrite_str = NULL;
+
+ error = git_repository_index(&idx, g_repo);
+ if (error < 0) {
+ const git_error *e = git_error_last();
+ print_error("git_repository_index(): %s\n", e->message);
+ return error;
+ }
+
+ switch (action) {
+ case GIT_ACTION_INSERT:
+ action_str = "insert";
+ error = git_index_add_bypath(idx, path);
+ break;
+ case GIT_ACTION_GENERATE:
+ action_str = "generate";
+ error = git_index_add_bypath(idx, path);
+ break;
+ case GIT_ACTION_EDIT:
+ action_str = "edit";
+ error = git_index_add_bypath(idx, path);
+ break;
+
+ case GIT_ACTION_DELETE:
+ action_str = "delete";
+ error = git_index_remove_bypath(idx, path);
+ break;
+
+ case GIT_ACTION_MOVE:
+ error = git_index_remove_bypath(idx, path);
+ if (!error)
+ error = git_index_add_bypath(idx, new_path);
+
+ if (is_overwrite)
+ overwrite_str = " (overwrite)";
+ message = xstrcat(path, " -> ", new_path, overwrite_str, NULL);
+ break;
+ }
+
+ if (error) {
+ const git_error *e = git_error_last();
+ print_error("Index operation failed: %s\n", e->message);
+ git_index_free(idx);
+ return error;
+ }
+
+ // Generate commit message if not already set (for move)
+ if (!message) {
+ if (is_overwrite)
+ overwrite_str = " (overwrite)";
+ message = xstrcat(path, ": ", action_str, overwrite_str, NULL);
+ }
+
+ if (!message) {
+ git_index_free(idx);
+ return 1;
+ }
+
+ error = lg2_commit(idx, message);
+
+ free(message);
+ git_index_free(idx);
+ return error;
+}
+
+#endif /* LIGBIT */
diff --git a/src/r-lg2.h b/src/r-lg2.h
new file mode 100644
index 0000000..640a2c6
--- /dev/null
+++ b/src/r-lg2.h
@@ -0,0 +1,22 @@
+#ifndef LPASS_RLG2_H
+#define LPASS_RLG2_H
+
+#ifdef LIGBIT
+
+#include <git2.h>
+
+typedef enum {
+ GIT_ACTION_INSERT,
+ GIT_ACTION_GENERATE,
+ GIT_ACTION_EDIT,
+ GIT_ACTION_DELETE,
+ GIT_ACTION_MOVE
+} git_action_t;
+
+int lg2_open_repo(const char *path);
+int lg2_close_repo();
+int lg2_simple_action(git_action_t action, int is_overwrite, const char *path, const char *new_path);
+
+#endif /* LIGBIT */
+
+#endif /* LPASS_RLG2_H */