diff cmd.c @ 22:ec01c579024a

Add fully interactive mode
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu, 07 Sep 2017 12:40:50 +0200
parents 5c6155c8e9b6
children 1b89066d992c
line wrap: on
line diff
--- a/cmd.c	Wed Sep 06 16:41:58 2017 +0200
+++ b/cmd.c	Thu Sep 07 12:40:50 2017 +0200
@@ -402,18 +402,101 @@
 	return (retval);
 }
 
+static int
+read_record_fields(struct pwm_ctx *ctx, struct record *record)
+{
+	char		group_buf[PWM_LINE_MAX] = { '\0' };
+	char		title_buf[PWM_LINE_MAX] = { '\0' };
+	char		username_buf[PWM_LINE_MAX] = { '\0' };
+	char		password_buf[PWM_LINE_MAX] = { '\0' };
+	char		notes_buf[PWM_LINE_MAX] = { '\0' };
+	char		url_buf[PWM_LINE_MAX] = { '\0' };
+
+	if (io_get_line(NULL, "Group: ", 0, record->group, -1,
+	    sizeof (group_buf), group_buf) == IO_SIGNAL) {
+		return (CMD_SIGNAL);
+	}
+	io_trim_nl(group_buf);
+
+	if (io_get_line(NULL, "Title: ", 0, record->title, -1,
+	    sizeof (title_buf), title_buf) == IO_SIGNAL) {
+		return (CMD_SIGNAL);
+	}
+	io_trim_nl(title_buf);
+
+	if (io_get_line(NULL, "Username: ", 0, record->username, -1,
+	    sizeof (username_buf), username_buf) == IO_SIGNAL) {
+		return (CMD_SIGNAL);
+	}
+	io_trim_nl(username_buf);
+
+	for (;;) {
+		switch (io_get_password("Password: ", "Confirm Password: ",
+		    sizeof (password_buf), password_buf)) {
+		case IO_OK:		/* FALLTHROUGH */
+		case IO_PASSWORD_EMPTY:
+			goto password_done;
+		case IO_SIGNAL:
+			return (CMD_SIGNAL);
+		case IO_PASSWORD_MISMATCH:
+			pwm_err(ctx, "passwords do not match");
+			continue;
+		}
+	}
+
+password_done:
+	if (io_get_line(NULL, "Notes: ", 0, record->notes, -1,
+	    sizeof (notes_buf), notes_buf) == IO_SIGNAL) {
+		return (CMD_SIGNAL);
+	}
+	io_trim_nl(notes_buf);
+
+	if (io_get_line(NULL, "URL: ", 0, record->url, -1, sizeof (url_buf),
+	    url_buf) == IO_SIGNAL) {
+		return (CMD_SIGNAL);
+	}
+	io_trim_nl(url_buf);
+
+	free(record->group);
+	record->group = (group_buf[0] != '\0') ? xstrdup(group_buf) : NULL;
+	free(record->title);
+	record->title = (title_buf[0] != '\0') ? xstrdup(title_buf) : NULL;
+	free(record->username);
+	record->username = (username_buf[0] != '\0') ? xstrdup(username_buf) :
+	    NULL;
+	/*
+	 * the current password cannot be edited, keep the current password if
+	 * the user pressed return or ^D instead of deleting it like other
+	 * fields
+	 */
+	if (password_buf[0] != '\0') {
+		free(record->password);
+		record->password = xstrdup(password_buf);
+	}
+	free(record->notes);
+	record->notes = (notes_buf[0] != '\0') ? xstrdup(notes_buf) : NULL;
+	free(record->url);
+	record->url = (url_buf[0] != '\0') ? xstrdup(url_buf) : NULL;
+
+	return (CMD_OK);
+}
+
 static enum cmd_return
 cmd_create(struct pwm_ctx *ctx, int argc, char *argv[])
 {
+	enum cmd_return	retval = CMD_ERR;
 	int		i;
-	struct record record = { 0 };
+	struct record *record = NULL;
 	enum field_type	type;
 	char		*value;
 
-	if (argc < 2) {
-		return (CMD_USAGE);
+	if (!ctx->is_interactive && (argc < 2)) {
+		retval = CMD_USAGE;
+		goto out;
 	}
 
+	record = pwfile_create_record();
+
 	for (i = 1; i < argc; i++) {
 		type = parse_arg(argv[i], field_namev, '=', &value);
 		if (type == FIELD_UNKNOWN) {
@@ -425,57 +508,76 @@
 		}
 		switch (type) {
 		case FIELD_GROUP:
-			record.group = value;
+			free(record->group);
+			record->group = xstrdup(value);
 			break;
 		case FIELD_TITLE:
-			record.title = value;
+			free(record->title);
+			record->title = xstrdup(value);
 			break;
 		case FIELD_USERNAME:
-			record.username = value;
+			free(record->username);
+			record->username = xstrdup(value);
 			break;
 		case FIELD_PASSWORD:
-			record.password = value;
+			free(record->password);
+			record->password = xstrdup(value);
 			break;
 		case FIELD_NOTES:
-			record.notes = value;
+			free(record->notes);
+			record->notes = xstrdup(value);
 			break;
 		case FIELD_URL:
-			record.url = value;
+			free(record->url);
+			record->url = xstrdup(value);
 			break;
 		default:
 			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
-			return (CMD_ERR);
+			goto out;
 		}
 	}
 
-	pwfile_create_record(ctx, &record);
+	if (ctx->is_interactive && (argc < 2)) {
+		if (read_record_fields(ctx, record) != 0) {
+			goto out;
+		}
+	}
 
-	return (CMD_OK);
+	pwfile_create_pws_record(ctx, record);
+	retval = CMD_OK;
+
+out:
+	pwfile_destroy_record(record);
+
+	return (retval);
 }
 
 static enum cmd_return
 cmd_modify(struct pwm_ctx *ctx, int argc, char *argv[])
 {
+	int		retval = CMD_ERR;
 	unsigned int	id;
 	int		i;
-	struct record	record = { 0 };
+	struct record	*record = NULL;
 	enum field_type	type;
 	char		*value;
 
-	if (argc < 2) {
-		return (CMD_USAGE);
+	if (!ctx->is_interactive && (argc < 2)) {
+		retval = CMD_USAGE;
+		goto out;
 	}
 
 	if (parse_id(argv[1], &id) != 0) {
 		pwm_err(ctx, "invalid id %s", argv[1]);
-		return (CMD_ERR);
+		goto out;
 	}
+	record = pwfile_get_record(ctx, id);
 
 	for (i = 2; i < argc; i++) {
 		type = parse_arg(argv[i], field_namev, '=', &value);
 		if (type == FIELD_UNKNOWN) {
 			pwm_err(ctx, "bad field assignment \"%s\"", argv[i]);
-			return (CMD_ERR);
+			goto out;
 		}
 		if (value[0] == '\0') {
 			/* skip empty assignments */
@@ -483,32 +585,48 @@
 		}
 		switch (type) {
 		case FIELD_GROUP:
-			record.group = value;
+			free(record->group);
+			record->group = xstrdup(value);
 			break;
 		case FIELD_TITLE:
-			record.title = value;
+			free(record->title);
+			record->title = xstrdup(value);
 			break;
 		case FIELD_USERNAME:
-			record.username = value;
+			free(record->username);
+			record->username = xstrdup(value);
 			break;
 		case FIELD_PASSWORD:
-			record.password = value;
+			free(record->password);
+			record->password = xstrdup(value);
 			break;
 		case FIELD_NOTES:
-			record.notes = value;
+			free(record->notes);
+			record->notes = xstrdup(value);
 			break;
 		case FIELD_URL:
-			record.url = value;
+			free(record->url);
+			record->url = xstrdup(value);
 			break;
 		default:
 			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
-			return (CMD_ERR);
+			goto out;
 		}
 	}
 
-	pwfile_modify_record(ctx, id, &record);
+	if (ctx->is_interactive && (argc < 3)) {
+		if (read_record_fields(ctx, record) != 0) {
+			goto out;
+		}
+	}
 
-	return (CMD_OK);
+	pwfile_modify_pws_record(ctx, id, record);
+	retval = CMD_OK;
+
+out:
+	pwfile_destroy_record(record);
+
+	return (retval);
 }
 
 static enum cmd_return
@@ -633,7 +751,7 @@
 	}
 
 	if (id != 0) {
-		if (pwfile_modify_record(ctx, id,
+		if (pwfile_modify_pws_record(ctx, id,
 		    &(struct record){ .password = password }) != 0) {
 			pwm_err(ctx, "record %u does not exist", id);
 			goto out;
@@ -663,7 +781,7 @@
 		return (CMD_ERR);
 	}
 
-	if (pwfile_remove_record(ctx, id) != 0) {
+	if (pwfile_remove_pws_record(ctx, id) != 0) {
 		pwm_err(ctx, "failed to remove record %u", id);
 		return (CMD_ERR);
 	}
@@ -835,13 +953,27 @@
 static enum cmd_return
 cmd_creategroup(struct pwm_ctx *ctx, int argc, char *argv[])
 {
-	if (argc != 2) {
+	char		group_buf[PWM_LINE_MAX] = { '\0' };
+
+	if (!ctx->is_interactive && (argc != 2)) {
 		return (CMD_USAGE);
 	}
 
-	if (pwfile_create_group(ctx, argv[1]) != 0) {
-		pwm_err(ctx, "group \"%s\" already exists", argv[1]);
-		return (CMD_ERR);
+	if (ctx->is_interactive && (argc != 2)) {
+		if (io_get_line(NULL, "Group: ", 0, NULL, 0,
+		    sizeof (group_buf), group_buf) == IO_SIGNAL) {
+			return (CMD_SIGNAL);
+		}
+		io_trim_nl(group_buf);
+	} else {
+		strcpy(group_buf, argv[1]);
+	}
+
+	if (group_buf[0] != '\0') {
+		if (pwfile_create_group(ctx, group_buf) != 0) {
+			pwm_err(ctx, "group \"%s\" already exists", group_buf);
+			return (CMD_ERR);
+		}
 	}
 
 	return (CMD_OK);