diff options
author | Kevin McCarthy <kevin@8t8.us> | 2019-07-28 19:24:11 -0700 |
---|---|---|
committer | Kevin McCarthy <kevin@8t8.us> | 2019-08-03 14:08:09 -0700 |
commit | 85bd11b08d66ceaaeb30bafb963a950a507185d1 (patch) | |
tree | a8e6ada0c4a856f6e826ccd8b1b76bf6b367bd0e /autocrypt | |
parent | 361156045e886b0aeba6920992aec4c20536d23c (diff) |
Basic autocrypt account menu.
Provide ability to create, delete, and toggle the prefer-encrypt and
enabled flag for an account.
Hook into the index via 'A' <autocrypt-acct-menu>.
Diffstat (limited to 'autocrypt')
-rw-r--r-- | autocrypt/Makefile.am | 2 | ||||
-rw-r--r-- | autocrypt/autocrypt.c | 15 | ||||
-rw-r--r-- | autocrypt/autocrypt.h | 1 | ||||
-rw-r--r-- | autocrypt/autocrypt_acct_menu.c | 282 | ||||
-rw-r--r-- | autocrypt/autocrypt_db.c | 161 | ||||
-rw-r--r-- | autocrypt/autocrypt_private.h | 5 |
6 files changed, 458 insertions, 8 deletions
diff --git a/autocrypt/Makefile.am b/autocrypt/Makefile.am index d7f3f0da..aefff20a 100644 --- a/autocrypt/Makefile.am +++ b/autocrypt/Makefile.am @@ -8,4 +8,4 @@ AM_CPPFLAGS = -I$(top_srcdir) -I../intl noinst_LIBRARIES = libautocrypt.a libautocrypt_a_SOURCES = autocrypt.c autocrypt.h autocrypt_db.c autocrypt_private.h \ - autocrypt_schema.c autocrypt_gpgme.c + autocrypt_schema.c autocrypt_gpgme.c autocrypt_acct_menu.c diff --git a/autocrypt/autocrypt.c b/autocrypt/autocrypt.c index 82620035..2c866fca 100644 --- a/autocrypt/autocrypt.c +++ b/autocrypt/autocrypt.c @@ -100,18 +100,22 @@ void mutt_autocrypt_cleanup (void) mutt_autocrypt_db_close (); } -/* Creates a brand new account the first time autocrypt is initialized */ -int mutt_autocrypt_account_init (void) +/* Creates a brand new account. + * This is used the first time autocrypt is initialized, and + * in the account menu. */ +int mutt_autocrypt_account_init (int prompt) { ADDRESS *addr = NULL; BUFFER *keyid = NULL, *keydata = NULL; AUTOCRYPT_ACCOUNT *account = NULL; int done = 0, rv = -1, prefer_encrypt = 0; - dprint (1, (debugfile, "In mutt_autocrypt_account_init\n")); - if (mutt_yesorno (_("Create an initial autocrypt account?"), + if (prompt) + { + if (mutt_yesorno (_("Create an initial autocrypt account?"), MUTT_YES) != MUTT_YES) - return 0; + return 0; + } keyid = mutt_buffer_pool_get (); keydata = mutt_buffer_pool_get (); @@ -152,6 +156,7 @@ int mutt_autocrypt_account_init (void) if (account) { mutt_error _("That email address already has an autocrypt account"); + mutt_sleep (1); goto cleanup; } diff --git a/autocrypt/autocrypt.h b/autocrypt/autocrypt.h index 78779777..a50ec311 100644 --- a/autocrypt/autocrypt.h +++ b/autocrypt/autocrypt.h @@ -80,5 +80,6 @@ int mutt_autocrypt_set_sign_as_default_key (HEADER *hdr); int mutt_autocrypt_write_autocrypt_header (ENVELOPE *env, FILE *fp); int mutt_autocrypt_write_gossip_headers (ENVELOPE *env, FILE *fp); int mutt_autocrypt_generate_gossip_list (HEADER *hdr); +void mutt_autocrypt_account_menu (void); #endif diff --git a/autocrypt/autocrypt_acct_menu.c b/autocrypt/autocrypt_acct_menu.c new file mode 100644 index 00000000..dd1f0c5c --- /dev/null +++ b/autocrypt/autocrypt_acct_menu.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2019 Kevin J. McCarthy <kevin@8t8.us> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "mutt_menu.h" +#include "mutt_idna.h" + +#include "autocrypt.h" +#include "autocrypt_private.h" + +typedef struct entry +{ + int tagged; /* TODO */ + int num; + AUTOCRYPT_ACCOUNT *account; + ADDRESS *addr; +} ENTRY; + +static const struct mapping_t AutocryptAcctHelp[] = { + { N_("Exit"), OP_EXIT }, + /* L10N: Autocrypt Account Menu Help line: + create new account + */ + { N_("Create"), OP_AUTOCRYPT_CREATE_ACCT }, + /* L10N: Autocrypt Account Menu Help line: + delete account + */ + { N_("Delete"), OP_AUTOCRYPT_DELETE_ACCT }, + /* L10N: Autocrypt Account Menu Help line: + toggle an account active/inactive + */ + { N_("Tgl Active"), OP_AUTOCRYPT_TOGGLE_ACTIVE }, + /* L10N: Autocrypt Account Menu Help line: + toggle "prefer-encrypt" on an account + */ + { N_("Prf Enc"), OP_AUTOCRYPT_TOGGLE_PREFER }, + { N_("Help"), OP_HELP }, + { NULL, 0 } +}; + +static const char *account_format_str (char *dest, size_t destlen, size_t col, + int cols, char op, const char *src, + const char *fmt, const char *ifstring, + const char *elsestring, + unsigned long data, format_flag flags) +{ + ENTRY *entry = (ENTRY *)data; + char tmp[SHORT_STRING]; + + switch (op) + { + case 'a': + mutt_format_s (dest, destlen, fmt, entry->addr->mailbox); + break; + case 'k': + mutt_format_s (dest, destlen, fmt, entry->account->keyid); + break; + case 'n': + snprintf (tmp, sizeof (tmp), "%%%sd", fmt); + snprintf (dest, destlen, tmp, entry->num); + break; + case 'p': + if (entry->account->prefer_encrypt) + /* L10N: + Autocrypt Account menu. + flag that an account has prefer-encrypt set + */ + mutt_format_s (dest, destlen, fmt, _("prefer encrypt")); + else + /* L10N: + Autocrypt Account menu. + flag that an account has prefer-encrypt unset; + thus encryption will need to be manually enabled. + */ + mutt_format_s (dest, destlen, fmt, _("manual encrypt")); + break; + case 's': + if (entry->account->enabled) + /* L10N: + Autocrypt Account menu. + flag that an account is enabled/active + */ + mutt_format_s (dest, destlen, fmt, _("active")); + else + /* L10N: + Autocrypt Account menu. + flag that an account is disabled/inactive + */ + mutt_format_s (dest, destlen, fmt, _("inactive")); + break; + } + + return (src); +} + +static void account_entry (char *s, size_t slen, MUTTMENU *m, int num) +{ + ENTRY *entry = &((ENTRY *) m->data)[num]; + + mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, + NONULL (AutocryptAcctFormat), account_format_str, + (unsigned long) entry, MUTT_FORMAT_ARROWCURSOR); +} + +static MUTTMENU *create_menu () +{ + MUTTMENU *menu = NULL; + AUTOCRYPT_ACCOUNT **accounts = NULL; + ENTRY *entries = NULL; + int num_accounts = 0, i; + char *helpstr; + + if (mutt_autocrypt_db_account_get_all (&accounts, &num_accounts) < 0) + return NULL; + + menu = mutt_new_menu (MENU_AUTOCRYPT_ACCT); + menu->make_entry = account_entry; + /* menu->tag = account_tag; */ + /* L10N: + Autocrypt Account Management Menu title + */ + menu->title = _("Autocrypt Accounts"); + helpstr = safe_malloc (STRING); + menu->help = mutt_compile_help (helpstr, STRING, MENU_AUTOCRYPT_ACCT, + AutocryptAcctHelp); + + menu->data = entries = safe_calloc (num_accounts, sizeof(ENTRY)); + menu->max = num_accounts; + + for (i = 0; i < num_accounts; i++) + { + entries[i].num = i + 1; + /* note: we are transfering the account pointer to the entries + * array, and freeing the accounts array below. the account + * will be freed in free_menu(). + */ + entries[i].account = accounts[i]; + + entries[i].addr = rfc822_new_address (); + entries[i].addr->mailbox = safe_strdup (accounts[i]->email_addr); + mutt_addrlist_to_local (entries[i].addr); + } + FREE (&accounts); + + mutt_push_current_menu (menu); + + return menu; +} + +static void free_menu (MUTTMENU **menu) +{ + int i; + ENTRY *entries; + + entries = (ENTRY *)(*menu)->data; + for (i = 0; i < (*menu)->max; i++) + { + mutt_autocrypt_db_account_free (&entries[i].account); + rfc822_free_address (&entries[i].addr); + } + FREE (&(*menu)->data); + + mutt_pop_current_menu (*menu); + FREE (&(*menu)->help); + mutt_menuDestroy (menu); +} + +static void toggle_active (ENTRY *entry) +{ + entry->account->enabled = !entry->account->enabled; + if (mutt_autocrypt_db_account_update (entry->account) != 0) + { + entry->account->enabled = !entry->account->enabled; + mutt_error _("Error updating account record"); + } +} + +static void toggle_prefer_encrypt (ENTRY *entry) +{ + entry->account->prefer_encrypt = !entry->account->prefer_encrypt; + if (mutt_autocrypt_db_account_update (entry->account)) + { + entry->account->prefer_encrypt = !entry->account->prefer_encrypt; + mutt_error _("Error updating account record"); + } +} + +void mutt_autocrypt_account_menu (void) +{ + MUTTMENU *menu; + int done = 0, op; + ENTRY *entry; + char msg[SHORT_STRING]; + + if (!option (OPTAUTOCRYPT)) + return; + + if (mutt_autocrypt_init (0)) + return; + + menu = create_menu (); + if (!menu) + return; + + while (!done) + { + switch ((op = mutt_menuLoop (menu))) + { + case OP_EXIT: + done = 1; + break; + + case OP_AUTOCRYPT_CREATE_ACCT: + if (!mutt_autocrypt_account_init (0)) + { + free_menu (&menu); + menu = create_menu (); + } + break; + + case OP_AUTOCRYPT_DELETE_ACCT: + if (menu->data) + { + entry = (ENTRY *)(menu->data) + menu->current; + snprintf (msg, sizeof(msg), + /* L10N: + Confirms deleting an autocrypt account + */ + _("Really delete account \"%s\"?"), + entry->addr->mailbox); + if (mutt_yesorno (msg, MUTT_NO) != MUTT_YES) + break; + + if (!mutt_autocrypt_db_account_delete (entry->account)) + { + free_menu (&menu); + menu = create_menu (); + } + } + break; + + case OP_AUTOCRYPT_TOGGLE_ACTIVE: + if (menu->data) + { + entry = (ENTRY *)(menu->data) + menu->current; + toggle_active (entry); + menu->redraw |= REDRAW_FULL; + } + break; + + case OP_AUTOCRYPT_TOGGLE_PREFER: + if (menu->data) + { + entry = (ENTRY *)(menu->data) + menu->current; + toggle_prefer_encrypt (entry); + menu->redraw |= REDRAW_FULL; + } + break; + } + } + + free_menu (&menu); +} diff --git a/autocrypt/autocrypt_db.c b/autocrypt/autocrypt_db.c index 7fad7068..cadd0dcd 100644 --- a/autocrypt/autocrypt_db.c +++ b/autocrypt/autocrypt_db.c @@ -28,6 +28,8 @@ /* Prepared statements */ static sqlite3_stmt *AccountGetStmt; static sqlite3_stmt *AccountInsertStmt; +static sqlite3_stmt *AccountUpdateStmt; +static sqlite3_stmt *AccountDeleteStmt; static sqlite3_stmt *PeerGetStmt; static sqlite3_stmt *PeerInsertStmt; static sqlite3_stmt *PeerUpdateStmt; @@ -74,7 +76,7 @@ int mutt_autocrypt_db_init (int can_create) if (autocrypt_db_create (mutt_b2s (db_path))) goto cleanup; /* Don't abort the whole init process because account creation failed */ - mutt_autocrypt_account_init (); + mutt_autocrypt_account_init (1); } else { @@ -108,6 +110,10 @@ void mutt_autocrypt_db_close (void) AccountGetStmt = NULL; sqlite3_finalize (AccountInsertStmt); AccountInsertStmt = NULL; + sqlite3_finalize (AccountUpdateStmt); + AccountUpdateStmt = NULL; + sqlite3_finalize (AccountDeleteStmt); + AccountDeleteStmt = NULL; sqlite3_finalize (PeerGetStmt); PeerGetStmt = NULL; @@ -307,6 +313,159 @@ cleanup: return rv; } +int mutt_autocrypt_db_account_update (AUTOCRYPT_ACCOUNT *acct) +{ + int rv = -1; + + if (!AccountUpdateStmt) + { + if (sqlite3_prepare_v3 ( + AutocryptDB, + "UPDATE account SET " + "keyid = ?, " + "keydata = ?, " + "prefer_encrypt = ?, " + "enabled = ? " + "WHERE email_addr = ?;", + -1, + SQLITE_PREPARE_PERSISTENT, + &AccountUpdateStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text (AccountUpdateStmt, + 1, + acct->keyid, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (AccountUpdateStmt, + 2, + acct->keydata, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int (AccountUpdateStmt, + 3, + acct->prefer_encrypt) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_int (AccountUpdateStmt, + 4, + acct->enabled) != SQLITE_OK) + goto cleanup; + if (sqlite3_bind_text (AccountUpdateStmt, + 5, + acct->email_addr, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step (AccountUpdateStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + sqlite3_reset (AccountUpdateStmt); + return rv; +} + +int mutt_autocrypt_db_account_delete (AUTOCRYPT_ACCOUNT *acct) +{ + int rv = -1; + + if (!AccountDeleteStmt) + { + if (sqlite3_prepare_v3 ( + AutocryptDB, + "DELETE from account " + "WHERE email_addr = ?;", + -1, + SQLITE_PREPARE_PERSISTENT, + &AccountDeleteStmt, + NULL) != SQLITE_OK) + goto cleanup; + } + + if (sqlite3_bind_text (AccountDeleteStmt, + 1, + acct->email_addr, + -1, + SQLITE_STATIC) != SQLITE_OK) + goto cleanup; + + if (sqlite3_step (AccountDeleteStmt) != SQLITE_DONE) + goto cleanup; + + rv = 0; + +cleanup: + sqlite3_reset (AccountDeleteStmt); + return rv; +} + +int mutt_autocrypt_db_account_get_all (AUTOCRYPT_ACCOUNT ***accounts, int *num_accounts) +{ + int rv = -1, result; + sqlite3_stmt *stmt = NULL; + AUTOCRYPT_ACCOUNT **results = NULL, *account; + int results_len = 0, results_count = 0; + + *accounts = NULL; + *num_accounts = 0; + + /* Note, speed is not of the essence for the account management screen, + * so we don't bother with a persistent prepared statement */ + if (sqlite3_prepare_v2 ( + AutocryptDB, + "SELECT " + "email_addr, " + "keyid, " + "keydata, " + "prefer_encrypt, " + "enabled " + "FROM account " + "ORDER BY email_addr", + -1, + &stmt, + NULL) != SQLITE_OK) + goto cleanup; + + while ((result = sqlite3_step (stmt)) == SQLITE_ROW) + { + if (results_count == results_len) + { + results_len += 5; + safe_realloc (&results, results_len * sizeof(AUTOCRYPT_ACCOUNT *)); + } + + results[results_count++] = account = mutt_autocrypt_db_account_new (); + + account->email_addr = strdup_column_text (stmt, 0); + account->keyid = strdup_column_text (stmt, 1); + account->keydata = strdup_column_text (stmt, 2); + account->prefer_encrypt = sqlite3_column_int (stmt, 3); + account->enabled = sqlite3_column_int (stmt, 4); + } + + if (result == SQLITE_DONE) + { + *accounts = results; + rv = *num_accounts = results_count; + } + else + { + while (results_count > 0) + mutt_autocrypt_db_account_free (&results[--results_count]); + FREE (&results); + } + +cleanup: + sqlite3_finalize (stmt); + return rv; +} + AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void) { return safe_calloc (1, sizeof(AUTOCRYPT_PEER)); diff --git a/autocrypt/autocrypt_private.h b/autocrypt/autocrypt_private.h index 1c8a038f..96c487f5 100644 --- a/autocrypt/autocrypt_private.h +++ b/autocrypt/autocrypt_private.h @@ -21,7 +21,7 @@ #include <sqlite3.h> -int mutt_autocrypt_account_init (void); +int mutt_autocrypt_account_init (int prompt); int mutt_autocrypt_db_init (int can_create); void mutt_autocrypt_db_close (void); @@ -33,6 +33,9 @@ void mutt_autocrypt_db_account_free (AUTOCRYPT_ACCOUNT **account); int mutt_autocrypt_db_account_get (ADDRESS *addr, AUTOCRYPT_ACCOUNT **account); int mutt_autocrypt_db_account_insert (ADDRESS *addr, const char *keyid, const char *keydata, int prefer_encrypt); +int mutt_autocrypt_db_account_update (AUTOCRYPT_ACCOUNT *acct); +int mutt_autocrypt_db_account_delete (AUTOCRYPT_ACCOUNT *acct); +int mutt_autocrypt_db_account_get_all (AUTOCRYPT_ACCOUNT ***accounts, int *num_accounts); AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void); void mutt_autocrypt_db_peer_free (AUTOCRYPT_PEER **peer); |