Mercurial > projects > pwm
view cmd.c @ 0:a7e41e1a79c8
Initial revision
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Thu, 19 Jan 2017 22:39:51 +0100 |
parents | |
children | b5c4267a7182 |
line wrap: on
line source
/* * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * 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 OR COPYRIGHT HOLDERS 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. */ #include "compat.h" #ifdef HAVE_ERR_H #include <err.h> #endif /* HAVE_ERR_H */ #include <errno.h> #include <limits.h> #ifdef HAVE_READPASSPHRASE_H #include <readpassphrase.h> #endif /* READPASSPHRASE_H */ #include <stdlib.h> #include <string.h> #include <unistd.h> #include "cmd.h" #include "pwfile.h" #include "util.h" enum field_type { FIELD_UNKNOWN = -1, FIELD_GROUP, FIELD_TITLE, FIELD_USERNAME, FIELD_PASSWORD, FIELD_NOTES, FIELD_URL }; static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_remove(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_show(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_pipe(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_creategroup(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_removegroup(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_changepassword(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_help(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_write(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_quit(struct pwm_ctx *, int, char *[]); static const char *field_names[] = { "group", "title", "username", "password", "notes", "url" }; static const char *field_labels[] = { "Group: ", "Title: ", "Username: ", "Password: ", "Notes: ", "URL: " }; static struct cmd cmds[] = { { "ls", "list", "list", "List entries", cmd_list }, { "c", "create", "create field=value ...", "Create entry", cmd_create }, { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, { "rm", "remove", "remove id", "Delete entry", cmd_remove }, { "s", "show", "show id field", "Show entry", cmd_show }, { "p", "pipe", "pipe id field command", "Pipe entry to external command", cmd_pipe }, { "cg", "creategroup", "creategroup name", "Create empty group", cmd_creategroup }, { "rg", "removegroup", "removegroup name", "Delete empty group", cmd_removegroup }, { "ch", "changepassword", "changepassword", "Change password", cmd_changepassword }, { "h", "help", "help", "Show help text", cmd_help }, { "w", "write", "write", "Write the database", cmd_write }, { "q", "quit", "quit", "Quit", cmd_quit }, { 0 } }; static enum field_type parse_field_name(const char *name) { int i; for (i = 0; i < (int)COUNTOF(field_names); i++) { if (strcmp(field_names[i], name) == 0) { return (i); } } return (FIELD_UNKNOWN); } static enum field_type parse_field_assignment(char *arg, struct record *record) { int i; size_t field_name_len; char *value; for (i = 0; i < (int)COUNTOF(field_names); i++) { field_name_len = strlen(field_names[i]); if ((strncmp(field_names[i], arg, field_name_len) == 0) && (arg[field_name_len] == '=')){ value = arg + field_name_len + 1; if (*value == '\0') { /* skip empty assignments */ return (i); } switch (i) { case FIELD_GROUP: record->group = value; break; case FIELD_TITLE: record->title = value; break; case FIELD_USERNAME: record->username = value; break; case FIELD_PASSWORD: record->password = value; break; case FIELD_NOTES: record->notes = value; break; case FIELD_URL: record->url = value; break; default: break; } return (i); } } return (FIELD_UNKNOWN); } static int parse_id(const char *arg, unsigned int *idp) { long x; char *p; errno = 0; x = strtol(arg, &p, 10); if ((errno != 0) || (*arg == '\0') || (*p != '\0') || (x > UINT_MAX) || (x <= 0)) { return (-1); } *idp = (unsigned int)x; return (0); } static enum cmd_return cmd_list(struct pwm_ctx *ctx, int argc, char *argv[]) { union list_item **list; size_t i; if (argc != 1) { return (CMD_USAGE); } list = pwfile_create_list(ctx); for (i = 0; list[i] != NULL; i++) { if (list[i]->any.type == ITEM_TYPE_GROUP) { printf("[%s]\n", list[i]->group.group); } else { printf("%4u %s\n", list[i]->record.id, (list[i]->record.title != NULL) ? list[i]->record.title : ""); } } pwfile_destroy_list(list); return (CMD_OK); } static enum cmd_return cmd_create(struct pwm_ctx *ctx, int argc, char *argv[]) { int i; struct record record = { 0 }; if (argc < 2) { return (CMD_USAGE); } for (i = 1; i < argc; i++) { if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); return (CMD_ERR); } } pwfile_create_record(ctx, &record); return (CMD_OK); } static enum cmd_return cmd_modify(struct pwm_ctx *ctx, int argc, char *argv[]) { unsigned int id; int i; struct record record = { 0 }; if (argc < 2) { return (CMD_USAGE); } if (parse_id(argv[1], &id) != 0) { fprintf(stderr, "invalid id %s\n", argv[1]); return (CMD_ERR); } for (i = 2; i < argc; i++) { if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); return (CMD_ERR); } } pwfile_modify_record(ctx, id, &record); return (CMD_OK); } static enum cmd_return cmd_remove(struct pwm_ctx *ctx, int argc, char *argv[]) { unsigned int id; if (argc != 2) { return (CMD_USAGE); } if (parse_id(argv[1], &id) != 0) { fprintf(stderr, "invalid id %s\n", argv[1]); return (CMD_ERR); } if (pwfile_remove_record(ctx, id) != 0) { fprintf(stderr, "failed to remove record %u\n", id); return (CMD_ERR); } return (CMD_OK); } static int print_field(const char *label, const char *value, int show_label, FILE *fp) { fprintf(fp, "%s%s\n", show_label ? label : "", (value != NULL) ? value : ""); if (ferror(fp)) { warn("fprintf"); return (-1); } return (0); } static void print_record(struct record *record, int fields[], int show_labels, FILE *fp) { if (fields[FIELD_TITLE]) { if (print_field(field_labels[FIELD_TITLE], record->title, show_labels, fp) != 0) { return; } } if (fields[FIELD_GROUP]) { if (print_field(field_labels[FIELD_GROUP], record->group, show_labels, fp)) { return; } } if (fields[FIELD_USERNAME]) { if (print_field(field_labels[FIELD_USERNAME], record->username, show_labels, fp)) { return; } } if (fields[FIELD_PASSWORD]) { if (print_field(field_labels[FIELD_PASSWORD], record->password, show_labels, fp)) { return; } } if (fields[FIELD_NOTES]) { if (print_field(field_labels[FIELD_NOTES], record->notes, show_labels, fp)) { return; } } if (fields[FIELD_URL]) { if (print_field(field_labels[FIELD_URL], record->url, show_labels, fp)) { return; } } } static enum cmd_return cmd_show(struct pwm_ctx *ctx, int argc, char *argv[]) { unsigned int id; struct record *record; int i; enum field_type type; int fields[COUNTOF(field_names)] = { 0 }; if (argc < 2) { return (CMD_USAGE); } if (parse_id(argv[1], &id) != 0) { fprintf(stderr, "invalid id %s\n", argv[1]); return (CMD_ERR); } for (i = 2; i < argc; i++) { type = parse_field_name(argv[i]); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[i]); return (CMD_ERR); } fields[type] = 1; } record = pwfile_get_record(ctx, id); if (record == NULL) { fprintf(stderr, "record %u does not exist\n", id); return (CMD_ERR); } print_record(record, fields, 1, stdout); pwfile_destroy_record(record); return (CMD_OK); } static enum cmd_return cmd_pipe(struct pwm_ctx *ctx, int argc, char *argv[]) { enum cmd_return retval = CMD_ERR; unsigned int id; struct record *record = NULL; enum field_type type; int fields[COUNTOF(field_names)] = { 0 }; FILE *fp = NULL; if (argc != 4) { return (CMD_USAGE); } if (parse_id(argv[1], &id) != 0) { fprintf(stderr, "invalid id %s\n", argv[1]); return (CMD_ERR); } type = parse_field_name(argv[2]); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[2]); return (CMD_ERR); } fields[type] = 1; fp = popen(argv[3], "w"); if (fp == NULL) { warn("popen"); goto out; } record = pwfile_get_record(ctx, id); if (record == NULL) { fprintf(stderr, "record %u does not exist\n", id); goto out; } print_record(record, fields, 0, fp); retval = CMD_OK; out: pwfile_destroy_record(record); if (fp != NULL) { pclose(fp); } return (retval); } static enum cmd_return cmd_creategroup(struct pwm_ctx *ctx, int argc, char *argv[]) { if (argc != 2) { return (CMD_USAGE); } if (pwfile_create_group(ctx, argv[1]) != 0) { fprintf(stderr, "group \"%s\" already exists\n", argv[1]); return (CMD_ERR); } return (CMD_OK); } static enum cmd_return cmd_removegroup(struct pwm_ctx *ctx, int argc, char *argv[]) { if (argc != 2) { return (CMD_USAGE); } if (pwfile_remove_group(ctx, argv[1]) != 0) { fprintf(stderr, "group \"%s\" does not exist\n", argv[1]); return (CMD_ERR); } return (CMD_OK); } static enum cmd_return cmd_changepassword(struct pwm_ctx *ctx, int argc, char *argv[]) { size_t password_len; char password_buf[PWS3_MAX_PASSWORD_LEN + 1]; char confirm_buf[PWS3_MAX_PASSWORD_LEN + 1]; if (argc > 2) { return (CMD_USAGE); } else if (argc == 2) { password_len = strlen(argv[1]); if (password_len == 0) { fprintf(stderr, "password must not be empty\n"); return (CMD_ERR); } else if (password_len + 1 > sizeof (ctx->password)) { fprintf(stderr, "password too long\n"); return (CMD_ERR); } memcpy(ctx->password, argv[1], password_len + 1); } else { if (readpassphrase("Enter password: ", password_buf, sizeof (password_buf), RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { err(1, "readpassphrase"); } password_len = strlen(password_buf); if (password_len == 0) { fprintf(stderr, "password must not be empty\n"); return (CMD_ERR); } if (readpassphrase("Confirm password: ", confirm_buf, sizeof (confirm_buf), RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { err(1, "readpassphrase"); } if (strcmp(password_buf, confirm_buf) != 0) { fprintf(stderr, "passwords do not match\n"); return (CMD_ERR); } memcpy(ctx->password, password_buf, password_len + 1); } return (CMD_OK); } static enum cmd_return cmd_help(struct pwm_ctx *ctx, int argc, char *argv[]) { struct cmd *cmd; if (argc != 1) { return (CMD_USAGE); } printf("Commands:\n"); for (cmd = cmds; cmd->cmd_func != NULL; cmd++) { printf("%-2s %-16s %s\n", cmd->abbrev_cmd, cmd->full_cmd, cmd->description); } return (CMD_OK); } static enum cmd_return cmd_write(struct pwm_ctx *ctx, int argc, char *argv[]) { if (argc != 1) { return (CMD_USAGE); } return ((pwfile_write_file(ctx) == 0) ? CMD_OK : CMD_ERR); } static enum cmd_return cmd_quit(struct pwm_ctx *ctx, int argc, char *argv[]) { if (argc != 1) { return (CMD_USAGE); } return (CMD_QUIT); } struct cmd * cmd_match(const char *cmd_name) { size_t i; for (i = 0; cmds[i].cmd_func != NULL; i++) { if ((strcmp(cmds[i].abbrev_cmd, cmd_name) == 0) || (strcmp(cmds[i].full_cmd, cmd_name) == 0)) { return (&cmds[i]); } } return (NULL); }