From 4fcff492c978e1d355373978170dac5ebdef26cd Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Tue, 9 Apr 2024 22:24:15 +0530 Subject: pass/ls: init --- README.md | 4 +- include/libnpass/libnpass.h | 20 ++++++ meson.build | 1 + src/libnpass/libnpass.c | 170 +++++++++++++++++++++++++++++++++++++++++++- src/npass/npass.c | 71 +++++++++++++++++- 5 files changed, 261 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 14b85f6..410b608 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ section. Todo ---- -- [ ] cmdline +- [x] cmdline - [x] pass init - - [ ] pass ls + - [x] pass ls - [x] pass rm - [x] pass add - [x] pass gen diff --git a/include/libnpass/libnpass.h b/include/libnpass/libnpass.h index ac7423a..130954d 100644 --- a/include/libnpass/libnpass.h +++ b/include/libnpass/libnpass.h @@ -1,4 +1,6 @@ #include +#include +#include typedef enum { PASS_GEN_DIGIT = 0, @@ -6,6 +8,24 @@ typedef enum { PASS_GEN_GRAPH = 2, } pass_gen_t; +typedef enum { + PASS_STORE_DIR = 0, + PASS_STORE_ENC = 1, + PASS_STORE_INV = 2, +} pass_store_t; + +struct store { + char name[NAME_MAX]; + pass_store_t type; +}; + +DIR *openstore(const char *spath); +int readstore(DIR *dirp, struct store *s); +int readstore_all(const char *path, struct store **stor); + +pass_store_t pass_store_type(const char *spath); +int pass_store_cmp(const void *vp1, const void *vp2); + int pass_init(const char *fpr); int pass_cat(FILE *out, const char *path); int pass_add(const char *path, const char *pass, size_t n); diff --git a/meson.build b/meson.build index ff77d19..0384579 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,7 @@ project( add_project_arguments( [ '-D_POSIX_C_SOURCE=200809L', + '-D_DEFAULT_SOURCE', '-Wvla', ], language: 'c', diff --git a/src/libnpass/libnpass.c b/src/libnpass/libnpass.c index aa1a285..37fc40c 100644 --- a/src/libnpass/libnpass.c +++ b/src/libnpass/libnpass.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "libnpass/libnpass.h" #include "libnpass/gpg.h" @@ -14,8 +15,8 @@ #include "util.h" -#define DEF_PASS_DIR "pass" -#define FPR_MAX 256 +#define DEF_PASS_DIR "pass" +#define FPR_MAX NAME_MAX static char pass_dir[PATH_MAX] = {0}; const char *pass_gen_set[] = { @@ -32,6 +33,7 @@ const char *pass_gen_set[] = { }; int set_pass_dir(void); +int is_storeobj(struct dirent *dir); int set_pass_dir(void) { const char *env; @@ -59,6 +61,170 @@ int set_pass_dir(void) { return 1; } +int pass_store_cmp(const void *vp1, const void *vp2) { + struct store *sp1, *sp2; + + sp1 = (struct store *) vp1; + sp2 = (struct store *) vp2; + return strcmp(sp1->name, sp2->name); +} + +int is_storeobj(struct dirent *dir) { + int r; + char *s; + + switch (dir->d_type) { + case DT_DIR: + r = strcmp(dir->d_name, "."); + if (r) + r = strcmp(dir->d_name, ".."); + + return r; + break; + case DT_REG: + s = strrchr(dir->d_name, '.'); + if (!s) + return 0; + + r = strcmp(s, ".gpg"); + return (r == 0); + break; + default: + return 0; + break; + } +} + +pass_store_t pass_store_type(const char *spath) { + int r; + struct stat sbuf; + char abs_path[PATH_MAX]; + + r = set_pass_dir(); + if (r) + err_ret(PASS_STORE_INV, "PASSWORD_STORE_DIR not set"); + + r = snprintf(abs_path, sizeof(abs_path) - 1, "%s/%s", + pass_dir, spath); + if (r >= (int) sizeof(abs_path)) + err_ret(PASS_STORE_INV, "path exceeded PATH_MAX"); + r = stat(abs_path, &sbuf); + if (!r && (sbuf.st_mode & S_IFMT) == S_IFDIR) + return PASS_STORE_DIR; + + r = snprintf(abs_path, sizeof(abs_path) - 1, "%s/%s.gpg", + pass_dir, spath); + if (r >= (int) sizeof(abs_path)) + err_ret(PASS_STORE_INV, "path exceeded PATH_MAX"); + r = stat(abs_path, &sbuf); + if (r) + err_ret(PASS_STORE_INV, "%s", strerror(errno)); + + if ((sbuf.st_mode & S_IFMT) == S_IFREG) { + return PASS_STORE_ENC; + } else { + err_ret(PASS_STORE_INV, "%s is not a regular file", abs_path); + } +} + +DIR *openstore(const char *spath) { + int r; + DIR *d; + const char *path; + char abs_path[PATH_MAX]; + + r = set_pass_dir(); + if (r) + err_ret(NULL, "PASSWORD_STORE_DIR not set"); + + if (spath) { + r = snprintf(abs_path, sizeof(abs_path) - 1, "%s/%s", + pass_dir, spath); + if (r >= (int) sizeof(abs_path)) + err_ret(NULL, "path exceeded PATH_MAX"); + + path = abs_path; + } else { + path = pass_dir; + } + + d = opendir(path); + if (!d) + err_ret(NULL, "%s", strerror(errno)); + + return d; +} + +int readstore(DIR *dirp, struct store *s) { + struct dirent *dir; + + errno = 0; + + while ((dir = readdir(dirp))) { + if (is_storeobj(dir)) + break; + } + + if (!dir) { + if (errno != 0) + err_ret(1, "%s", strerror(errno)); + + return EOF; + } + + strncpy(s->name , dir->d_name, sizeof(s->name)); + switch (dir->d_type) { + case DT_DIR: + s->type = PASS_STORE_DIR; + break; + case DT_REG: + /* this is safe since is_storeobj() + * rejects files without .gpg suffix */ + *(strrchr(s->name, '.')) = '\0'; + s->type = PASS_STORE_ENC; + break; + default: + s->type = PASS_STORE_INV; + break; + } + + return 0; +} + +int readstore_all(const char *path, struct store **stor) { + int r; + void *p; + size_t i; + DIR *dirp; + size_t len = PATH_MAX; + + *stor = malloc(sizeof(struct store) * len); + if (!*stor) + err_ret(-1, "%s", strerror(errno)); + + dirp = openstore(path); + if (!dirp) { + free(*stor); + return -1; + }; + + for (i = 0; !(r = readstore(dirp, *stor + i)); i++) { + if (i < len - 1) + continue; + + len *= 2; + p = realloc(*stor, sizeof(**stor) * len); + if (!p) { + free(*stor); + err_ret(-1, "%s", strerror(errno)); + } else { + *stor = p; + } + } + + return i; +} + int pass_init(const char *fpr) { int r; diff --git a/src/npass/npass.c b/src/npass/npass.c index a055317..0b5f792 100644 --- a/src/npass/npass.c +++ b/src/npass/npass.c @@ -7,12 +7,22 @@ #include "libnpass/libnpass.h" #include "util.h" -#define invalid_usage_err_ret() err_ret(1, "invalid usage, try pass help") +#define invalid_usage_err_ret() \ + err_ret(1, "invalid usage, try pass help") + +#define BLUE "\e[0;34m" +#define NCOL "\e[0m" + +typedef enum { + DEPTH_ITS_OVER = 0, + DEPTH_NOT_OVER = 1, +} depth_state_t; void print_usage(void); int cat(const char *path); int add(const char *path); int gen(int argc, char *argv[]); +int ls(const char *path, size_t depth, depth_state_t *depth_state); void print_usage(void) { @@ -46,6 +56,60 @@ int cat(const char *path) return r; } +int ls(const char *path, size_t depth, depth_state_t *depth_state) { + void *p; + char *prefix; + int i, j, len; + struct store *stor; + char new_path[PATH_MAX]; + static size_t depth_state_len = NAME_MAX; + + len = readstore_all(path, &stor); + if (len < 0) + return 1; + + if (!depth) { + depth_state = malloc(sizeof(depth_state_t) * depth_state_len); + puts((path) ? path : "Password Store"); + } else if (depth == depth_state_len - 1) { + depth_state_len *= 2; + p = realloc(stor, sizeof(depth_state[0]) * len); + if (!p) + err_ret(1, "%s", strerror(errno)); + depth_state = p; + } + + qsort(stor, len, sizeof(stor[0]), pass_store_cmp); + for (i = 0; i < len; i++) { + if (i == len - 1) { + prefix = "└──"; + depth_state[depth] = DEPTH_ITS_OVER; + } else { + prefix = "├──"; + depth_state[depth] = DEPTH_NOT_OVER; + } + + for (j = 0; (size_t) j < depth; j++) + printf("%s ", (depth_state[j]) == DEPTH_ITS_OVER ? " " : "│"); + + if (stor[i].type == PASS_STORE_DIR) { + printf("%s %s%s%s\n", prefix, BLUE, stor[i].name, NCOL); + + snprintf(new_path, sizeof(new_path) - 1, "%s/%s", + (path) ? path : "", stor[i].name); + ls(new_path, depth + 1, depth_state); + } else { + printf("%s %s\n", prefix, stor[i].name); + } + } + + if (!depth) + free(depth_state); + free(stor); + + return 0; +} + int add(const char *path) { char *p1 = NULL, *p2 = NULL; @@ -153,6 +217,11 @@ int main(int argc, char *argv[]) invalid_usage_err_ret(); r = pass_init(argv[1]); + } else if (!strcmp("ls", *argv)) { + if (argc > 2) + invalid_usage_err_ret(); + + r = ls(argv[1], 0, NULL); } else if (!strcmp("cat", *argv)) { if (argc != 2) invalid_usage_err_ret(); -- cgit v1.2.3