Mercurial > projects > pwm
changeset 10:17fb30016e64
Enable access to record and file metadata
Add info command to show file metadata.
Enable display of creation and modification dates of records.
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Fri, 28 Jul 2017 15:53:57 +0200 |
parents | 60c8ab006e55 |
children | 85bce13237cf |
files | cmd.c pwfile.c pwfile.h pwm.1.xml |
diffstat | 4 files changed, 259 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/cmd.c Fri Jul 28 09:53:46 2017 +0200 +++ b/cmd.c Fri Jul 28 15:53:57 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * Copyright (C) 2017 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 @@ -33,12 +33,16 @@ #endif /* READPASSPHRASE_H */ #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include "cmd.h" #include "pwfile.h" #include "util.h" +#define TIME_FORMAT "%Y-%m-%dT%TZ" +#define TIME_SIZE (4 + 1 + 2 + 1 + 2 + 1 + 8 + 1 + 1) + enum field_type { FIELD_UNKNOWN = -1, FIELD_GROUP, @@ -46,9 +50,12 @@ FIELD_USERNAME, FIELD_PASSWORD, FIELD_NOTES, - FIELD_URL + FIELD_URL, + FIELD_MTIME, + FIELD_CTIME }; +static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]); 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 *[]); @@ -68,7 +75,9 @@ "username", "password", "notes", - "url" + "url", + "ctime", + "mtime" }; static const char *field_labels[] = { @@ -77,10 +86,14 @@ "Username: ", "Password: ", "Notes: ", - "URL: " + "URL: ", + "Created: ", + "Modified: " }; static struct cmd cmds[] = { + { "i", "info", "info", "Show metadata information about the current file", + cmd_info }, { "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 }, @@ -150,7 +163,7 @@ record->url = value; break; default: - break; + return (FIELD_UNKNOWN); } return (i); } @@ -177,6 +190,37 @@ } static enum cmd_return +cmd_info(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + struct metadata *metadata; + struct tm *tm; + char timebuf[TIME_SIZE]; + + if (argc != 1) { + return (CMD_USAGE); + } + + metadata = pwfile_get_metadata(ctx); + printf("Format: 0x%04x\n", metadata->version); + if (metadata->user != NULL) { + printf("User: %s\n", metadata->user); + } + if (metadata->user != NULL) { + printf("Host: %s\n", metadata->host); + } + if (metadata->user != NULL) { + printf("Application: %s\n", metadata->application); + } + tm = gmtime(&metadata->timestamp); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + printf("Last Saved: %s\n", timebuf); + + pwfile_destroy_metadata(metadata); + + return (CMD_OK); +} + +static enum cmd_return cmd_list(struct pwm_ctx *ctx, int argc, char *argv[]) { union list_item **list; @@ -290,6 +334,9 @@ static void print_record(struct record *record, int fields[], int show_labels, FILE *fp) { + struct tm *tm; + char timebuf[TIME_SIZE]; + if (fields[FIELD_TITLE]) { if (print_field(field_labels[FIELD_TITLE], record->title, show_labels, fp) != 0) { @@ -326,6 +373,22 @@ return; } } + if (fields[FIELD_CTIME]) { + tm = gmtime(&record->ctime); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + if (print_field(field_labels[FIELD_CTIME], timebuf, + show_labels, fp)) { + return; + } + } + if (fields[FIELD_MTIME]) { + tm = gmtime(&record->mtime); + strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm); + if (print_field(field_labels[FIELD_MTIME], timebuf, + show_labels, fp)) { + return; + } + } } static enum cmd_return
--- a/pwfile.c Fri Jul 28 09:53:46 2017 +0200 +++ b/pwfile.c Fri Jul 28 15:53:57 2017 +0200 @@ -23,6 +23,7 @@ #include "compat.h" +#include <ctype.h> #ifdef HAVE_ERR_H #include <err.h> #endif /* HAVE_ERR_H */ @@ -597,6 +598,94 @@ free(list); } +static int +parse_user_host(const char *user_host, char **userp, char **hostp) +{ + size_t user_host_len; + size_t i; + unsigned int user_len; + + user_host_len = strlen(user_host); + if (user_host_len < 4) { + return (-1); + } + for (i = 0; i < 4; i++) { + if (!isxdigit(user_host[i])) { + return (-1); + } + } + if (sscanf(user_host, "%04x", &user_len) != 1) { + return (-1); + } + if (4 + (size_t)user_len > user_host_len) { + return (-1); + } + + xasprintf(userp, "%.*s", (int)user_len, user_host + 4); + xasprintf(hostp, "%s", user_host + 4 + user_len); + + return (0); +} + +struct metadata * +pwfile_get_metadata(struct pwm_ctx *ctx) +{ + struct metadata *metadata; + struct pws3_field *version_field; + struct pws3_field *save_app_field; + struct pws3_field *save_timestamp_field; + struct pws3_field *save_user_field; + struct pws3_field *save_host_field; + struct pws3_field *save_user_host_field; + + metadata = xmalloc(sizeof (struct metadata)); + + version_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_VERSION); + metadata->version = pws3_field_get_uint16(version_field); + + save_app_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_APPLICATION); + metadata->application = (save_app_field != NULL) ? + xstrdup(pws3_field_get_text(save_app_field)) : NULL; + + save_timestamp_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_TIMESTAMP); + metadata->timestamp = (save_timestamp_field != NULL) ? + pws3_field_get_time(save_timestamp_field) : 0; + + save_user_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_USER); + save_host_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_HOST); + save_user_host_field = pws3_file_get_header_field(ctx->file, + PWS3_HEADER_FIELD_SAVE_USER_HOST); + metadata->user = NULL; + metadata->host = NULL; + if ((save_user_field != NULL) && (save_host_field != NULL)) { + metadata->user = xstrdup(pws3_field_get_text(save_user_field)); + metadata->host = xstrdup(pws3_field_get_text(save_host_field)); + } else if (save_user_host_field != NULL) { + parse_user_host(pws3_field_get_text(save_user_host_field), + &metadata->user, &metadata->host); + } + + return (metadata); +} + +void +pwfile_destroy_metadata(struct metadata *metadata) +{ + if (metadata == NULL) { + return; + } + + free(metadata->user); + free(metadata->host); + free(metadata->application); + free(metadata); +} + static void update_record(struct pws3_record *pws3_record, struct record *record) { @@ -820,6 +909,8 @@ struct record *record; const unsigned char *uuid; struct pws3_record *pws3_record; + struct pws3_field *ctime_field; + struct pws3_field *mtime_field; struct pws3_field *title_field; struct pws3_field *group_field; struct pws3_field *username_field; @@ -835,6 +926,16 @@ record = xmalloc(sizeof (struct record)); + ctime_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_CREATION_TIME); + record->ctime = (ctime_field != NULL) ? + pws3_field_get_time(ctime_field) : (time_t)0; + + mtime_field = pws3_record_get_field(pws3_record, + PWS3_RECORD_FIELD_MODIFICATION_TIME); + record->mtime = (mtime_field != NULL) ? + pws3_field_get_time(mtime_field) : (time_t)0; + title_field = pws3_record_get_field(pws3_record, PWS3_RECORD_FIELD_TITLE); record->title = (title_field != NULL) ?
--- a/pwfile.h Fri Jul 28 09:53:46 2017 +0200 +++ b/pwfile.h Fri Jul 28 15:53:57 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name> + * Copyright (C) 2017 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 @@ -51,6 +51,14 @@ struct record_id_tree; +struct metadata { + int version; + char *user; + char *host; + char *application; + time_t timestamp; +}; + struct record { char *title; char *group; @@ -58,6 +66,8 @@ char *password; char *notes; char *url; + time_t ctime; + time_t mtime; }; void pwfile_init(struct pwm_ctx *ctx); @@ -66,6 +76,8 @@ int pwfile_write_file(struct pwm_ctx *); union list_item ** pwfile_create_list(struct pwm_ctx *); void pwfile_destroy_list(union list_item **); +struct metadata * pwfile_get_metadata(struct pwm_ctx *); +void pwfile_destroy_metadata(struct metadata *); int pwfile_create_record(struct pwm_ctx *, struct record *); int pwfile_modify_record(struct pwm_ctx *, unsigned int, struct record *);
--- a/pwm.1.xml Fri Jul 28 09:53:46 2017 +0200 +++ b/pwm.1.xml Fri Jul 28 15:53:57 2017 +0200 @@ -34,7 +34,7 @@ <email>guido+pwm@berhoerster.name</email> <personblurb/> </author> - <date>3 February, 2017</date> + <date>28 July, 2017</date> </info> <refmeta> <refentrytitle>pwm</refentrytitle> @@ -80,11 +80,11 @@ character encoding.</para> <refsect2> <title>Output format</title> - <para>The <command>show</command> command displays selected fields by - printing the field name followed by a colon, one or more space characters - and the field's verbatim content to the standard output stream. Field - values may contain newlines, non-printable and/or control - characters.</para> + <para>The <command>show</command> and <command>info</command> commands + display fields by printing the field name followed by a colon, one or + more space characters and the field's verbatim content to the standard + output stream. Field content may contain newlines, non-printable and/or + control characters.</para> <para>The <command>pipe</command> prints the verbatim field content to the standard input stream of the given command.</para> <para>Error messages are printed to the standard error stream.</para> @@ -128,16 +128,52 @@ </refsect2> <refsect2> <title>Fields</title> - <para>The following entry fields are supported: - <simplelist type="vert"> - <member>group</member> - <member>title</member> - <member>username</member> - <member>password</member> - <member>notes</member> - <member>url</member> - </simplelist> - </para> + <para>The following entry fields are supported:</para> + <table xml:id="field-table"> + <title>Fields and their identifiers</title> + <tgroup cols="2" align="left" colsep="1" rowsep="1"> + <thead> + <row> + <entry>Field</entry> + <entry>Field Identifier</entry> + </row> + </thead> + <tbody> + <row> + <entry>Group</entry> + <entry>group</entry> + </row> + <row> + <entry>Title</entry> + <entry>title</entry> + </row> + <row> + <entry>Username</entry> + <entry>username</entry> + </row> + <row> + <entry>Password</entry> + <entry>password</entry> + </row> + <row> + <entry>Notes</entry> + <entry>notes</entry> + </row> + <row> + <entry>URL</entry> + <entry>url</entry> + </row> + <row> + <entry>Creation Time</entry> + <entry>ctime</entry> + </row> + <row> + <entry>Modification Time</entry> + <entry>mtime</entry> + </row> + </tbody> + </tgroup> + </table> <para>Other, existing fields specified by the PasswordSafe file format will be preserved but cannot be displayed or modified.</para> </refsect2> @@ -374,6 +410,22 @@ </listitem> </varlistentry> <varlistentry> + <term>Show metadata information</term> + <listitem> + <cmdsynopsis> + <command>info</command> + </cmdsynopsis> + <cmdsynopsis> + <command>i</command> + <sbr/> + </cmdsynopsis> + <para>Display metadata information such as the user who last wrote + to the database, the time when the database was last written to, + and the host on which the password database was last written + to.</para> + </listitem> + </varlistentry> + <varlistentry> <term>Write database</term> <listitem> <cmdsynopsis> @@ -423,6 +475,15 @@ <manvolnum>5</manvolnum></citerefentry></para> </listitem> </varlistentry> + <varlistentry> + <term> + <literal>LOGNAME</literal> + </term> + <listitem> + <para>The name of the logged in user which is recorded when writing + the password database</para> + </listitem> + </varlistentry> </variablelist> </refsect1> <refsect1>