From 64ef51a8ce6bc148ba493e350e2cac4cba470201 Mon Sep 17 00:00:00 2001 From: Joursoir Date: Sat, 31 Oct 2020 17:45:29 +0000 Subject: some features: remake README.md for new code architecture; add help command; add man page; --- Makefile | 24 ++- README.md | 232 ++++++++++++++++++++--- easydir.c | 79 -------- easydir.h | 9 - handerror.c | 30 --- handerror.h | 8 - implementation.c | 256 ------------------------- implementation.h | 17 -- main.c | 491 ------------------------------------------------ man/lpass.1 | 162 ++++++++++++++++ src/easydir.c | 79 ++++++++ src/easydir.h | 9 + src/handerror.c | 30 +++ src/handerror.h | 8 + src/implementation.c | 256 +++++++++++++++++++++++++ src/implementation.h | 17 ++ src/main.c | 517 +++++++++++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 1303 insertions(+), 921 deletions(-) delete mode 100644 easydir.c delete mode 100644 easydir.h delete mode 100644 handerror.c delete mode 100644 handerror.h delete mode 100644 implementation.c delete mode 100644 implementation.h delete mode 100644 main.c create mode 100644 man/lpass.1 create mode 100644 src/easydir.c create mode 100644 src/easydir.h create mode 100644 src/handerror.c create mode 100644 src/handerror.h create mode 100644 src/implementation.c create mode 100644 src/implementation.h create mode 100644 src/main.c diff --git a/Makefile b/Makefile index 5122e69..c0e734f 100755 --- a/Makefile +++ b/Makefile @@ -1,8 +1,11 @@ PREFIX = /usr/local/bin CC = gcc CFLAGS = -Wall -g -SOURCES = easydir.c handerror.c implementation.c main.c -OBJECTS = $(SOURCES:.c=.o) +MAN_PATH = /usr/share/man/man1 +SOURCES = src/easydir.c src/handerror.c src/implementation.c src/main.c +OBJECTS = easydir.o handerror.o implementation.o main.o +MAN_SOURCES = man/lpass.1 +MAN_OBJECTS = lpass.1.gz BASH = lpass_copy.sh EXECUTABLE = lpass @@ -12,6 +15,7 @@ all: $(EXECUTABLE) clean: @rm -rf $(EXECUTABLE) $(OBJECTS) + @rm -rf $(MAN_OBJECTS) $(OBJECTS): @$(CC) -c $(CFLAGS) $(SOURCES) @@ -21,12 +25,16 @@ $(EXECUTABLE): $(OBJECTS) install: all @echo installing files to $(PREFIX) - @install $(EXECUTABLE) $(PREFIX) - @chmod 755 $(PREFIX)/$(EXECUTABLE) - @install $(BASH) $(PREFIX) - @chmod 755 $(PREFIX)/$(BASH) + @install $(EXECUTABLE) $(PREFIX) && chmod 755 $(PREFIX)/$(EXECUTABLE) + @install $(BASH) $(PREFIX) && chmod 755 $(PREFIX)/$(BASH) + @echo installing man page + @cat $(MAN_SOURCES) | gzip > $(MAN_OBJECTS) + @install $(MAN_OBJECTS) $(MAN_PATH) uninstall: @echo removing files from $(PREFIX) - @rm -rf $(PREFIX)/$(EXECUTABLE) - @rm -rf $(PREFIX)/$(BASH) \ No newline at end of file + @echo deleting man page + @rm -rf \ + $(PREFIX)/$(EXECUTABLE) \ + $(PREFIX)/$(BASH) \ + $(MAN_PATH)/$(MAN_OBJECTS) \ No newline at end of file diff --git a/README.md b/README.md index ac13ed6..95a2209 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,209 @@ -# LockPassword - a simple terminal password manager -## Synopsis -### lpass [command] - -## Commands -### --init -Initialize password bank. Before use a LockPassword you must be run this command. -### -c -In developing... -### -e name -Edit a existing password in 'name'. -### -g [number-of-symbols] name -Generate a new password using length [number-of-symbols] and insert in name. Without [number-of-symbols] default generate 10 chars. -### -i name -Create 'name' store and insert your password there. -### -R name -Remove 'name' store from the password bank. - -## How are passwords stored? -For now, passwords are kept in clear еtext. We will add password encryption in the next update - -## Guide -Coming soon... +# LockPassword +a simple terminal password manager, using GnuPG to encrypt passwords. The application positions itself as open-source software. Distributed under the Unlicense license. + +## Dependencies: +The following dependencies must be installed for work: +* tree +* gpg +* gcc + +P.S: these can be installed using the package manager such as `pacman`, `apt`, `yum` etc. + +## Installation: +Run the next commands: +``` +git clone https://github.com/Joursoir/lockpassword +make +sudo make install +``` + +## Synopsis: +lpass [command] [arguments] ... + +## Commands: +### init *gpg-key* +Initialize the password manager using the passed *gpg-key* as the encryption key. This command must be run first before you start working with LockPassword. +### insert [**-e, --echo**] [**-c, --copy**] [**-f, --force**] *passname* +Add the specified *passname* to the password manager. The password will be read interactively using standard input, character display is hidden. The **-e, --echo** argument enable the show of characters when typing a password; **-c, --copy** write password to clipboard; **-f, --force** ignore exist of *passname*, overwrites it without prompt. +### edit [**-t, --text-editor=text-editor**] *passname* +Open the specified *passname* in a text editor, waiting for changes. Standard text editor - vim, argument **-t, --text-editor = text-editor** allow you to change it. +### generate [**-l, --length=pass-length**] [**-c, --copy**] [**-f, --force**] *passname* +Generate a random password and write it in *passname*. The **-l, --length = pass-length** argument allow you to specify the desired password length. Without this argument, a 14 character password will be generated. **-c, --copy** write password to clipboard; **-f, --force** ignore exist of *passname*, overwrites it without prompt. +### mv/move [**-f, --force**] *old-path* *new-path* +Move/rename *old-path* to *new-path*. *old-path* must be an exist file, *new-path* can be a file/directory. The **-f, --force** argument ignore exist of *new-path* (if it's a file), overwrites it without prompt. +### rm/remove/delete *passname* +Remove the *passname* you specified from the password manager. If the directories where your *passname* was nested became empty after deletion, then they are also deleted. +### help +Print help information about commands and the application itself. +### version +Print information about the version, release date, and license of the application. + +## Guide: +* Initialize the password manager: +``` +[joursoir@archlin ~]$ lpass init joursoir@github.com +mkdir: created directory '/home/joursoir/.lock-password/' +LockPassword initialized successfully +``` +``` +[joursoir@archlin ~]$ lpass init 3BC3B37774696574B0F1C7D47B411E35F4F03E49 +mkdir: created directory '/home/joursoir/.lock-password/' +LockPassword initialized successfully +``` + +* Add password in the password manager: +``` +[joursoir@archlin ~]$ lpass insert games/chess/user +Please type your new password: [invisible input] +Please type your new password again: [invisible input] +Password added successfully for games/chess/user +``` + +* Print a list of exists password: +``` +[joursoir@archlin ~]$ lpass +Password Manager +|-- banks +| |-- abankpro +| | `-- phone_number +| `-- ubank +| `-- phone_number +`-- games + `-- chess + |-- site.com + `-- user +``` + +* Print a list of exists password in some directory: +``` +[joursoir@archlin ~]$ lpass banks +Password Manager/banks +|-- abankpro +| `-- phone_number +`-- ubank + `-- phone_number +``` + +* Show password: +``` +[joursoir@archlin ~]$ lpass games/iko/LordOfNight +helloitismypassword123 +``` + +* Copy password to clipboard: +``` +[joursoir@archlin ~]$ lpass -c games/iko/LordOfNight +Password copied to clipboard. +``` + +* Generate password: +``` +[joursoir@archlin ~]$ lpass generate bank/sbank/phone_number +Generated password: NsNu:+^Re(cshW +Password added successfully for bank/sbank/phone_number +``` + +# LockPassword - +простой терминальный менеджер паролей, использующий GnuPG для шифрования паролей. Приложение позиционирует себя, как открытое программное обеспечение. Распространяется под лицензией Unlicense. + +## Зависимости: +Для работы приложения необходимо установить следующие зависимости: +* tree +* gpg +* gcc + +P.S: их можно установить с помощью пакет менеджеров, таких как `pacman`, `apt`, `yum` и другие. + +## Установка: +Выполните следующие команды: +``` +git clone https://github.com/Joursoir/lockpassword +make +sudo make install +``` + +## Синтаксис: +lpass [command] [arguments] ... + +## Команды: +### init *gpg-key* +Инициализирует менеджер паролей, в качестве ключа шифрования использует переданный *gpg-key*. Это команда должна быть запущена самой первой, перед тем как вы начнете работу с LockPassword. +### insert [**-e, --echo**] [**-c, --copy**] [**-f, --force**] *passname* +Добавляет указанный *passname* в менеджер паролей. Пароль будет считан в интерактивном режиме, с помощью стандартного потока ввода данных, отображение символов скрыто. Аргумент **-e, --echo** позволяет включить отображение символов при вводе пароля; **-c, --copy** записывает пароль в буфер обмена; **-f, --force** игнорирует наличие существующего *passname*, перезаписывает его не спрашивая. +### edit [**-t, --text-editor=text-editor**] *passname* +Открывает указанный *passname* в текстовом редакторе, ожидая изменений. Стандартный текстовый редактор - vim, аргумент **-t, --text-editor=text-editor** позволяет сменить его. +### generate [**-l, --length=pass-length**] [**-c, --copy**] [**-f, --force**] *passname* +Генерирует случайный пароль и записывает его в *passname*. Аргумент **-l, --length=pass-length** позволяет указать желаемую длину пароля. Без данного аргумента будет сгенерирован пароль длиной 14 символов. Аргумент **-c, --copy** записывает пароль в буфер обмена; **-f, --force** игнорирует наличие существующего *passname*, перезаписывает его не спрашивая. +### mv/move [**-f, --force**] *old-path* *new-path* +Передвигает/переименовывает *old-path* в *new-path*. *old-path* обязательно должен быть существующим файлом, *new-path* может быть файлом/директорией. Аргумент **-f, --force** игнорирует наличие существующего *new-path* (если это файл), перезаписывает его не спрашивая. +### rm/remove/delete *passname* +Удаляет указанный вами *passname* из менеджера паролей. Если директории, куда был вложен ваш *passname* после удаления стали пусты, то они тоже удаляются. +### help +Выводит справочную информацию о командах и самом приложении. +### version +Выводит информацию о версии, дате выпуска и лицензии приложения. + +## Гайд: +* Инициализация менеджера паролей: +``` +[joursoir@archlin ~]$ lpass init joursoir@github.com +mkdir: created directory '/home/joursoir/.lock-password/' +LockPassword initialized successfully +``` +``` +[joursoir@archlin ~]$ lpass init 3BC3B37774696574B0F1C7D47B411E35F4F03E49 +mkdir: created directory '/home/joursoir/.lock-password/' +LockPassword initialized successfully +``` + +* Добавить пароль в менеджер паролей: +``` +[joursoir@archlin ~]$ lpass insert games/chess/user +Please type your new password: [невидимый ввод текста] +Please type your new password again: [невидимый ввод текста] +Password added successfully for games/chess/user +``` + +* Вывести список существующих паролей: +``` +[joursoir@archlin ~]$ lpass +Password Manager +|-- banks +| |-- abankpro +| | `-- phone_number +| `-- ubank +| `-- phone_number +`-- games + `-- chess + |-- site.com + `-- user +``` + +* Вывести список существующих паролей в какой-либо директории: +``` +[joursoir@archlin ~]$ lpass banks +Password Manager/banks +|-- abankpro +| `-- phone_number +`-- ubank + `-- phone_number +``` + +* Показать пароль: +``` +[joursoir@archlin ~]$ lpass games/iko/LordOfNight +helloitismypassword123 +``` + +* Скопировать пароль в буфер обмена: +``` +[joursoir@archlin ~]$ lpass -c games/iko/LordOfNight +Password copied to clipboard. +``` + +* Сгенерировать пароль: +``` +[joursoir@archlin ~]$ lpass generate bank/sbank/phone_number +Generated password: NsNu:+^Re(cshW +Password added successfully for bank/sbank/phone_number +``` \ No newline at end of file diff --git a/easydir.c b/easydir.c deleted file mode 100644 index 162bae7..0000000 --- a/easydir.c +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "handerror.h" - -int deleteFile(char *file_path) -{ - char *arguments[] = {"rm", file_path, NULL}; - easyFork("rm", arguments); - - return 1; -} - -int deleteEmptyDir(char *dir_path) -{ - #if defined(DEBUG) - char *arguments[] = {"rmdir", "-p", dir_path, NULL}; - #else - char *arguments[] = {"rmdir", "-p", "--ignore-fail-on-non-empty", dir_path, NULL}; - #endif - easyFork("rmdir", arguments); - - return 1; -} - -int checkFileExist(char *path_to_file) -{ - FILE *pFile; - - pFile = fopen(path_to_file, "r+"); // r+ so that errno can equal EISDIR - if(pFile == NULL) { - if(errno == ENOENT) // file doesn't exist - return 0; - if(errno == EISDIR) // it's directory - return 2; - else callError(120); - } - fclose(pFile); - - return 1; -} - -char *fileCropLineFeed(char *path, char *text, int maxlen) -{ - FILE *file = fopen(path, "r+"); - if(file == NULL) callError(130); - - int symbol; - int pos = 0; - char *str = (char *) malloc(sizeof(char) * maxlen); - while((symbol = fgetc(file))) - { - switch(symbol) - { - case '\n': - case EOF: { - str[pos] = '\0'; - pos = -1; // for break while - break; - } - default: { - str[pos] = symbol; - pos++; - break; - } - } - if(pos == -1) break; - if(pos > maxlen-1) { str[pos-1] = '\0'; break; } - } - fclose(file); - - strcpy(text, str); - free(str); - return text; -} diff --git a/easydir.h b/easydir.h deleted file mode 100644 index 4680709..0000000 --- a/easydir.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef EASYDIR_H -#define EASYDIR_H - -int deleteFile(char *file_path); -int deleteEmptyDir(char *dir_path); -int checkFileExist(char *path_to_file); -char *fileCropLineFeed(char *path, char *text, int maxlen); - -#endif \ No newline at end of file diff --git a/handerror.c b/handerror.c deleted file mode 100644 index 35e8f28..0000000 --- a/handerror.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include -#include - -void callError(int num) -{ - fprintf(stderr, "lpass: Sorry, there was an error in the program [#%d]\n", num); - exit(3); -} - -void printError(char *text) -{ - fprintf(stderr, "%s", text); - exit(4); -} - -void easyFork(char *name, char *arguments[]) -{ - int pid; - pid = fork(); - if(pid == -1) callError(100); - if(pid == 0) { /* new process */ - execvp(name, arguments); - perror(name); - exit(4); - } - wait(&pid); -} - diff --git a/handerror.h b/handerror.h deleted file mode 100644 index 6f75c7b..0000000 --- a/handerror.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef HANDERROR_H -#define HANDERROR_H - -void easyFork(char *name, char *arguments[]); -void callError(int num); -void printError(char *text); - -#endif \ No newline at end of file diff --git a/implementation.c b/implementation.c deleted file mode 100644 index 3ba2be7..0000000 --- a/implementation.c +++ /dev/null @@ -1,256 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "handerror.h" -#include "easydir.h" -#include "implementation.h" - -/* define in implementation.h */ -// GPG_PUBLICKEY_MAXLENGTH 1025 - -#define BASH_EXEC_COPY "lpass_copy.sh" - -// == global var == -extern char *gPath_rootdir; // /home/[username]/.lockpassword/ -extern char *gPath_subdir; // example: programming/github.com -extern char *gPath_pass; // example: programming/github.com/joursoir.gpg - -static void copyText(char *password) -{ - size_t size = (strlen(password) + strlen(BASH_EXEC_COPY) + 1) * sizeof(char); - char *command = malloc(size); - - snprintf(command, size, "%s %s", BASH_EXEC_COPY, password); - system(command); - - free(command); -} - -void checkForbiddenPaths(char *path) // check two dot in path -{ - int firstdot = 0; - for(int i=0; i < strlen(path); i++) - { - if(path[i] == '.') - firstdot ? printError("Error: please, don't use forbidden paths\n") : firstdot++; - else firstdot = 0; - } -} - -char *getGPGKey(char *dest, size_t size) -{ - FILE *fileGPG = fopen(".gpg-key", "r"); - if(fileGPG == NULL) { - if(errno == ENOENT) printError("error: No GPG key exists\n"); - callError(121); - } - - if(!fgets(dest, size, fileGPG)) { - callError(122); - } - fclose(fileGPG); - - return dest; -} - -char* getPassword(char *path_pass, char *password, size_t size, int flag_copy) -{ - int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; - char *secret_gpgkey = (char *) malloc(size_gpgkey); - getGPGKey(secret_gpgkey, size_gpgkey); - - char *arguments[] = {"gpg", "-d", "--quiet", "-r", secret_gpgkey, "-o", path_pass, gPath_pass, NULL}; - easyFork("gpg", arguments); - - FILE *filePass = fopen(path_pass, "r"); - if(filePass == NULL) callError(127); - - if(!fgets(password, size, filePass)) { - callError(111); - } - fclose(filePass); - - if(flag_copy) copyText(password); - - remove(path_pass); - free(secret_gpgkey); - return password; -} - -void nonvisibleEnter(int status) -{ - struct termios term_settings; - tcgetattr(0, &term_settings); // get current settings - if(status == 1) { - term_settings.c_lflag &= ~ECHO; // flag reset - } - else { - term_settings.c_lflag |= ECHO; - } - tcsetattr(0, TCSANOW, &term_settings); -} - -void insertPass(char *add_path, char *password, int flag_copy) -{ - /* gPath_rootdir = /home/[username]/.lock-password/ - add_path = banks/france/[number] - gPath_pass = banks/france/[number].gpg - gPath_subdir = banks/france */ - - int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; - char *secret_gpgkey = (char *) malloc(size_gpgkey); - getGPGKey(secret_gpgkey, size_gpgkey); - - char *arguments1[] = {"mkdir", "-p", gPath_subdir, NULL}; - easyFork("mkdir", arguments1); - - // create file, copy password there - FILE *filePass; - filePass = fopen(add_path, "w"); - if(filePass == NULL) { - callError(108); - } - fputs(password, filePass); - fclose(filePass); - - if(flag_copy) copyText(password); - - // encryption - char *arguments2[] = {"gpg", "--quiet", "--yes", "-r", secret_gpgkey, "-e", add_path, NULL}; - easyFork("gpg", arguments2); - - remove(add_path); - free(secret_gpgkey); -} - -char *typePass(char *text, char *dest, int minlen, int maxlen) -{ - printf("%s", text); - if(fgets(dest, sizeof(char)*maxlen, stdin) == NULL) { - nonvisibleEnter(0); - printError("lpass: Unexpected end of file\n"); - } - - int len = strlen(dest); - if(len < minlen || len > maxlen) { - nonvisibleEnter(0); - printError("lpass: incorrect password\n"); - } - - if(dest[len-1] == '\n') { - dest[len-1] = '\0'; - } - - #if defined(DEBUG) - printf("%s", dest); - #endif - - printf("\n"); // for new line - return dest; -} - -int userEnterPassword(int minlen, int maxlen, char *path_insert, int flag_echo, int flag_copy) -{ - char *pass_one = (char *) malloc(sizeof(char) * maxlen); - int rvalue = 0; - if(!flag_echo) { - char *pass_two = (char *) malloc(sizeof(char) * maxlen); - - nonvisibleEnter(1); // change terminal work - typePass("Type your password: ", pass_one, minlen, maxlen); - typePass("Type your password again: ", pass_two, minlen, maxlen); - nonvisibleEnter(0); - - if(strcmp(pass_one, pass_two) == 0) { - insertPass(path_insert, pass_one, flag_copy); - rvalue = 1; - } - free(pass_two); - } - else { - typePass("Type your password: ", pass_one, minlen, maxlen); - insertPass(path_insert, pass_one, flag_copy); - rvalue = 1; - } - - free(pass_one); - return rvalue; -} - -char *generatePassword(char *dest, int amount, int max_len) -{ - char allowed_symbols[] = { - 'A','E','I','J','O','U','B','C','D','F','G','H', - 'K','L','M','N','P','Q','R','S','T','V','W','X', - 'Y','Z','a','e','i','j','o','u','b','c','d','f', - 'g','h','k','l','m','n','p','q','r','s','t','v', - 'w','x','y','z','1','2','3','4','5','6','7','8', - '9','0','!','#','$',';','%','^',':','&','?','*', - '(',')','-','_','+','=','<', '>' - }; - int max = sizeof(allowed_symbols); - srand(time(NULL)); - - char password[max_len]; - for(int i=0; i < amount; i++) - { - char c = allowed_symbols[rand() % max]; - - password[i] = c; - password[i+1] = '\0'; - } - - strcpy(dest, password); - return dest; -} - -unsigned long hash(char *str) -{ - unsigned long hash = 5381; - char c; - - while( (c = *str++) ) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} - -static void clearStdinBuff() -{ - char garbage; - - while( (garbage = fgetc(stdin)) != '\n' && garbage != EOF ) - ; -} - - -int getOverwriteAnswer(char *path) -{ - int buffSize = (strlen("Password for '' exists. Overwrite? (Y/N)") + strlen(path) + 1)* sizeof(char); - char *text = malloc(buffSize); - snprintf(text, buffSize, "Password for '%s' exists. Overwrite? (Y/N)", path); - printf("%s ", text); - - int answer; - while((answer = fgetc(stdin))) - { - clearStdinBuff(); - switch(answer) - { - case 'Y': - case 'y': { free(text); return 1; } - case 'N': - case 'n': { free(text); return 0; } - case EOF: printError("Error: Unexpected end of file\n"); - default: { printf("%s ", text); break; } - } - } - - free(text); - return -1; -} diff --git a/implementation.h b/implementation.h deleted file mode 100644 index f41cc4a..0000000 --- a/implementation.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef IMPLEMENTATION_H -#define IMPLEMENTATION_H - -#define GPG_PUBLICKEY_MAXLENGTH 1025 // +1 for '\0' - -void checkForbiddenPaths(char *path); -char *getGPGKey(char *dest, size_t size); -char* getPassword(char *path_pass, char *password, size_t size, int flag_copy); -void nonvisibleEnter(int status); -void insertPass(char *add_path, char *password, int flag_copy); -char *typePass(char *text, char *dest, int minlen, int maxlen); -int userEnterPassword(int minlen, int maxlen, char *path_insert, int flag_echo, int flag_copy); -char *generatePassword(char *dest, int amount, int max_len); -unsigned long hash(char *str); -int getOverwriteAnswer(char *path); - -#endif \ No newline at end of file diff --git a/main.c b/main.c deleted file mode 100644 index 06154ec..0000000 --- a/main.c +++ /dev/null @@ -1,491 +0,0 @@ -/* -** Code written by Joursoir -** -** This is free and unencumbered software released into the public domain. -** (C) The Unlicense -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "easydir.h" -#include "handerror.h" -#include "implementation.h" - -#define VERSION "1.0" -#define DATE_RELEASE "21 October, 2020" -//#define DEBUG -#define STANDARD_TEXTEDITOR "vim" -#define MAXLEN_TEXTEDITOR 16 -#define MINLEN_PASSWORD 1 -#define MAXLEN_PASSWORD 128 -#define STANDARD_AMOUNT_GENERATE_SYMBOLS 14 -#define LOCKPASS_DIR "/.lock-password/" -#define GPGKEY_FILE "/.gpg-key" - -#define TREE_OUTPUT_FILE ".tree" -#define TEXTEDITOR_FILE ".text-editor" - -#define HASH_INIT 6385337657 -#define HASH_HELP 6385292014 -#define HASH_VERSION 229486327000139 -#define HASH_EDIT 6385183019 -#define HASH_MV 5863624 -#define HASH_MOVE 249844339311324255 -#define HASH_GENERATE 7572409341523952 -#define HASH_INSERT 6953633055386 -#define HASH_RM 5863780 -#define HASH_REMOVE 6953974396019 -#define HASH_DELETE 6953426453624 -#define WITHOUT_ARGUMENTS 1 - -#define STR_SHOWTREEUSE "Use: lpass [-c=passname] [passname]\n" -#define STR_INITUSE "Use: lpass init gpg-key\n" -#define STR_INSERTUSE "Use: lpass insert [-ef] passname\n" -#define STR_EDITUSE "Use: lpass edit [-t=text-editor] passname\n" -#define STR_GENERATEUSE "Use: lpass generate [-l=pass-length] [-f] passname\n" -#define STR_REMOVEUSE "Use: lpass remove/rm/delete passname\n" -#define STR_MOVEUSE "Use: lpass move/mv [-f] old-path new-path\n" - -// == global var == -char *gPath_rootdir; // /home/[username]/.lockpassword/ -char *gPath_subdir; // example: programming/github.com -char *gPath_pass; // example: programming/github.com/joursoir.gpg - -static void globalSplitPath(char *source) -{ - int len_path = strlen(source) + strlen(".gpg") + 1; - - gPath_pass = malloc(sizeof(char) * len_path); // path without working dir - strcpy(gPath_pass, source); - strcat(gPath_pass, ".gpg"); - - gPath_subdir = malloc(sizeof(char) * len_path); // path without working dir and pass file - strcpy(gPath_subdir, source); - gPath_subdir = dirname(gPath_subdir); - - #if defined(DEBUG) - printf("dir: %s\n", gPath_subdir); - printf("pass: %s\n", gPath_pass); - #endif -} - -static void cmd_init(int argc, char *argv[]) -{ - char *gpg_key = argv[2]; - if(gpg_key == NULL) printError(STR_INITUSE); - - // create main directory: - int len_init_storage = strlen(gPath_rootdir) + strlen(GPGKEY_FILE) + 1; // +1 for '\0' - char *path_init_storage = (char *) malloc(sizeof(char) * len_init_storage); - strcpy(path_init_storage, gPath_rootdir); - - char *arguments[] = {"mkdir", "-vp", path_init_storage, NULL}; - easyFork("mkdir", arguments); - - strcat(path_init_storage, GPGKEY_FILE); - - // create .gpg-key in storage - FILE *filekey; - filekey = fopen(path_init_storage, "w"); - if(filekey == NULL) { - callError(122); - } - fputs(gpg_key, filekey); - fclose(filekey); - - free(path_init_storage); - printf("LockPassword initialized for %s\n", gpg_key); -} - -static void cmd_edit(int argc, char *argv[]) -{ - const struct option long_options[] = { - {"text-editor", required_argument, NULL, 't'}, - {NULL, 0, NULL, 0} - }; - - int result; - while((result = getopt_long(argc, argv, "t:", long_options, NULL)) != -1) { - switch(result) { - case 't': - { - // create file, copy name text editor there - FILE *f_texteditor = fopen(TEXTEDITOR_FILE, "w"); - if(f_texteditor == NULL) callError(108); - fputs(optarg, f_texteditor); - fclose(f_texteditor); - printf("You changed text editor to %s\n", optarg); - break; - } - default: printError(STR_EDITUSE); - } - } - - if(optind < argc) optind++; // for skip "edit" - #if defined(DEBUG) - for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); - printf("passname: %s\n", argv[optind]); - #endif - - char *path_to_password; - if(argv[optind] == NULL) printError(STR_EDITUSE); - else path_to_password = argv[optind]; - - checkForbiddenPaths(path_to_password); - globalSplitPath(path_to_password); - - if(checkFileExist(gPath_pass) != 1) - printError("Error: No such file exists\n"); - - // configure text editor file - char text_editor[MAXLEN_TEXTEDITOR]; - FILE *f_texteditor = fopen(TEXTEDITOR_FILE, "r"); - if(f_texteditor == NULL) { - f_texteditor = fopen(TEXTEDITOR_FILE, "w"); - if(f_texteditor == NULL) callError(108); - fputs(STANDARD_TEXTEDITOR, f_texteditor); // in file - strcpy(text_editor, STANDARD_TEXTEDITOR); // in variable - } - else { - if(!fgets(text_editor, sizeof(char)*MAXLEN_TEXTEDITOR, f_texteditor)) - callError(122); - } - fclose(f_texteditor); - - #if defined(DEBUG) - printf("using text editor: %s\n", text_editor); - #endif - // end configure - - // decryption - int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; - char *secret_gpgkey = (char *) malloc(size_gpgkey); - getGPGKey(secret_gpgkey, size_gpgkey); - - char *decryp_arg[] = {"gpg", "-d", "--quiet", "-r", secret_gpgkey, "-o", path_to_password, gPath_pass, NULL}; - easyFork("gpg", decryp_arg); - - // start vim/etc for edit passowrd - char *texte_arg[] = {text_editor, path_to_password, NULL}; - easyFork(text_editor, texte_arg); - - // delete '\n' and paste good pass - char password[MAXLEN_PASSWORD]; - fileCropLineFeed(path_to_password, password, MAXLEN_PASSWORD); - - FILE *file = fopen(path_to_password, "w"); - if(file == NULL) callError(108); - fputs(password, file); - fclose(file); - - // encryption - char *encryp_arg[] = {"gpg", "--quiet", "--yes", "-r", secret_gpgkey, "-e", path_to_password, NULL}; - easyFork("gpg", encryp_arg); - - remove(path_to_password); - free(secret_gpgkey); -} - -static void cmd_move(int argc, char *argv[]) -{ - /* we have a two situation: - 1) mv file file - 2) mv file directory */ - - const struct option long_options[] = { - {"force", no_argument, NULL, 'f'}, - {NULL, 0, NULL, 0} - }; - - int result, flag_force = 0; - while((result = getopt_long(argc, argv, "f", long_options, NULL)) != -1) { - switch(result) { - case 'f': { flag_force = 1; break; } - default: printError(STR_MOVEUSE); - } - } - - if(optind < argc) optind++; // for skip "move" - #if defined(DEBUG) - for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); - printf("old-path: %s\n", argv[optind]); - if(argv[optind] != NULL) printf("new-path: %s\n", argv[optind+1]); - #endif - - if(argv[optind] == NULL) printError(STR_MOVEUSE); - if(argv[optind+1] == NULL) printError(STR_MOVEUSE); - - char *old_path = argv[optind]; - checkForbiddenPaths(old_path); globalSplitPath(old_path); - if(checkFileExist(gPath_pass) != 1) printError("Error: No such old-path exists\n"); - - char *old_path_gpg = gPath_pass; - char *old_path_subdir = gPath_subdir; - - char *new_path = argv[optind+1]; - checkForbiddenPaths(new_path); globalSplitPath(new_path); - - if(checkFileExist(new_path) == 2) // if new-path = dir - ; - else if(checkFileExist(gPath_pass) == 1) { // if new-path = file - if(!flag_force) { - if(getOverwriteAnswer(new_path) != 1) - return; - } - new_path = gPath_pass; - } - else printError("Error: No such new-path exists\n"); - - char *arguments[] = {"mv", "-f", old_path_gpg, new_path, NULL}; - easyFork("mv", arguments); - - deleteEmptyDir(old_path_subdir); - free(old_path_subdir); free(old_path_gpg); -} - -static void cmd_generate(int argc, char *argv[]) -{ - int pass_length = STANDARD_AMOUNT_GENERATE_SYMBOLS, flag_force = 0, flag_copy = 0, result; - const struct option long_options[] = { - {"length", required_argument, NULL, 'l'}, - {"force", no_argument, NULL, 'f'}, - {"copy", no_argument, NULL, 'c'}, - {NULL, 0, NULL, 0} - }; - - while((result = getopt_long(argc, argv, "l:fc", long_options, NULL)) != -1) { - switch(result) { - // if optarg - incorrect number, atoi return 0 - case 'l': { pass_length = atoi(optarg); break; } - case 'f': { flag_force = 1; break; } - case 'c': { flag_copy = 1; break; } - default: printError(STR_GENERATEUSE); - } - } - - if(optind < argc) optind++; // for skip "generate" - #if defined(DEBUG) - for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); - printf("passname: %s\n", argv[optind]); - #endif - - char *path_to_password; - if(argv[optind] == NULL) printError(STR_GENERATEUSE); - else path_to_password = argv[optind]; - - if(pass_length < MINLEN_PASSWORD || pass_length > MAXLEN_PASSWORD) - printError("Error: you typed an incorrect number\n"); - - checkForbiddenPaths(path_to_password); - globalSplitPath(path_to_password); - - if(checkFileExist(gPath_pass) == 1) { - if(!flag_force) { - if(getOverwriteAnswer(path_to_password) != 1) - return; - } - } - - // generate password - char gpass[MAXLEN_PASSWORD]; - generatePassword(gpass, pass_length, MAXLEN_PASSWORD); - - insertPass(path_to_password, gpass, flag_copy); - if(!flag_copy) printf("Generated password: %s\n", gpass); - printf("Password added successfully for %s\n", path_to_password); -} - -static void cmd_insert(int argc, char *argv[]) -{ - int flag_echo = 0, flag_force = 0, flag_copy = 0, result; - const struct option long_options[] = { - {"echo", no_argument, NULL, 'e'}, - {"force", no_argument, NULL, 'f'}, - {"copy", no_argument, NULL, 'c'}, - {NULL, 0, NULL, 0} - }; - - while((result = getopt_long(argc, argv, "efc", long_options, NULL)) != -1) { - switch(result) { - case 'e': { flag_echo = 1; break; } - case 'f': { flag_force = 1; break; } - case 'c': { flag_copy = 1; break; } - default: printError(STR_INSERTUSE); - } - } - - if(optind < argc) optind++; // for skip "insert" - #if defined(DEBUG) - for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); - printf("passname: %s\n", argv[optind]); - #endif - - char *path_to_password; - if(argv[optind] == NULL) printError(STR_INSERTUSE); - else path_to_password = argv[optind]; - - checkForbiddenPaths(path_to_password); - globalSplitPath(path_to_password); - - if(checkFileExist(gPath_pass) == 1) { - if(!flag_force) { - if(getOverwriteAnswer(path_to_password) != 1) - return; - } - } - - if(userEnterPassword(MINLEN_PASSWORD, MAXLEN_PASSWORD, path_to_password, flag_echo, flag_copy) == 1) { - printf("Password added successfully for %s\n", path_to_password); - } - else - printf("Passwords do not match\n"); -} - -static void cmd_remove(int argc, char *argv[]) -{ - char *path_to_password = argv[2]; - if(path_to_password == NULL) - printError(STR_REMOVEUSE); - - checkForbiddenPaths(path_to_password); - globalSplitPath(path_to_password); - - if(checkFileExist(gPath_pass) != 1) - printError("Error: No such file exists\n"); - - if(deleteFile(gPath_pass)) - deleteEmptyDir(gPath_subdir); -} - -static void cmd_showtree(int argc, char *argv[]) -{ - int flag_copy = 0, result; - char *path; - const struct option long_options[] = { - {"copy", no_argument, NULL, 'c'}, - {NULL, 0, NULL, 0} - }; - - while((result = getopt_long(argc, argv, "c", long_options, NULL)) != -1) { - switch(result) { - case 'c': { flag_copy = 1; break; } - default: printError(STR_SHOWTREEUSE); - } - } - - #if defined(DEBUG) - for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); - printf("passname: %s\n", argv[optind]); - #endif - - if(argv[optind] == NULL) { - if(flag_copy) printError(STR_SHOWTREEUSE); - else { - path = (char *) malloc(sizeof(char) * 2); - strcpy(path, "."); - } - } - else path = argv[optind]; - checkForbiddenPaths(path); - - if(opendir(path) != NULL) // if it's directory - { - if(flag_copy) printError("Error: you must type a passname, not a directory\n"); - - char *arg1[] = {"tree", "-C", "--noreport", path, "-o", TREE_OUTPUT_FILE, NULL}; - easyFork("tree", arg1); - - char *arg2[] = {"sed", "-i", "-E", "s/\\.gpg(\\x1B\\[[0-9]+m)?( ->|$)/\\1\\2/g", TREE_OUTPUT_FILE, NULL}; - easyFork("sed", arg2); // remove .gpg at the pass name - - if(strcmp(path, ".") == 0) printf("Password Manager\n"); - else printf("Password Manager/%s\n", path); - - char *arg3[] = {"tail", "-n", "+2", TREE_OUTPUT_FILE, NULL}; - easyFork("tail", arg3); // remove working directory - - remove(TREE_OUTPUT_FILE); - } - else - { - globalSplitPath(path); - - if(checkFileExist(gPath_pass) == 1) // exist - { - char password[MAXLEN_PASSWORD]; - getPassword(path, password, sizeof(char)*MAXLEN_PASSWORD, flag_copy); - if(!flag_copy) printf("%s\n", password); - } - else printf("Error: %s is not in the password storage\n", path); - } - - if(argv[1] == NULL) free(path); -} - -static void cmd_help() -{ - -} - -static void cmd_version() -{ - printf("LockPassword v%s\n", VERSION); - printf("Release date: %s\n\n", DATE_RELEASE); - printf("Code written by Joursoir\n"); - printf("This is free and unencumbered software released into the public domain.\n\n"); -} - -int main(int argc, char *argv[]) -{ - if(!isatty(0)) { // stdin - printError("lpass: Please, use a terminal to run this program\n"); - } - - /* init global path to root directory */ - int len_rootdir = strlen(getenv("HOME")) + strlen(LOCKPASS_DIR) + 1; // +1 for '\0' - - gPath_rootdir = (char *) malloc(sizeof(char) * len_rootdir); - strcpy(gPath_rootdir, getenv("HOME")); - strcat(gPath_rootdir, LOCKPASS_DIR); - /* end init */ - - unsigned long ihash = WITHOUT_ARGUMENTS; - if(argv[1] != NULL) { - ihash = hash(argv[1]); - } - - if(chdir(gPath_rootdir) != 0 && ihash != HASH_INIT) { - printf("Before starting work, you must initialize LockPassword\n"); - printError(STR_INITUSE); - } - - switch(ihash) - { - case HASH_INIT: { cmd_init(argc, argv); break; } - case HASH_EDIT: { cmd_edit(argc, argv); break; } - case HASH_MV: - case HASH_MOVE: { cmd_move(argc, argv); break; } - case HASH_GENERATE: { cmd_generate(argc, argv); break; } - case HASH_INSERT: { cmd_insert(argc, argv); break; } - case HASH_RM: - case HASH_REMOVE: - case HASH_DELETE: { cmd_remove(argc, argv); break; } - case HASH_HELP: { cmd_help(); break; } - case HASH_VERSION: { cmd_version(); break; } - default: { cmd_showtree(argc, argv); break; } - } - - if(gPath_subdir != NULL) { - free(gPath_subdir); - free(gPath_pass); - } - free(gPath_rootdir); - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/man/lpass.1 b/man/lpass.1 new file mode 100644 index 0000000..02ed320 --- /dev/null +++ b/man/lpass.1 @@ -0,0 +1,162 @@ +.TH "LPASS" "1" "30\ \&OCTOBER\ \&2020" "LPASS v1.0" "LockPassword" + +.SH "NAME" +LockPassword - a password manager. + +.SH "SYNOPSIS" +\fBlpass\fR [\fBcommand\fR] [\fBarguments\fR] ... + +.SH "DESCRIPTION" +\fBlpass\fR \- a simple terminal password manager, using GnuPG to encrypt passwords. The application positions itself as open-source software. Distributed under the Unlicense license. + +.SH "COMMANDS" +\fBinit \fIgpg-key\fR +.RS 4 +Initialize the password manager using the passed \fIgpg-key\fR as the encryption key. This command must be run first before you start working with LockPassword. +.RE +.PP + +\fBinsert\fR [\fB-e, --echo\fR] [\fB-c, --copy\fR] [\fB-f, --force\fR] \fIpassname\fR +.RS 4 +Add the specified \fIpassname\fR to the password manager. The password will be read interactively using standard input, character display is hidden. The \fB-e, --echo\fR argument enable the show of characters when typing a password; \fB-c, --copy\fR write password to clipboard; \fB-f, --force\fR ignore exist of \fIpassname\fR, overwrites it without prompt. +.RE +.PP + +\fBedit\fR [\fB-t, --text-editor=text-editor\fR] \fIpassname\fR +.RS 4 +Open the specified \fIpassname\fR in a text editor, waiting for changes. Standard text editor - vim, argument \fB-t, --text-editor = text-editor\fR allow you to change it. +.RE +.PP + +\fBgenerate\fR [\fB-l, --length=pass-length\fB] [\fB-c, --copy\fR] [\fB-f, --force\fR] \fIpassname\fR +.RS 4 +Generate a random password and write it in \fIpassname\fR. The \fB-l, --length = pass-length\fR argument allow you to specify the desired password length. Without this argument, a 14 character password will be generated. \fB-c, --copy\fR write password to clipboard; \fB-f, --force\fR ignore exist of \fIpassname\fR, overwrites it without prompt. +.RE +.PP + +\fBmv/move\R [\fB-f, --force\fR] \fIold-path\R \fInew-path\fR +.RS 4 +Move/rename \fIold-path\fR to \fInew-path\fR. \fIold-path\fR must be an exist file, \fInew-path\fR can be a file/directory. The \fB-f, --force\fR argument ignore exist of \fInew-path\fR (if it's a file), overwrites it without prompt. +.RE +.PP + +\fBrm/remove/delete \fIpassname\fR +.RS 4 +Remove the \fIpassname\fR you specified from the password manager. If the directories where your \fIpassname\fR was nested became empty after deletion, then they are also deleted. +.RE +.PP + +\fBhelp\fR +.RS 4 +Print help information about commands and the application itself. +.RE +.PP + +\fBversion\fR +.RS 4 +Print information about the version, release date, and license of the application. +.RE +.PP + +.SH "EXAMPLES" +Initialize the password manager: +.RS 4 +\fB[joursoir@archlin ~]$ lpass init joursoir@github.com\fR +.br +mkdir: created directory '/home/joursoir/.lock-password/'R +.br +LockPassword initialized successfully +.RE +.PP + +Add password in the password manager: +.RS 4 +\fB[joursoir@archlin ~]$ lpass insert games/chess/user\fR +.br +Please type your new password: [invisible input] +.br +Please type your new password again: [invisible input] +.br +Password added successfully for games/chess/user +.RE +.PP + +Print a list of exists password: +.RS 4 +\fB[joursoir@archlin ~]$ lpass\fR +.br +Password Manager +.br +|-- banks +.br +| |-- abankpro +.br +| | `-- phone_number +.br +| `-- ubank +.br +| `-- phone_number +.br +`-- games +.br + `-- chess +.br + |-- site.com +.br + `-- user +.RE +.PP + +Print a list of exists password in some directory: +.RS 4 +\fB[joursoir@archlin ~]$ lpass banks\fR +.br +Password Manager/banks +.br +|-- abankpro +.br +| `-- phone_number +.br +`-- ubank +.br + `-- phone_number +.RE +.PP + +Show password: +.RS 4 +\fB[joursoir@archlin ~]$ lpass games/iko/LordOfNight\fR +.br +helloitismypassword123 +.RE +.PP + +Copy password to clipboard: +.RS 4 +\fB[joursoir@archlin ~]$ lpass -c games/iko/LordOfNight\fR +.br +Password copied to clipboard. +.RE +.PP + +Generate password: +.RS 4 +\fB[joursoir@archlin ~]$ lpass generate bank/sbank/phone_number\fR +.br +Generated password: NsNu:+^Re(cshW +.br +Password added successfully for bank/sbank/phone_number +.RE +.PP + +.SH "AUTHOR" +Code was written by Joursoir + +.SH "COPYING" +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. + +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 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. \ No newline at end of file diff --git a/src/easydir.c b/src/easydir.c new file mode 100644 index 0000000..162bae7 --- /dev/null +++ b/src/easydir.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include + +#include "handerror.h" + +int deleteFile(char *file_path) +{ + char *arguments[] = {"rm", file_path, NULL}; + easyFork("rm", arguments); + + return 1; +} + +int deleteEmptyDir(char *dir_path) +{ + #if defined(DEBUG) + char *arguments[] = {"rmdir", "-p", dir_path, NULL}; + #else + char *arguments[] = {"rmdir", "-p", "--ignore-fail-on-non-empty", dir_path, NULL}; + #endif + easyFork("rmdir", arguments); + + return 1; +} + +int checkFileExist(char *path_to_file) +{ + FILE *pFile; + + pFile = fopen(path_to_file, "r+"); // r+ so that errno can equal EISDIR + if(pFile == NULL) { + if(errno == ENOENT) // file doesn't exist + return 0; + if(errno == EISDIR) // it's directory + return 2; + else callError(120); + } + fclose(pFile); + + return 1; +} + +char *fileCropLineFeed(char *path, char *text, int maxlen) +{ + FILE *file = fopen(path, "r+"); + if(file == NULL) callError(130); + + int symbol; + int pos = 0; + char *str = (char *) malloc(sizeof(char) * maxlen); + while((symbol = fgetc(file))) + { + switch(symbol) + { + case '\n': + case EOF: { + str[pos] = '\0'; + pos = -1; // for break while + break; + } + default: { + str[pos] = symbol; + pos++; + break; + } + } + if(pos == -1) break; + if(pos > maxlen-1) { str[pos-1] = '\0'; break; } + } + fclose(file); + + strcpy(text, str); + free(str); + return text; +} diff --git a/src/easydir.h b/src/easydir.h new file mode 100644 index 0000000..4680709 --- /dev/null +++ b/src/easydir.h @@ -0,0 +1,9 @@ +#ifndef EASYDIR_H +#define EASYDIR_H + +int deleteFile(char *file_path); +int deleteEmptyDir(char *dir_path); +int checkFileExist(char *path_to_file); +char *fileCropLineFeed(char *path, char *text, int maxlen); + +#endif \ No newline at end of file diff --git a/src/handerror.c b/src/handerror.c new file mode 100644 index 0000000..35e8f28 --- /dev/null +++ b/src/handerror.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +void callError(int num) +{ + fprintf(stderr, "lpass: Sorry, there was an error in the program [#%d]\n", num); + exit(3); +} + +void printError(char *text) +{ + fprintf(stderr, "%s", text); + exit(4); +} + +void easyFork(char *name, char *arguments[]) +{ + int pid; + pid = fork(); + if(pid == -1) callError(100); + if(pid == 0) { /* new process */ + execvp(name, arguments); + perror(name); + exit(4); + } + wait(&pid); +} + diff --git a/src/handerror.h b/src/handerror.h new file mode 100644 index 0000000..6f75c7b --- /dev/null +++ b/src/handerror.h @@ -0,0 +1,8 @@ +#ifndef HANDERROR_H +#define HANDERROR_H + +void easyFork(char *name, char *arguments[]); +void callError(int num); +void printError(char *text); + +#endif \ No newline at end of file diff --git a/src/implementation.c b/src/implementation.c new file mode 100644 index 0000000..3ba2be7 --- /dev/null +++ b/src/implementation.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "handerror.h" +#include "easydir.h" +#include "implementation.h" + +/* define in implementation.h */ +// GPG_PUBLICKEY_MAXLENGTH 1025 + +#define BASH_EXEC_COPY "lpass_copy.sh" + +// == global var == +extern char *gPath_rootdir; // /home/[username]/.lockpassword/ +extern char *gPath_subdir; // example: programming/github.com +extern char *gPath_pass; // example: programming/github.com/joursoir.gpg + +static void copyText(char *password) +{ + size_t size = (strlen(password) + strlen(BASH_EXEC_COPY) + 1) * sizeof(char); + char *command = malloc(size); + + snprintf(command, size, "%s %s", BASH_EXEC_COPY, password); + system(command); + + free(command); +} + +void checkForbiddenPaths(char *path) // check two dot in path +{ + int firstdot = 0; + for(int i=0; i < strlen(path); i++) + { + if(path[i] == '.') + firstdot ? printError("Error: please, don't use forbidden paths\n") : firstdot++; + else firstdot = 0; + } +} + +char *getGPGKey(char *dest, size_t size) +{ + FILE *fileGPG = fopen(".gpg-key", "r"); + if(fileGPG == NULL) { + if(errno == ENOENT) printError("error: No GPG key exists\n"); + callError(121); + } + + if(!fgets(dest, size, fileGPG)) { + callError(122); + } + fclose(fileGPG); + + return dest; +} + +char* getPassword(char *path_pass, char *password, size_t size, int flag_copy) +{ + int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; + char *secret_gpgkey = (char *) malloc(size_gpgkey); + getGPGKey(secret_gpgkey, size_gpgkey); + + char *arguments[] = {"gpg", "-d", "--quiet", "-r", secret_gpgkey, "-o", path_pass, gPath_pass, NULL}; + easyFork("gpg", arguments); + + FILE *filePass = fopen(path_pass, "r"); + if(filePass == NULL) callError(127); + + if(!fgets(password, size, filePass)) { + callError(111); + } + fclose(filePass); + + if(flag_copy) copyText(password); + + remove(path_pass); + free(secret_gpgkey); + return password; +} + +void nonvisibleEnter(int status) +{ + struct termios term_settings; + tcgetattr(0, &term_settings); // get current settings + if(status == 1) { + term_settings.c_lflag &= ~ECHO; // flag reset + } + else { + term_settings.c_lflag |= ECHO; + } + tcsetattr(0, TCSANOW, &term_settings); +} + +void insertPass(char *add_path, char *password, int flag_copy) +{ + /* gPath_rootdir = /home/[username]/.lock-password/ + add_path = banks/france/[number] + gPath_pass = banks/france/[number].gpg + gPath_subdir = banks/france */ + + int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; + char *secret_gpgkey = (char *) malloc(size_gpgkey); + getGPGKey(secret_gpgkey, size_gpgkey); + + char *arguments1[] = {"mkdir", "-p", gPath_subdir, NULL}; + easyFork("mkdir", arguments1); + + // create file, copy password there + FILE *filePass; + filePass = fopen(add_path, "w"); + if(filePass == NULL) { + callError(108); + } + fputs(password, filePass); + fclose(filePass); + + if(flag_copy) copyText(password); + + // encryption + char *arguments2[] = {"gpg", "--quiet", "--yes", "-r", secret_gpgkey, "-e", add_path, NULL}; + easyFork("gpg", arguments2); + + remove(add_path); + free(secret_gpgkey); +} + +char *typePass(char *text, char *dest, int minlen, int maxlen) +{ + printf("%s", text); + if(fgets(dest, sizeof(char)*maxlen, stdin) == NULL) { + nonvisibleEnter(0); + printError("lpass: Unexpected end of file\n"); + } + + int len = strlen(dest); + if(len < minlen || len > maxlen) { + nonvisibleEnter(0); + printError("lpass: incorrect password\n"); + } + + if(dest[len-1] == '\n') { + dest[len-1] = '\0'; + } + + #if defined(DEBUG) + printf("%s", dest); + #endif + + printf("\n"); // for new line + return dest; +} + +int userEnterPassword(int minlen, int maxlen, char *path_insert, int flag_echo, int flag_copy) +{ + char *pass_one = (char *) malloc(sizeof(char) * maxlen); + int rvalue = 0; + if(!flag_echo) { + char *pass_two = (char *) malloc(sizeof(char) * maxlen); + + nonvisibleEnter(1); // change terminal work + typePass("Type your password: ", pass_one, minlen, maxlen); + typePass("Type your password again: ", pass_two, minlen, maxlen); + nonvisibleEnter(0); + + if(strcmp(pass_one, pass_two) == 0) { + insertPass(path_insert, pass_one, flag_copy); + rvalue = 1; + } + free(pass_two); + } + else { + typePass("Type your password: ", pass_one, minlen, maxlen); + insertPass(path_insert, pass_one, flag_copy); + rvalue = 1; + } + + free(pass_one); + return rvalue; +} + +char *generatePassword(char *dest, int amount, int max_len) +{ + char allowed_symbols[] = { + 'A','E','I','J','O','U','B','C','D','F','G','H', + 'K','L','M','N','P','Q','R','S','T','V','W','X', + 'Y','Z','a','e','i','j','o','u','b','c','d','f', + 'g','h','k','l','m','n','p','q','r','s','t','v', + 'w','x','y','z','1','2','3','4','5','6','7','8', + '9','0','!','#','$',';','%','^',':','&','?','*', + '(',')','-','_','+','=','<', '>' + }; + int max = sizeof(allowed_symbols); + srand(time(NULL)); + + char password[max_len]; + for(int i=0; i < amount; i++) + { + char c = allowed_symbols[rand() % max]; + + password[i] = c; + password[i+1] = '\0'; + } + + strcpy(dest, password); + return dest; +} + +unsigned long hash(char *str) +{ + unsigned long hash = 5381; + char c; + + while( (c = *str++) ) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} + +static void clearStdinBuff() +{ + char garbage; + + while( (garbage = fgetc(stdin)) != '\n' && garbage != EOF ) + ; +} + + +int getOverwriteAnswer(char *path) +{ + int buffSize = (strlen("Password for '' exists. Overwrite? (Y/N)") + strlen(path) + 1)* sizeof(char); + char *text = malloc(buffSize); + snprintf(text, buffSize, "Password for '%s' exists. Overwrite? (Y/N)", path); + printf("%s ", text); + + int answer; + while((answer = fgetc(stdin))) + { + clearStdinBuff(); + switch(answer) + { + case 'Y': + case 'y': { free(text); return 1; } + case 'N': + case 'n': { free(text); return 0; } + case EOF: printError("Error: Unexpected end of file\n"); + default: { printf("%s ", text); break; } + } + } + + free(text); + return -1; +} diff --git a/src/implementation.h b/src/implementation.h new file mode 100644 index 0000000..f41cc4a --- /dev/null +++ b/src/implementation.h @@ -0,0 +1,17 @@ +#ifndef IMPLEMENTATION_H +#define IMPLEMENTATION_H + +#define GPG_PUBLICKEY_MAXLENGTH 1025 // +1 for '\0' + +void checkForbiddenPaths(char *path); +char *getGPGKey(char *dest, size_t size); +char* getPassword(char *path_pass, char *password, size_t size, int flag_copy); +void nonvisibleEnter(int status); +void insertPass(char *add_path, char *password, int flag_copy); +char *typePass(char *text, char *dest, int minlen, int maxlen); +int userEnterPassword(int minlen, int maxlen, char *path_insert, int flag_echo, int flag_copy); +char *generatePassword(char *dest, int amount, int max_len); +unsigned long hash(char *str); +int getOverwriteAnswer(char *path); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5a9971c --- /dev/null +++ b/src/main.c @@ -0,0 +1,517 @@ +/* +** Code written by Joursoir +** +** This is free and unencumbered software released into the public domain. +** (C) The Unlicense +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "easydir.h" +#include "handerror.h" +#include "implementation.h" + +#define VERSION "1.0" +#define DATE_RELEASE "31 October, 2020" +//#define DEBUG +#define STANDARD_TEXTEDITOR "vim" +#define MAXLEN_TEXTEDITOR 16 +#define MINLEN_PASSWORD 1 +#define MAXLEN_PASSWORD 128 +#define STANDARD_AMOUNT_GENERATE_SYMBOLS 14 +#define LOCKPASS_DIR "/.lock-password/" +#define GPGKEY_FILE "/.gpg-key" + +#define TREE_OUTPUT_FILE ".tree" +#define TEXTEDITOR_FILE ".text-editor" + +#define HASH_INIT 6385337657 +#define HASH_HELP 6385292014 +#define HASH_VERSION 229486327000139 +#define HASH_EDIT 6385183019 +#define HASH_MV 5863624 +#define HASH_MOVE 249844339311324255 +#define HASH_GENERATE 7572409341523952 +#define HASH_INSERT 6953633055386 +#define HASH_RM 5863780 +#define HASH_REMOVE 6953974396019 +#define HASH_DELETE 6953426453624 +#define WITHOUT_ARGUMENTS 1 + +#define STR_SHOWTREEUSE "Use: lpass [-c=passname] [passname]\n" +#define STR_INITUSE "Use: lpass init gpg-key\n" +#define STR_INSERTUSE "Use: lpass insert [-ef] passname\n" +#define STR_EDITUSE "Use: lpass edit [-t=text-editor] passname\n" +#define STR_GENERATEUSE "Use: lpass generate [-l=pass-length] [-f] passname\n" +#define STR_REMOVEUSE "Use: lpass remove/rm/delete passname\n" +#define STR_MOVEUSE "Use: lpass move/mv [-f] old-path new-path\n" + +// == global var == +char *gPath_rootdir; // /home/[username]/.lockpassword/ +char *gPath_subdir; // example: programming/github.com +char *gPath_pass; // example: programming/github.com/joursoir.gpg + +static void globalSplitPath(char *source) +{ + int len_path = strlen(source) + strlen(".gpg") + 1; + + gPath_pass = malloc(sizeof(char) * len_path); // path without working dir + strcpy(gPath_pass, source); + strcat(gPath_pass, ".gpg"); + + gPath_subdir = malloc(sizeof(char) * len_path); // path without working dir and pass file + strcpy(gPath_subdir, source); + gPath_subdir = dirname(gPath_subdir); + + #if defined(DEBUG) + printf("dir: %s\n", gPath_subdir); + printf("pass: %s\n", gPath_pass); + #endif +} + +static void cmd_init(int argc, char *argv[]) +{ + char *gpg_key = argv[2]; + if(gpg_key == NULL) printError(STR_INITUSE); + + // create main directory: + int len_init_storage = strlen(gPath_rootdir) + strlen(GPGKEY_FILE) + 1; // +1 for '\0' + char *path_init_storage = (char *) malloc(sizeof(char) * len_init_storage); + strcpy(path_init_storage, gPath_rootdir); + + char *arguments[] = {"mkdir", "-vp", path_init_storage, NULL}; + easyFork("mkdir", arguments); + + strcat(path_init_storage, GPGKEY_FILE); + + // create .gpg-key in storage + FILE *filekey; + filekey = fopen(path_init_storage, "w"); + if(filekey == NULL) { + callError(122); + } + fputs(gpg_key, filekey); + fclose(filekey); + + free(path_init_storage); + printf("LockPassword initialized for %s\n", gpg_key); +} + +static void cmd_edit(int argc, char *argv[]) +{ + const struct option long_options[] = { + {"text-editor", required_argument, NULL, 't'}, + {NULL, 0, NULL, 0} + }; + + int result; + while((result = getopt_long(argc, argv, "t:", long_options, NULL)) != -1) { + switch(result) { + case 't': + { + // create file, copy name text editor there + FILE *f_texteditor = fopen(TEXTEDITOR_FILE, "w"); + if(f_texteditor == NULL) callError(108); + fputs(optarg, f_texteditor); + fclose(f_texteditor); + printf("You changed text editor to %s\n", optarg); + break; + } + default: printError(STR_EDITUSE); + } + } + + if(optind < argc) optind++; // for skip "edit" + #if defined(DEBUG) + for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); + printf("passname: %s\n", argv[optind]); + #endif + + char *path_to_password; + if(argv[optind] == NULL) printError(STR_EDITUSE); + else path_to_password = argv[optind]; + + checkForbiddenPaths(path_to_password); + globalSplitPath(path_to_password); + + if(checkFileExist(gPath_pass) != 1) + printError("Error: No such file exists\n"); + + // configure text editor file + char text_editor[MAXLEN_TEXTEDITOR]; + FILE *f_texteditor = fopen(TEXTEDITOR_FILE, "r"); + if(f_texteditor == NULL) { + f_texteditor = fopen(TEXTEDITOR_FILE, "w"); + if(f_texteditor == NULL) callError(108); + fputs(STANDARD_TEXTEDITOR, f_texteditor); // in file + strcpy(text_editor, STANDARD_TEXTEDITOR); // in variable + } + else { + if(!fgets(text_editor, sizeof(char)*MAXLEN_TEXTEDITOR, f_texteditor)) + callError(122); + } + fclose(f_texteditor); + + #if defined(DEBUG) + printf("using text editor: %s\n", text_editor); + #endif + // end configure + + // decryption + int size_gpgkey = sizeof(char) * GPG_PUBLICKEY_MAXLENGTH; + char *secret_gpgkey = (char *) malloc(size_gpgkey); + getGPGKey(secret_gpgkey, size_gpgkey); + + char *decryp_arg[] = {"gpg", "-d", "--quiet", "-r", secret_gpgkey, "-o", path_to_password, gPath_pass, NULL}; + easyFork("gpg", decryp_arg); + + // start vim/etc for edit passowrd + char *texte_arg[] = {text_editor, path_to_password, NULL}; + easyFork(text_editor, texte_arg); + + // delete '\n' and paste good pass + char password[MAXLEN_PASSWORD]; + fileCropLineFeed(path_to_password, password, MAXLEN_PASSWORD); + + FILE *file = fopen(path_to_password, "w"); + if(file == NULL) callError(108); + fputs(password, file); + fclose(file); + + // encryption + char *encryp_arg[] = {"gpg", "--quiet", "--yes", "-r", secret_gpgkey, "-e", path_to_password, NULL}; + easyFork("gpg", encryp_arg); + + remove(path_to_password); + free(secret_gpgkey); +} + +static void cmd_move(int argc, char *argv[]) +{ + /* we have a two situation: + 1) mv file file + 2) mv file directory */ + + const struct option long_options[] = { + {"force", no_argument, NULL, 'f'}, + {NULL, 0, NULL, 0} + }; + + int result, flag_force = 0; + while((result = getopt_long(argc, argv, "f", long_options, NULL)) != -1) { + switch(result) { + case 'f': { flag_force = 1; break; } + default: printError(STR_MOVEUSE); + } + } + + if(optind < argc) optind++; // for skip "move" + #if defined(DEBUG) + for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); + printf("old-path: %s\n", argv[optind]); + if(argv[optind] != NULL) printf("new-path: %s\n", argv[optind+1]); + #endif + + if(argv[optind] == NULL) printError(STR_MOVEUSE); + if(argv[optind+1] == NULL) printError(STR_MOVEUSE); + + char *old_path = argv[optind]; + checkForbiddenPaths(old_path); globalSplitPath(old_path); + if(checkFileExist(gPath_pass) != 1) printError("Error: No such old-path exists\n"); + + char *old_path_gpg = gPath_pass; + char *old_path_subdir = gPath_subdir; + + char *new_path = argv[optind+1]; + checkForbiddenPaths(new_path); globalSplitPath(new_path); + + if(checkFileExist(new_path) == 2) // if new-path = dir + ; + else if(checkFileExist(gPath_pass) == 1) { // if new-path = file + if(!flag_force) { + if(getOverwriteAnswer(new_path) != 1) + return; + } + new_path = gPath_pass; + } + else printError("Error: No such new-path exists\n"); + + char *arguments[] = {"mv", "-f", old_path_gpg, new_path, NULL}; + easyFork("mv", arguments); + + deleteEmptyDir(old_path_subdir); + free(old_path_subdir); free(old_path_gpg); +} + +static void cmd_generate(int argc, char *argv[]) +{ + int pass_length = STANDARD_AMOUNT_GENERATE_SYMBOLS, flag_force = 0, flag_copy = 0, result; + const struct option long_options[] = { + {"length", required_argument, NULL, 'l'}, + {"force", no_argument, NULL, 'f'}, + {"copy", no_argument, NULL, 'c'}, + {NULL, 0, NULL, 0} + }; + + while((result = getopt_long(argc, argv, "l:fc", long_options, NULL)) != -1) { + switch(result) { + // if optarg - incorrect number, atoi return 0 + case 'l': { pass_length = atoi(optarg); break; } + case 'f': { flag_force = 1; break; } + case 'c': { flag_copy = 1; break; } + default: printError(STR_GENERATEUSE); + } + } + + if(optind < argc) optind++; // for skip "generate" + #if defined(DEBUG) + for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); + printf("passname: %s\n", argv[optind]); + #endif + + char *path_to_password; + if(argv[optind] == NULL) printError(STR_GENERATEUSE); + else path_to_password = argv[optind]; + + if(pass_length < MINLEN_PASSWORD || pass_length > MAXLEN_PASSWORD) + printError("Error: you typed an incorrect number\n"); + + checkForbiddenPaths(path_to_password); + globalSplitPath(path_to_password); + + if(checkFileExist(gPath_pass) == 1) { + if(!flag_force) { + if(getOverwriteAnswer(path_to_password) != 1) + return; + } + } + + // generate password + char gpass[MAXLEN_PASSWORD]; + generatePassword(gpass, pass_length, MAXLEN_PASSWORD); + + insertPass(path_to_password, gpass, flag_copy); + if(!flag_copy) printf("Generated password: %s\n", gpass); + printf("Password added successfully for %s\n", path_to_password); +} + +static void cmd_insert(int argc, char *argv[]) +{ + int flag_echo = 0, flag_force = 0, flag_copy = 0, result; + const struct option long_options[] = { + {"echo", no_argument, NULL, 'e'}, + {"force", no_argument, NULL, 'f'}, + {"copy", no_argument, NULL, 'c'}, + {NULL, 0, NULL, 0} + }; + + while((result = getopt_long(argc, argv, "efc", long_options, NULL)) != -1) { + switch(result) { + case 'e': { flag_echo = 1; break; } + case 'f': { flag_force = 1; break; } + case 'c': { flag_copy = 1; break; } + default: printError(STR_INSERTUSE); + } + } + + if(optind < argc) optind++; // for skip "insert" + #if defined(DEBUG) + for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); + printf("passname: %s\n", argv[optind]); + #endif + + char *path_to_password; + if(argv[optind] == NULL) printError(STR_INSERTUSE); + else path_to_password = argv[optind]; + + checkForbiddenPaths(path_to_password); + globalSplitPath(path_to_password); + + if(checkFileExist(gPath_pass) == 1) { + if(!flag_force) { + if(getOverwriteAnswer(path_to_password) != 1) + return; + } + } + + if(userEnterPassword(MINLEN_PASSWORD, MAXLEN_PASSWORD, path_to_password, flag_echo, flag_copy) == 1) { + printf("Password added successfully for %s\n", path_to_password); + } + else + printf("Passwords do not match\n"); +} + +static void cmd_remove(int argc, char *argv[]) +{ + char *path_to_password = argv[2]; + if(path_to_password == NULL) + printError(STR_REMOVEUSE); + + checkForbiddenPaths(path_to_password); + globalSplitPath(path_to_password); + + if(checkFileExist(gPath_pass) != 1) + printError("Error: No such file exists\n"); + + if(deleteFile(gPath_pass)) + deleteEmptyDir(gPath_subdir); +} + +static void cmd_showtree(int argc, char *argv[]) +{ + int flag_copy = 0, result; + char *path; + const struct option long_options[] = { + {"copy", no_argument, NULL, 'c'}, + {NULL, 0, NULL, 0} + }; + + while((result = getopt_long(argc, argv, "c", long_options, NULL)) != -1) { + switch(result) { + case 'c': { flag_copy = 1; break; } + default: printError(STR_SHOWTREEUSE); + } + } + + #if defined(DEBUG) + for(int i=0; i < argc; i++) printf("arg: %s\n", argv[i]); + printf("passname: %s\n", argv[optind]); + #endif + + if(argv[optind] == NULL) { + if(flag_copy) printError(STR_SHOWTREEUSE); + else { + path = (char *) malloc(sizeof(char) * 2); + strcpy(path, "."); + } + } + else path = argv[optind]; + checkForbiddenPaths(path); + + if(opendir(path) != NULL) // if it's directory + { + if(flag_copy) printError("Error: you must type a passname, not a directory\n"); + + char *arg1[] = {"tree", "-C", "--noreport", path, "-o", TREE_OUTPUT_FILE, NULL}; + easyFork("tree", arg1); + + char *arg2[] = {"sed", "-i", "-E", "s/\\.gpg(\\x1B\\[[0-9]+m)?( ->|$)/\\1\\2/g", TREE_OUTPUT_FILE, NULL}; + easyFork("sed", arg2); // remove .gpg at the pass name + + if(strcmp(path, ".") == 0) printf("Password Manager\n"); + else printf("Password Manager/%s\n", path); + + char *arg3[] = {"tail", "-n", "+2", TREE_OUTPUT_FILE, NULL}; + easyFork("tail", arg3); // remove working directory + + remove(TREE_OUTPUT_FILE); + } + else + { + globalSplitPath(path); + + if(checkFileExist(gPath_pass) == 1) // exist + { + char password[MAXLEN_PASSWORD]; + getPassword(path, password, sizeof(char)*MAXLEN_PASSWORD, flag_copy); + if(!flag_copy) printf("%s\n", password); + } + else printf("Error: %s is not in the password storage\n", path); + } + + if(argv[1] == NULL) free(path); +} + +static void cmd_help() +{ + printf("Synopsis:\n\tlpass [command] [arguments] ...\n"); + + printf("Commands:\n\tinit gpg-key\n"); + printf("\t\tInitialize the password manager using the passed gpg-key.\n"); + + printf("\tinsert [-e, --echo] [-c, --copy] [-f, --force] passname\n"); + printf("\t\tAdd the specified passname to the password manager.\n"); + + printf("\tedit [-t, --text-editor=text-editor] passname\n"); + printf("\t\tOpen the specified passname in a text editor, waiting for changes.\n"); + + printf("\tgenerate [-l, --length=pass-length] [-c, --copy] [-f, --force] passname\n"); + printf("\t\tGenerate a random password and write it in passname.\n"); + + printf("\tmv/move [-f, --force] old-path new-path\n"); + printf("\t\tMove/rename old-path to new-path.\n"); + + printf("\trm/remove/delete passname\n"); + printf("\t\tRemove the passname you specified from the password manager.\n"); + + printf("\thelp\n"); + printf("\t\tPrint help information about commands and the application itself.\n"); + + printf("\tversion\n"); + printf("\t\tPrint version information.\n"); + + printf("\nMore information may be found in the lpass(1) man page.\n"); +} + +static void cmd_version() +{ + printf("LockPassword v%s\n", VERSION); + printf("Release date: %s\n\n", DATE_RELEASE); + printf("Code was written by Joursoir\n"); + printf("This is free and unencumbered software released into the public domain.\n\n"); +} + +int main(int argc, char *argv[]) +{ + if(!isatty(0)) { // stdin + printError("lpass: Please, use a terminal to run this program\n"); + } + + /* init global path to root directory */ + int len_rootdir = strlen(getenv("HOME")) + strlen(LOCKPASS_DIR) + 1; // +1 for '\0' + + gPath_rootdir = (char *) malloc(sizeof(char) * len_rootdir); + strcpy(gPath_rootdir, getenv("HOME")); + strcat(gPath_rootdir, LOCKPASS_DIR); + /* end init */ + + unsigned long ihash = WITHOUT_ARGUMENTS; + if(argv[1] != NULL) { + ihash = hash(argv[1]); + } + + if(chdir(gPath_rootdir) != 0 && ihash != HASH_INIT) { + printf("Before starting work, you must initialize LockPassword\n"); + printError(STR_INITUSE); + } + + switch(ihash) + { + case HASH_INIT: { cmd_init(argc, argv); break; } + case HASH_EDIT: { cmd_edit(argc, argv); break; } + case HASH_MV: + case HASH_MOVE: { cmd_move(argc, argv); break; } + case HASH_GENERATE: { cmd_generate(argc, argv); break; } + case HASH_INSERT: { cmd_insert(argc, argv); break; } + case HASH_RM: + case HASH_REMOVE: + case HASH_DELETE: { cmd_remove(argc, argv); break; } + case HASH_HELP: { cmd_help(); break; } + case HASH_VERSION: { cmd_version(); break; } + default: { cmd_showtree(argc, argv); break; } + } + + if(gPath_subdir != NULL) { + free(gPath_subdir); + free(gPath_pass); + } + free(gPath_rootdir); + return EXIT_SUCCESS; +} \ No newline at end of file -- cgit v1.2.3-18-g5258